diff --git a/composeApp/src/commonMain/composeResources/values-en/strings.xml b/composeApp/src/commonMain/composeResources/values-en/strings.xml
index 92042106..555a61aa 100644
--- a/composeApp/src/commonMain/composeResources/values-en/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values-en/strings.xml
@@ -205,6 +205,7 @@
This file is required to ensure Pixiv-MultiPlatform running correctly. Please do not delete this file!
Pixiv-MultiPlatform allows only one instance to run. Please close the existing instance
My Bookmarks
+ My Following
Network
Custom Share Domain
Telegram and Discord cannot properly display preview cards for artwork links from pixiv.com. If you change this setting, the domain of the shared link will no longer be pixiv.com. \nIt is recommended to change it to
diff --git a/composeApp/src/commonMain/composeResources/values-zh-rTW/string.xml b/composeApp/src/commonMain/composeResources/values-zh-rTW/string.xml
index 2ae474e1..0e278f4c 100644
--- a/composeApp/src/commonMain/composeResources/values-zh-rTW/string.xml
+++ b/composeApp/src/commonMain/composeResources/values-zh-rTW/string.xml
@@ -206,6 +206,7 @@
此檔案用於確保 Pixiv-MultiPlatform 正常運行,請勿刪除此檔案!
Pixiv-MultiPlatform 只允許一個實例運行,請先關閉已存在的實例
我的收藏
+ 我的追蹤
網路設定
下一章
下一步
diff --git a/composeApp/src/commonMain/composeResources/values/strings.xml b/composeApp/src/commonMain/composeResources/values/strings.xml
index 4a4a265b..a297a14e 100644
--- a/composeApp/src/commonMain/composeResources/values/strings.xml
+++ b/composeApp/src/commonMain/composeResources/values/strings.xml
@@ -205,6 +205,7 @@
该文件用于确保Pixiv-MultiPlatform正常运行,请不要删除此文件!
Pixiv-MultiPlatform只允许一个实例运行,请关闭已有的实例。
我的收藏
+ 我的关注
网络设置
自定义分享域名
对于telegram和discord,它们无法正常展示pixiv.com下的作品链接预览。如果修改此设置,分享链接的域名将不会是pixiv.com。 \n推荐修改为
diff --git a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/App.kt b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/App.kt
index 598043f2..1e494075 100644
--- a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/App.kt
+++ b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/App.kt
@@ -143,6 +143,9 @@ import top.kagg886.pmf.ui.route.main.detail.novel.NovelSimilarScreen
import top.kagg886.pmf.ui.route.main.detail.novel.NovelSimilarViewModel
import top.kagg886.pmf.ui.route.main.download.DownloadScreenModel
import top.kagg886.pmf.ui.route.main.download.DownloadScreenSideEffect
+import top.kagg886.pmf.ui.route.main.follow.FollowRoute
+import top.kagg886.pmf.ui.route.main.follow.FollowScreen
+import top.kagg886.pmf.ui.route.main.follow.FollowViewModel
import top.kagg886.pmf.ui.route.main.history.HistoryIllustViewModel
import top.kagg886.pmf.ui.route.main.history.HistoryNovelViewModel
import top.kagg886.pmf.ui.route.main.later.ViewLaterModel
@@ -224,6 +227,7 @@ private val config = SavedStateConfiguration {
subclass(serializer = RankRoute.serializer())
subclass(serializer = ProfileRoute.serializer())
subclass(serializer = BookmarkRoute.serializer())
+ subclass(serializer = FollowRoute.serializer())
subclass(serializer = AboutRoute.serializer())
subclass(serializer = IllustDetailRoute.serializer())
subclass(serializer = IllustDetailPreFetchRoute.serializer())
@@ -511,6 +515,9 @@ fun setupEnv() {
viewModelOf(::BookmarkNovelViewModel)
navigation { BookmarkScreen() }
+ viewModelOf(::FollowViewModel)
+ navigation { FollowScreen() }
+
navigation { AboutScreen() }
viewModelOf(::IllustDetailViewModel)
diff --git a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/component/icon/ViewOff.kt b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/component/icon/ViewOff.kt
new file mode 100644
index 00000000..009277b5
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/component/icon/ViewOff.kt
@@ -0,0 +1,68 @@
+package top.kagg886.pmf.ui.component.icon
+
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.PathFillType.Companion.NonZero
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.StrokeCap.Companion.Butt
+import androidx.compose.ui.graphics.StrokeJoin.Companion.Miter
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.graphics.vector.ImageVector.Builder
+import androidx.compose.ui.graphics.vector.path
+import androidx.compose.ui.unit.dp
+
+val ViewOff: ImageVector by lazy {
+ Builder(
+ name = "ViewOff",
+ defaultWidth = 24.0.dp,
+ defaultHeight = 24.0.dp,
+ viewportWidth = 24.0f,
+ viewportHeight = 24.0f,
+ ).apply {
+ path(
+ fill = SolidColor(Color(0xFF5f6368)),
+ stroke = null,
+ strokeLineWidth = 0.0f,
+ strokeLineCap = Butt,
+ strokeLineJoin = Miter,
+ strokeLineMiter = 4.0f,
+ pathFillType = NonZero,
+ ) {
+ moveTo(12.0f, 6.5f)
+ curveToRelative(3.79f, 0.0f, 7.17f, 2.13f, 8.82f, 5.5f)
+ curveToRelative(-0.7f, 1.43f, -1.79f, 2.61f, -3.07f, 3.46f)
+ lineToRelative(1.43f, 1.43f)
+ curveTo(20.72f, 15.8f, 21.95f, 14.13f, 23.0f, 12.0f)
+ curveToRelative(-1.73f, -4.39f, -6.0f, -7.5f, -11.0f, -7.5f)
+ curveToRelative(-1.4f, 0.0f, -2.74f, 0.25f, -3.98f, 0.7f)
+ lineToRelative(1.68f, 1.68f)
+ curveToRelative(0.73f, -0.24f, 1.5f, -0.38f, 2.3f, -0.38f)
+ close()
+ moveTo(2.01f, 4.87f)
+ lineToRelative(2.68f, 2.68f)
+ curveTo(3.06f, 8.83f, 1.77f, 10.36f, 1.0f, 12.0f)
+ curveToRelative(1.73f, 4.39f, 6.0f, 7.5f, 11.0f, 7.5f)
+ curveToRelative(1.55f, 0.0f, 3.03f, -0.3f, 4.38f, -0.84f)
+ lineToRelative(3.42f, 3.42f)
+ lineToRelative(1.27f, -1.27f)
+ lineTo(3.28f, 3.6f)
+ lineTo(2.01f, 4.87f)
+ close()
+ moveTo(7.53f, 10.39f)
+ lineToRelative(1.55f, 1.55f)
+ curveToRelative(-0.05f, 0.28f, -0.08f, 0.56f, -0.08f, 0.86f)
+ curveToRelative(0.0f, 1.66f, 1.34f, 3.0f, 3.0f, 3.0f)
+ curveToRelative(0.3f, 0.0f, 0.58f, -0.03f, 0.86f, -0.08f)
+ lineToRelative(1.55f, 1.55f)
+ curveToRelative(-0.74f, 0.46f, -1.6f, 0.73f, -2.41f, 0.73f)
+ curveToRelative(-2.76f, 0.0f, -5.0f, -2.24f, -5.0f, -5.0f)
+ curveToRelative(0.0f, -0.81f, 0.27f, -1.67f, 0.73f, -2.41f)
+ close()
+ moveTo(11.84f, 9.02f)
+ lineToRelative(3.15f, 3.15f)
+ lineToRelative(0.02f, -0.16f)
+ curveToRelative(0.0f, -1.66f, -1.34f, -3.0f, -3.0f, -3.0f)
+ lineToRelative(-0.17f, 0.01f)
+ close()
+ }
+ }.build()
+}
diff --git a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/follow/FollowScreen.kt b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/follow/FollowScreen.kt
new file mode 100644
index 00000000..1c0b289a
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/follow/FollowScreen.kt
@@ -0,0 +1,83 @@
+package top.kagg886.pmf.ui.route.main.follow
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.filled.ArrowBack
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.navigation3.runtime.NavKey
+import kotlinx.serialization.Serializable
+import org.koin.compose.viewmodel.koinViewModel
+import org.koin.core.parameter.parametersOf
+import top.kagg886.pixko.module.user.UserLikePublicity
+import top.kagg886.pmf.LocalNavBackStack
+import top.kagg886.pmf.res.*
+import top.kagg886.pmf.ui.component.icon.View
+import top.kagg886.pmf.ui.component.icon.ViewOff
+import top.kagg886.pmf.ui.util.AuthorFetchScreen
+import top.kagg886.pmf.ui.util.removeLastOrNullWorkaround
+import top.kagg886.pmf.util.stringResource
+
+@Serializable
+data object FollowRoute : NavKey
+
+@Composable
+fun FollowScreen() {
+ val stack = LocalNavBackStack.current
+ var restrict by rememberSaveable { mutableStateOf(UserLikePublicity.PUBLIC) }
+ val model = koinViewModel(key = "follow_$restrict") {
+ parametersOf(restrict)
+ }
+
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ title = {
+ Text(stringResource(Res.string.my_follow))
+ },
+ navigationIcon = {
+ IconButton(
+ onClick = {
+ stack.removeLastOrNullWorkaround()
+ },
+ ) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = null,
+ )
+ }
+ },
+ actions = {
+ val isPublic = restrict == UserLikePublicity.PUBLIC
+ IconButton(
+ onClick = {
+ restrict = if (isPublic) UserLikePublicity.PRIVATE else UserLikePublicity.PUBLIC
+ },
+ ) {
+ Icon(
+ imageVector = if (isPublic) View else ViewOff,
+ contentDescription = stringResource(
+ if (isPublic) Res.string.public else Res.string.private,
+ ),
+ )
+ }
+ },
+ )
+ },
+ ) {
+ Box(Modifier.padding(it).fillMaxSize()) {
+ AuthorFetchScreen(model)
+ }
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/follow/FollowViewModel.kt b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/follow/FollowViewModel.kt
new file mode 100644
index 00000000..74a1edf8
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/follow/FollowViewModel.kt
@@ -0,0 +1,23 @@
+package top.kagg886.pmf.ui.route.main.follow
+
+import top.kagg886.pixko.module.user.UserLikePublicity
+import top.kagg886.pixko.module.user.getFollowingList
+import top.kagg886.pmf.backend.pixiv.PixivConfig
+import top.kagg886.pmf.ui.util.AuthorFetchViewModel
+import top.kagg886.pmf.ui.util.flowOf
+import top.kagg886.pmf.ui.util.page
+
+class FollowViewModel(
+ private val restrict: UserLikePublicity = UserLikePublicity.PUBLIC,
+) : AuthorFetchViewModel() {
+ private val id = PixivConfig.pixiv_user!!.userId
+
+ override fun source() = flowOf(30) { params ->
+ params.page { page ->
+ client.getFollowingList(id) {
+ this.page = page
+ publicity = restrict
+ }
+ }
+ }
+}
diff --git a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/profile/ProfileScreen.kt b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/profile/ProfileScreen.kt
index 30ec290c..b3f53bf1 100644
--- a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/profile/ProfileScreen.kt
+++ b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/profile/ProfileScreen.kt
@@ -56,6 +56,7 @@ import top.kagg886.pmf.res.*
import top.kagg886.pmf.ui.route.main.bookmark.BookmarkRoute
import top.kagg886.pmf.ui.route.main.detail.author.AuthorScreenWithoutCollapse
import top.kagg886.pmf.ui.route.main.download.DownloadScreen
+import top.kagg886.pmf.ui.route.main.follow.FollowRoute
import top.kagg886.pmf.ui.route.main.history.HistoryScreen
import top.kagg886.pmf.ui.route.main.later.ViewLaterScreen
import top.kagg886.pmf.ui.route.main.profile.ProfileItem.Download
@@ -156,6 +157,24 @@ fun ProfileScreen(route: ProfileRoute) {
},
)
+ Spacer(Modifier.height(8.dp))
+ NavigationDrawerItem(
+ modifier = Modifier.padding(horizontal = 8.dp),
+ label = {
+ Text(stringResource(Res.string.my_follow))
+ },
+ icon = {
+ Icon(top.kagg886.pmf.ui.component.icon.View, "")
+ },
+ selected = false,
+ onClick = {
+ stack += FollowRoute
+ scope.launch {
+ drawer.close()
+ }
+ },
+ )
+
Spacer(Modifier.height(8.dp))
NavigationDrawerItem(
modifier = Modifier.padding(horizontal = 8.dp),
diff --git a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/util/author-fetch.kt b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/util/author-fetch.kt
index c6aa496e..38331801 100644
--- a/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/util/author-fetch.kt
+++ b/composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/util/author-fetch.kt
@@ -56,10 +56,10 @@ abstract class AuthorFetchViewModel : ContainerHost if (u.id == author.id) u.copy(isFollowed = false) else u }
}
}