From ad3f497b062828ba256147b959cd5c9d7cec4a28 Mon Sep 17 00:00:00 2001 From: magic-cucumber Date: Thu, 14 May 2026 10:57:43 +0800 Subject: [PATCH] following page --- .../composeResources/values-en/strings.xml | 1 + .../composeResources/values-zh-rTW/string.xml | 1 + .../composeResources/values/strings.xml | 1 + .../commonMain/kotlin/top/kagg886/pmf/App.kt | 7 ++ .../kagg886/pmf/ui/component/icon/ViewOff.kt | 68 +++++++++++++++ .../pmf/ui/route/main/follow/FollowScreen.kt | 83 +++++++++++++++++++ .../ui/route/main/follow/FollowViewModel.kt | 23 +++++ .../ui/route/main/profile/ProfileScreen.kt | 19 +++++ .../top/kagg886/pmf/ui/util/author-fetch.kt | 4 +- 9 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/component/icon/ViewOff.kt create mode 100644 composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/follow/FollowScreen.kt create mode 100644 composeApp/src/commonMain/kotlin/top/kagg886/pmf/ui/route/main/follow/FollowViewModel.kt 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 } } }