From e775038f397ad2210fd7763570dc316c5601cebb Mon Sep 17 00:00:00 2001 From: LoxiaLiSA Date: Tue, 11 Feb 2025 14:09:40 +0800 Subject: [PATCH 1/3] aa --- .../java/ceui/lisa/database/AppDatabase.java | 8 +- .../main/java/ceui/pixiv/db/EntityWrapper.kt | 103 +++++++++++++----- app/src/main/java/ceui/pixiv/db/GeneralDao.kt | 9 ++ .../main/java/ceui/pixiv/db/GeneralEntity.kt | 7 +- app/src/main/java/ceui/pixiv/db/RecordType.kt | 4 + .../ui/blocking/BlockedItemListFragment.kt | 29 ++++- .../ui/common/CommonViewPagerFragment.kt | 44 +++++++- .../main/java/ceui/pixiv/ui/common/Either.kt | 25 +++++ .../pixiv/ui/common/NavFragmentViewModel.kt | 13 +++ .../ceui/pixiv/ui/common/PixivFragment.kt | 12 ++ .../ceui/pixiv/ui/detail/ArtworkFragment.kt | 31 ++++-- .../ceui/pixiv/ui/history/HistoryViewModel.kt | 10 +- .../ceui/pixiv/ui/novel/NovelTextFragment.kt | 4 +- .../ceui/pixiv/ui/user/MineProfileFragment.kt | 3 +- .../java/ceui/pixiv/ui/user/UserFragment.kt | 4 +- .../main/res/navigation/mobile_navigation.xml | 6 +- 16 files changed, 256 insertions(+), 56 deletions(-) create mode 100644 app/src/main/java/ceui/pixiv/ui/common/Either.kt diff --git a/app/src/main/java/ceui/lisa/database/AppDatabase.java b/app/src/main/java/ceui/lisa/database/AppDatabase.java index 48c48d23e..4707e32dc 100644 --- a/app/src/main/java/ceui/lisa/database/AppDatabase.java +++ b/app/src/main/java/ceui/lisa/database/AppDatabase.java @@ -56,11 +56,13 @@ public void migrate(@NonNull SupportSQLiteDatabase database) { public void migrate(@NonNull SupportSQLiteDatabase database) { database.execSQL( "CREATE TABLE IF NOT EXISTS general_table (" + - "id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, " + // ✅ 兼容 Long + "id INTEGER NOT NULL, " + // id 字段,非空 + "recordType INTEGER NOT NULL, " + // recordType 字段,非空 "json TEXT NOT NULL, " + "entityType INTEGER NOT NULL, " + - "recordType INTEGER NOT NULL, " + - "updatedTime INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000))" + "updatedTime INTEGER NOT NULL DEFAULT (strftime('%s', 'now') * 1000), " + + "PRIMARY KEY(id, recordType)" + // 复合主键 + ")" ); } }; diff --git a/app/src/main/java/ceui/pixiv/db/EntityWrapper.kt b/app/src/main/java/ceui/pixiv/db/EntityWrapper.kt index d8b60cfd8..79e638494 100644 --- a/app/src/main/java/ceui/pixiv/db/EntityWrapper.kt +++ b/app/src/main/java/ceui/pixiv/db/EntityWrapper.kt @@ -11,44 +11,93 @@ import kotlinx.coroutines.MainScope import kotlinx.coroutines.launch import timber.log.Timber + object EntityWrapper { - fun visitIllust(context: Context, illust: Illust) { + // 通用插入方法 + private suspend fun insertEntity(context: Context, entity: GeneralEntity) { + try { + AppDatabase.getAppDatabase(context).generalDao().insert(entity) + Timber.d("EntityWrapper insertEntity done ${entity.id}") + } catch (ex: Exception) { + Timber.e(ex, "Error inserting entity: ${entity.id}") + } + } + + // 通用删除方法 + private suspend fun deleteEntity(context: Context, recordType: Int, id: Long) { + try { + AppDatabase.getAppDatabase(context).generalDao().deleteByRecordTypeAndId(recordType, id) + Timber.d("EntityWrapper deleteEntity done $id") + } catch (ex: Exception) { + Timber.e(ex, "Error deleting entity: $id") + } + } + + // 插入访问记录 + private fun visit(context: Context, id: Long, entityJson: String, entityType: Int, recordType: Int) { MainScope().launch(Dispatchers.IO) { - try { - val json = Shaft.sGson.toJson(illust) - val entity = GeneralEntity(illust.id, json, EntityType.ILLUST, RecordType.VIEW_ILLUST_HISTORY) - AppDatabase.getAppDatabase(context).generalDao().insert(entity) - Timber.d("EntityWrapper visitIllust done ${illust.title}") - } catch (ex: Exception) { - Timber.e(ex) - } + val entity = GeneralEntity(id, entityJson, entityType, recordType) + insertEntity(context, entity) } } - fun visitNovel(context: Context, novel: Novel) { + // 插入或删除块操作 + private fun block(context: Context, id: Long, entityJson: String, entityType: Int, recordType: Int) { MainScope().launch(Dispatchers.IO) { - try { - val json = Shaft.sGson.toJson(novel) - val entity = GeneralEntity(novel.id, json, EntityType.NOVEL, RecordType.VIEW_NOVEL_HISTORY) - AppDatabase.getAppDatabase(context).generalDao().insert(entity) - Timber.d("EntityWrapper visitNovel done ${novel.title}") - } catch (ex: Exception) { - Timber.e(ex) - } + val entity = GeneralEntity(id, entityJson, entityType, recordType) + insertEntity(context, entity) } } + // 调用 `visit` 方法 + fun visitIllust(context: Context, illust: Illust) { + val json = Shaft.sGson.toJson(illust) + visit(context, illust.id, json, EntityType.ILLUST, RecordType.VIEW_ILLUST_HISTORY) + } + + fun visitNovel(context: Context, novel: Novel) { + val json = Shaft.sGson.toJson(novel) + visit(context, novel.id, json, EntityType.NOVEL, RecordType.VIEW_NOVEL_HISTORY) + } + fun visitUser(context: Context, user: User) { + val json = Shaft.sGson.toJson(user) + visit(context, user.id, json, EntityType.USER, RecordType.VIEW_USER_HISTORY) + } + + // 调用 `block` 方法 + fun blockIllust(context: Context, illust: Illust) { + val json = Shaft.sGson.toJson(illust) + block(context, illust.id, json, EntityType.ILLUST, RecordType.BLOCK_ILLUST) + } + + fun blockNovel(context: Context, novel: Novel) { + val json = Shaft.sGson.toJson(novel) + block(context, novel.id, json, EntityType.NOVEL, RecordType.BLOCK_NOVEL) + } + + fun blockUser(context: Context, user: User) { + val json = Shaft.sGson.toJson(user) + block(context, user.id, json, EntityType.USER, RecordType.BLOCK_USER) + } + + // 调用删除方法 + fun unblockIllust(context: Context, illust: Illust) { + MainScope().launch(Dispatchers.IO) { + deleteEntity(context, RecordType.BLOCK_ILLUST, illust.id) + } + } + + fun unblockNovel(context: Context, novel: Novel) { + MainScope().launch(Dispatchers.IO) { + deleteEntity(context, RecordType.BLOCK_NOVEL, novel.id) + } + } + + fun unblockUser(context: Context, user: User) { MainScope().launch(Dispatchers.IO) { - try { - val json = Shaft.sGson.toJson(user) - val entity = GeneralEntity(user.id, json, EntityType.USER, RecordType.VIEW_USER_HISTORY) - AppDatabase.getAppDatabase(context).generalDao().insert(entity) - Timber.d("EntityWrapper visitUser done ${user.name}") - } catch (ex: Exception) { - Timber.e(ex) - } + deleteEntity(context, RecordType.BLOCK_USER, user.id) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/ceui/pixiv/db/GeneralDao.kt b/app/src/main/java/ceui/pixiv/db/GeneralDao.kt index 839ab3218..0d98e514e 100644 --- a/app/src/main/java/ceui/pixiv/db/GeneralDao.kt +++ b/app/src/main/java/ceui/pixiv/db/GeneralDao.kt @@ -1,5 +1,6 @@ package ceui.pixiv.db +import androidx.lifecycle.LiveData import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy @@ -20,4 +21,12 @@ interface GeneralDao { // ✅ 根据 entityType 查询数据,并按 updatedTime 排序,支持分页 @Query("SELECT * FROM general_table WHERE recordType = :recordType ORDER BY updatedTime DESC LIMIT :limit OFFSET :offset") fun getByRecordType(recordType: Int, offset: Int, limit: Int = 30): List // 根据 entityType 返回数据,按 updatedTime 降序排列,支持分页 + + // ✅ 根据 recordType 和 id 删除记录 + @Query("DELETE FROM general_table WHERE recordType = :recordType AND id = :id") + fun deleteByRecordTypeAndId(recordType: Int, id: Long) + + // ✅ 根据 entityType 和 id 查询对象是否被屏蔽,返回 LiveData + @Query("SELECT COUNT(*) > 0 FROM general_table WHERE recordType = :recordType AND id = :id") + fun isObjectBlocked(recordType: Int, id: Long): LiveData } diff --git a/app/src/main/java/ceui/pixiv/db/GeneralEntity.kt b/app/src/main/java/ceui/pixiv/db/GeneralEntity.kt index fe8b28113..3d60f88f9 100644 --- a/app/src/main/java/ceui/pixiv/db/GeneralEntity.kt +++ b/app/src/main/java/ceui/pixiv/db/GeneralEntity.kt @@ -1,14 +1,15 @@ package ceui.pixiv.db import androidx.room.Entity -import androidx.room.PrimaryKey import ceui.lisa.activities.Shaft import ceui.lisa.models.ModelObject import ceui.loxia.ObjectPool -@Entity(tableName = "general_table") +@Entity( + tableName = "general_table", + primaryKeys = ["id", "recordType"] // 指定复合主键 +) data class GeneralEntity( - @PrimaryKey(autoGenerate = true) val id: Long, val json: String, val entityType: Int, diff --git a/app/src/main/java/ceui/pixiv/db/RecordType.kt b/app/src/main/java/ceui/pixiv/db/RecordType.kt index 7485f0cbe..9879d3d0d 100644 --- a/app/src/main/java/ceui/pixiv/db/RecordType.kt +++ b/app/src/main/java/ceui/pixiv/db/RecordType.kt @@ -4,4 +4,8 @@ object RecordType { const val VIEW_ILLUST_HISTORY = 1 const val VIEW_NOVEL_HISTORY = 2 const val VIEW_USER_HISTORY = 3 + + const val BLOCK_ILLUST = 4 + const val BLOCK_NOVEL = 5 + const val BLOCK_USER = 6 } \ No newline at end of file diff --git a/app/src/main/java/ceui/pixiv/ui/blocking/BlockedItemListFragment.kt b/app/src/main/java/ceui/pixiv/ui/blocking/BlockedItemListFragment.kt index 252e09a5f..192b40257 100644 --- a/app/src/main/java/ceui/pixiv/ui/blocking/BlockedItemListFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/blocking/BlockedItemListFragment.kt @@ -5,19 +5,40 @@ import android.view.View import ceui.pixiv.ui.common.PixivFragment import ceui.lisa.databinding.FragmentPixivListBinding import ceui.lisa.R +import ceui.lisa.database.AppDatabase +import ceui.loxia.launchSuspend +import ceui.loxia.threadSafeArgs +import ceui.pixiv.db.RecordType import ceui.pixiv.ui.common.ListMode +import ceui.pixiv.ui.common.constructVM import ceui.pixiv.ui.common.setUpCustomAdapter +import ceui.pixiv.ui.common.setUpRefreshState import ceui.pixiv.ui.common.viewBinding +import ceui.pixiv.ui.history.HistoryViewModel +import ceui.pixiv.ui.history.ViewHistoryFragmentArgs +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import kotlin.getValue class BlockedItemListFragment : PixivFragment(R.layout.fragment_pixiv_list) { private val binding by viewBinding(FragmentPixivListBinding::bind) + private val safeArgs by threadSafeArgs() + private val viewModel by constructVM({ + AppDatabase.getAppDatabase(requireContext()) to safeArgs.recordType + }) { (database, recordType) -> + HistoryViewModel(database, recordType) + } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - val adapter = setUpCustomAdapter(binding, ListMode.VERTICAL) - BlockingManager.blockedWorks.observe(viewLifecycleOwner) { list -> - adapter.submitList(list?.map { BlockedItemHolder(it) }) - } + setUpRefreshState( + binding, viewModel, + if (safeArgs.recordType == RecordType.BLOCK_ILLUST) { + ListMode.STAGGERED_GRID + } else { + ListMode.VERTICAL + } + ) } } \ No newline at end of file diff --git a/app/src/main/java/ceui/pixiv/ui/common/CommonViewPagerFragment.kt b/app/src/main/java/ceui/pixiv/ui/common/CommonViewPagerFragment.kt index eb8c54a20..47c82a0e1 100644 --- a/app/src/main/java/ceui/pixiv/ui/common/CommonViewPagerFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/common/CommonViewPagerFragment.kt @@ -13,8 +13,12 @@ import ceui.lisa.databinding.FragmentCommonViewpagerBinding import ceui.lisa.utils.Params import ceui.pixiv.db.RecordType import ceui.pixiv.session.SessionManager +import ceui.pixiv.ui.blocking.BlockedItemListFragment +import ceui.pixiv.ui.blocking.BlockedItemListFragmentArgs import ceui.pixiv.ui.circles.PagedFragmentItem import ceui.pixiv.ui.circles.SmartFragmentPagerAdapter +import ceui.pixiv.ui.common.ViewPagerContentType.MyBlockingHistory +import ceui.pixiv.ui.common.ViewPagerContentType.MyViewHistory import ceui.pixiv.ui.history.ViewHistoryFragment import ceui.pixiv.ui.history.ViewHistoryFragmentArgs import ceui.pixiv.ui.user.UserBookmarkedIllustsFragment @@ -30,6 +34,7 @@ object ViewPagerContentType { const val MyBookmarkNovel = 2 const val MyFollowingUsers = 3 const val MyViewHistory = 4 + const val MyBlockingHistory = 5 } class CommonViewPagerViewModel : ViewModel() { @@ -136,7 +141,7 @@ class CommonViewPagerFragment : TitledViewPagerFragment(R.layout.fragment_common initialTitle = getString(R.string.string_392) ) ) - } else if (args.contentType == ViewPagerContentType.MyViewHistory) { + } else if (args.contentType == MyViewHistory) { pagedItems.add( PagedFragmentItem( builder = { @@ -173,6 +178,43 @@ class CommonViewPagerFragment : TitledViewPagerFragment(R.layout.fragment_common initialTitle = getString(R.string.type_user) ) ) + } else if (args.contentType == MyBlockingHistory) { + pagedItems.add( + PagedFragmentItem( + builder = { + BlockedItemListFragment().apply { + arguments = BlockedItemListFragmentArgs( + RecordType.BLOCK_ILLUST + ).toBundle() + } + }, + initialTitle = getString(R.string.string_136) + ) + ) + pagedItems.add( + PagedFragmentItem( + builder = { + BlockedItemListFragment().apply { + arguments = BlockedItemListFragmentArgs( + RecordType.BLOCK_NOVEL + ).toBundle() + } + }, + initialTitle = getString(R.string.type_novel) + ) + ) + pagedItems.add( + PagedFragmentItem( + builder = { + BlockedItemListFragment().apply { + arguments = BlockedItemListFragmentArgs( + RecordType.BLOCK_USER + ).toBundle() + } + }, + initialTitle = getString(R.string.type_user) + ) + ) } val adapter = SmartFragmentPagerAdapter(pagedItems, this) binding.commonViewpager.adapter = adapter diff --git a/app/src/main/java/ceui/pixiv/ui/common/Either.kt b/app/src/main/java/ceui/pixiv/ui/common/Either.kt new file mode 100644 index 000000000..cf24cd7a3 --- /dev/null +++ b/app/src/main/java/ceui/pixiv/ui/common/Either.kt @@ -0,0 +1,25 @@ +package ceui.pixiv.ui.common + +sealed class Either { + data class Left(val value: L) : Either() + data class Right(val value: R) : Either() +} + +sealed class ResultOrNoOp { + class Done(val res: ResultT) : ResultOrNoOp() + class NoOp : ResultOrNoOp() +} + +fun ResultOrNoOp>.flatten(): ResultOrNoOp { + return when (this) { + is ResultOrNoOp.Done -> res + else -> ResultOrNoOp.NoOp() + } +} + +fun ResultOrNoOp.getOrNull(): ResultT? { + return when (this) { + is ResultOrNoOp.Done -> res + else -> null + } +} diff --git a/app/src/main/java/ceui/pixiv/ui/common/NavFragmentViewModel.kt b/app/src/main/java/ceui/pixiv/ui/common/NavFragmentViewModel.kt index 1a5445b69..42414a2f5 100644 --- a/app/src/main/java/ceui/pixiv/ui/common/NavFragmentViewModel.kt +++ b/app/src/main/java/ceui/pixiv/ui/common/NavFragmentViewModel.kt @@ -16,4 +16,17 @@ class NavFragmentViewModel(state: SavedStateHandle): ViewModel() { } val viewCreatedTime = state.getLiveData("viewCreatedTime") + + private val doneOnceTasks: MutableSet = + state["doneOnceTasks"] ?: mutableSetOf().also { + state["doneOnceTasks"] = it + } + + fun taskHasDone(taskId: String): Boolean { + return doneOnceTasks.contains(taskId) + } + + fun setTaskHasDone(taskId: String) { + doneOnceTasks.add(taskId) + } } \ No newline at end of file diff --git a/app/src/main/java/ceui/pixiv/ui/common/PixivFragment.kt b/app/src/main/java/ceui/pixiv/ui/common/PixivFragment.kt index ad2362eb1..2db0d9cb7 100644 --- a/app/src/main/java/ceui/pixiv/ui/common/PixivFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/common/PixivFragment.kt @@ -99,6 +99,18 @@ open class PixivFragment(layoutId: Int) : Fragment(layoutId), fragmentViewModel.viewCreatedTime.value = System.currentTimeMillis() } + fun runOnceWithinFragmentLifecycle( + taskId: String, + task: () -> ResultT + ): ResultOrNoOp { + return if (fragmentViewModel.taskHasDone(taskId)) { + ResultOrNoOp.NoOp() + } else { + fragmentViewModel.setTaskHasDone(taskId) + ResultOrNoOp.Done(task()) + } + } + override fun onClickIllustCard(illust: Illust) { onClickIllust(illust.id) } diff --git a/app/src/main/java/ceui/pixiv/ui/detail/ArtworkFragment.kt b/app/src/main/java/ceui/pixiv/ui/detail/ArtworkFragment.kt index 950be79be..de8e4fa8c 100644 --- a/app/src/main/java/ceui/pixiv/ui/detail/ArtworkFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/detail/ArtworkFragment.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import ceui.pixiv.ui.common.PixivFragment import ceui.lisa.R +import ceui.lisa.database.AppDatabase import ceui.lisa.databinding.FragmentPixivListBinding import ceui.lisa.models.ObjectSpec import ceui.lisa.view.LinearItemDecorationKt @@ -14,9 +15,11 @@ import ceui.loxia.ObjectType import ceui.loxia.clearItemDecorations import ceui.loxia.combineLatest import ceui.loxia.flag.FlagReasonFragmentArgs +import ceui.loxia.launchSuspend import ceui.loxia.pushFragment import ceui.loxia.threadSafeArgs import ceui.pixiv.db.EntityWrapper +import ceui.pixiv.db.RecordType import ceui.pixiv.ui.blocking.BlockingManager import ceui.pixiv.ui.comments.CommentsFragmentArgs import ceui.pixiv.ui.common.FitsSystemWindowFragment @@ -34,6 +37,9 @@ import ceui.pixiv.utils.ppppx import ceui.pixiv.utils.setOnClick import ceui.pixiv.ui.common.viewBinding import ceui.pixiv.ui.works.blurBackground +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import timber.log.Timber import kotlin.getValue class ArtworkFragment : PixivFragment(R.layout.fragment_pixiv_list), FitsSystemWindowFragment, GalleryActionReceiver { @@ -47,14 +53,18 @@ class ArtworkFragment : PixivFragment(R.layout.fragment_pixiv_list), FitsSystemW override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) setUpRefreshState(binding, viewModel, ListMode.CUSTOM) - combineLatest(BlockingManager.isWorkBlocked(safeArgs.illustId), viewModel.illustLiveData).observe(viewLifecycleOwner) { (isBlocked, illust) -> - if (illust == null) { + val ctx = requireContext() + + val isBlockedLiveData = AppDatabase.getAppDatabase(ctx).generalDao().isObjectBlocked(RecordType.BLOCK_ILLUST, safeArgs.illustId) + + binding.listView.layoutManager = LinearLayoutManager(ctx) + blurBackground(binding, safeArgs.illustId) + + combineLatest(isBlockedLiveData, viewModel.illustLiveData).observe(viewLifecycleOwner) { (isBlocked, illust) -> + if (isBlocked == null || illust == null) { return@observe } - val ctx = requireContext() - EntityWrapper.visitIllust(ctx, illust) - if (isBlocked == true) { binding.refreshLayout.isVisible = false binding.pageBackground.isVisible = false @@ -63,19 +73,22 @@ class ArtworkFragment : PixivFragment(R.layout.fragment_pixiv_list), FitsSystemW showActionMenu { add( MenuItem(getString(R.string.remove_blocking)) { - BlockingManager.removeBlockedWork(safeArgs.illustId) + EntityWrapper.unblockIllust(ctx, illust) } ) } } } else { + runOnceWithinFragmentLifecycle("visit-illust-${safeArgs.illustId}") { + EntityWrapper.visitIllust(ctx, illust) + } + binding.refreshLayout.isVisible = true binding.pageBackground.isVisible = true binding.dimmer.isVisible = true binding.listView.clearItemDecorations() binding.listView.addItemDecoration(LinearItemDecorationKt(16.ppppx, illust.page_count)) - binding.listView.layoutManager = LinearLayoutManager(ctx) - blurBackground(binding, safeArgs.illustId) + binding.toolbarLayout.naviMore.setOnClick { showActionMenu { add( @@ -101,7 +114,7 @@ class ArtworkFragment : PixivFragment(R.layout.fragment_pixiv_list), FitsSystemW ) add( MenuItem(getString(R.string.add_blocking)) { - BlockingManager.addBlockedWork(safeArgs.illustId) + EntityWrapper.blockIllust(ctx, illust) } ) } diff --git a/app/src/main/java/ceui/pixiv/ui/history/HistoryViewModel.kt b/app/src/main/java/ceui/pixiv/ui/history/HistoryViewModel.kt index c2c5e3725..cc0f61cd8 100644 --- a/app/src/main/java/ceui/pixiv/ui/history/HistoryViewModel.kt +++ b/app/src/main/java/ceui/pixiv/ui/history/HistoryViewModel.kt @@ -61,13 +61,13 @@ class HistoryViewModel( } private fun mapper(entity: GeneralEntity): ListItemHolder? { - return if (recordType == RecordType.VIEW_ILLUST_HISTORY) { + return if (recordType == RecordType.VIEW_ILLUST_HISTORY || recordType == RecordType.BLOCK_ILLUST) { val illust = entity.typedObject() IllustCardHolder(illust) - } else if (recordType == RecordType.VIEW_USER_HISTORY) { + } else if (recordType == RecordType.VIEW_USER_HISTORY || recordType == RecordType.BLOCK_USER) { val user = entity.typedObject() UserInfoHolder(user.id) - } else if (recordType == RecordType.VIEW_NOVEL_HISTORY) { + } else if (recordType == RecordType.VIEW_NOVEL_HISTORY || recordType == RecordType.BLOCK_NOVEL) { val novel = entity.typedObject() NovelCardHolder(novel) } else { @@ -76,13 +76,13 @@ class HistoryViewModel( } override fun prepareIdMap(fragmentUniqueId: String) { - if (recordType == RecordType.VIEW_ILLUST_HISTORY) { + if (recordType == RecordType.VIEW_ILLUST_HISTORY || recordType == RecordType.BLOCK_ILLUST) { val filteredList = _itemHolders.value.orEmpty() .filterIsInstance() .map { it.illust.id } ArtworksMap.store[fragmentUniqueId] = filteredList - } else if (recordType == RecordType.VIEW_NOVEL_HISTORY) { + } else if (recordType == RecordType.VIEW_NOVEL_HISTORY || recordType == RecordType.BLOCK_NOVEL) { val filteredList = _itemHolders.value.orEmpty() .filterIsInstance() .map { it.novel.id } diff --git a/app/src/main/java/ceui/pixiv/ui/novel/NovelTextFragment.kt b/app/src/main/java/ceui/pixiv/ui/novel/NovelTextFragment.kt index bec7f372b..a32cc2ae7 100644 --- a/app/src/main/java/ceui/pixiv/ui/novel/NovelTextFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/novel/NovelTextFragment.kt @@ -63,7 +63,9 @@ class NovelTextFragment : PixivFragment(R.layout.fragment_pixiv_list), FitsSyste combineLatest(liveNovel, textModel.webNovel).observe(viewLifecycleOwner) { (novel, webNovel) -> if (novel != null) { - EntityWrapper.visitNovel(requireContext(), novel) + runOnceWithinFragmentLifecycle("visit-novel-${safeArgs.novelId}") { + EntityWrapper.visitNovel(requireContext(), novel) + } } binding.toolbarLayout.naviMore.setOnClick { diff --git a/app/src/main/java/ceui/pixiv/ui/user/MineProfileFragment.kt b/app/src/main/java/ceui/pixiv/ui/user/MineProfileFragment.kt index 3e9815a79..cc4a48f5f 100644 --- a/app/src/main/java/ceui/pixiv/ui/user/MineProfileFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/user/MineProfileFragment.kt @@ -92,7 +92,8 @@ class MineProfileFragment : PixivFragment(R.layout.fragment_pixiv_list) { }, TabCellHolder(getString(R.string.blocking_list)).onItemClick { pushFragment( - R.id.navigation_blocking_item_list, + R.id.navigation_common_viewpager, + CommonViewPagerFragmentArgs(ViewPagerContentType.MyBlockingHistory).toBundle() ) }, TabCellHolder(getString(R.string.created_tasks)).onItemClick { diff --git a/app/src/main/java/ceui/pixiv/ui/user/UserFragment.kt b/app/src/main/java/ceui/pixiv/ui/user/UserFragment.kt index 7ea88185f..358f2c687 100644 --- a/app/src/main/java/ceui/pixiv/ui/user/UserFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/user/UserFragment.kt @@ -68,7 +68,9 @@ class UserFragment : PixivFragment(R.layout.fragment_user), ViewPagerFragment, S windowInsets } viewModel.userLiveData.observe(viewLifecycleOwner) { user -> - EntityWrapper.visitUser(requireContext(), user) + runOnceWithinFragmentLifecycle("visit-user-${safeArgs.userId}") { + EntityWrapper.visitUser(requireContext(), user) + } binding.iconOfficial.isVisible = user.isOfficial() binding.iconVolunteer.isVisible = user.isVolunteer() } diff --git a/app/src/main/res/navigation/mobile_navigation.xml b/app/src/main/res/navigation/mobile_navigation.xml index 45210e4a1..5ae0d6026 100644 --- a/app/src/main/res/navigation/mobile_navigation.xml +++ b/app/src/main/res/navigation/mobile_navigation.xml @@ -63,7 +63,11 @@ + tools:layout="@layout/fragment_pixiv_list" > + + Date: Tue, 11 Feb 2025 14:55:56 +0800 Subject: [PATCH 2/3] aa --- .../java/ceui/lisa/utils/ShareIllust.java | 1 + .../main/java/ceui/loxia/PixivHttpError.kt | 12 ++++++++++ app/src/main/java/ceui/loxia/RefreshState.kt | 16 ++++++++++++- .../pixiv/ui/detail/ArtworkCaptionHolder.kt | 16 +++++++++++++ .../ceui/pixiv/ui/novel/NovelCaptionHolder.kt | 14 +++++++++++ .../main/res/layout/cell_artwork_caption.xml | 24 +++++++++++++++---- .../main/res/layout/cell_novel_caption.xml | 24 +++++++++++++++---- app/src/main/res/values/strings.xml | 2 ++ 8 files changed, 100 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/ceui/loxia/PixivHttpError.kt diff --git a/app/src/main/java/ceui/lisa/utils/ShareIllust.java b/app/src/main/java/ceui/lisa/utils/ShareIllust.java index 241d6d03a..14c1f8cab 100644 --- a/app/src/main/java/ceui/lisa/utils/ShareIllust.java +++ b/app/src/main/java/ceui/lisa/utils/ShareIllust.java @@ -13,6 +13,7 @@ public abstract class ShareIllust implements IExecutor { public static final String URL_Head = "https://www.pixiv.net/artworks/"; + public static final String USER_URL_Head = "https://www.pixiv.net/users/"; private final IllustsBean mIllustsBean; private final Context mContext; diff --git a/app/src/main/java/ceui/loxia/PixivHttpError.kt b/app/src/main/java/ceui/loxia/PixivHttpError.kt new file mode 100644 index 000000000..7bdda64fc --- /dev/null +++ b/app/src/main/java/ceui/loxia/PixivHttpError.kt @@ -0,0 +1,12 @@ +package ceui.loxia + +data class PixivHttpError( + val user_message: String? = null, + val message: String? = null, + val reason: String? = null, +) + +data class ErrorResp( + val error: PixivHttpError? = null +) + diff --git a/app/src/main/java/ceui/loxia/RefreshState.kt b/app/src/main/java/ceui/loxia/RefreshState.kt index 5d4c186fe..1471871dd 100644 --- a/app/src/main/java/ceui/loxia/RefreshState.kt +++ b/app/src/main/java/ceui/loxia/RefreshState.kt @@ -5,8 +5,11 @@ import androidx.core.view.isVisible import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LiveData import ceui.lisa.R +import ceui.lisa.activities.Shaft import ceui.lisa.databinding.ItemLoadingBinding import ceui.pixiv.utils.setOnClick +import retrofit2.HttpException +import timber.log.Timber import java.io.Serializable import java.lang.Exception import java.net.SocketTimeoutException @@ -72,7 +75,18 @@ fun Throwable.getHumanReadableMessage(context: Context): String { val title = titleAfter.substringBefore("") title } else { - "${lc}: ${this.javaClass.simpleName}" + if (this is HttpException) { + val errorBody = this.response()?.errorBody()?.string() + try { + val obj = Shaft.sGson.fromJson(errorBody, ErrorResp::class.java) + obj.error?.user_message ?: errorBody ?: "" + } catch (ex: kotlin.Exception) { + Timber.e(ex) + errorBody ?: "" + } + } else { + "${lc}: ${this.javaClass.simpleName}" + } } } } \ No newline at end of file diff --git a/app/src/main/java/ceui/pixiv/ui/detail/ArtworkCaptionHolder.kt b/app/src/main/java/ceui/pixiv/ui/detail/ArtworkCaptionHolder.kt index 8ab820a96..2ecc2ecd9 100644 --- a/app/src/main/java/ceui/pixiv/ui/detail/ArtworkCaptionHolder.kt +++ b/app/src/main/java/ceui/pixiv/ui/detail/ArtworkCaptionHolder.kt @@ -1,17 +1,21 @@ package ceui.pixiv.ui.detail +import android.text.method.LinkMovementMethod import androidx.core.text.HtmlCompat import androidx.core.view.isVisible import ceui.lisa.R import ceui.lisa.annotations.ItemHolder import ceui.lisa.databinding.CellArtworkCaptionBinding import ceui.lisa.utils.Common +import ceui.lisa.utils.ShareIllust import ceui.loxia.DateParse import ceui.loxia.Illust import ceui.loxia.ObjectPool import ceui.pixiv.ui.common.ListItemHolder import ceui.pixiv.ui.common.ListItemViewHolder +import ceui.pixiv.ui.novel.CustomLinkMovementMethod import ceui.pixiv.utils.setOnClick +import timber.log.Timber class ArtworkCaptionHolder(val illustId: Long) : ListItemHolder() { @@ -37,6 +41,18 @@ class ArtworkCaptionViewHolder(bd: CellArtworkCaptionBinding) : ListItemViewHold binding.userId.setOnClick { Common.copy(context, illust.user?.id?.toString()) } + binding.illustLink.text = + context.getString(R.string.artwork_link, ShareIllust.URL_Head + illust.id) + binding.illustLink.setOnClick { + Common.copy(context, ShareIllust.URL_Head + illust.id) + } + + binding.userLink.text = + context.getString(R.string.user_link, ShareIllust.USER_URL_Head + illust.user?.id) + binding.userLink.setOnClick { + Common.copy(context, ShareIllust.USER_URL_Head + illust.user?.id) + } + binding.publishTime.text = context.getString( R.string.published_on, DateParse.getTimeAgo(context, illust.create_date) diff --git a/app/src/main/java/ceui/pixiv/ui/novel/NovelCaptionHolder.kt b/app/src/main/java/ceui/pixiv/ui/novel/NovelCaptionHolder.kt index dc68d774b..ef0f81498 100644 --- a/app/src/main/java/ceui/pixiv/ui/novel/NovelCaptionHolder.kt +++ b/app/src/main/java/ceui/pixiv/ui/novel/NovelCaptionHolder.kt @@ -9,6 +9,7 @@ import ceui.lisa.annotations.ItemHolder import ceui.lisa.databinding.CellArtworkCaptionBinding import ceui.lisa.databinding.CellNovelCaptionBinding import ceui.lisa.utils.Common +import ceui.lisa.utils.ShareIllust import ceui.loxia.Client import ceui.loxia.DateParse import ceui.loxia.Novel @@ -19,6 +20,7 @@ import ceui.loxia.launchSuspend import ceui.pixiv.ui.common.IllustCardActionReceiver import ceui.pixiv.ui.common.ListItemHolder import ceui.pixiv.ui.common.ListItemViewHolder +import ceui.pixiv.ui.common.NOVEL_URL_HEAD import ceui.pixiv.ui.common.NovelActionReceiver import ceui.pixiv.ui.common.PixivFragment import ceui.pixiv.ui.detail.ArtworksMap @@ -63,6 +65,18 @@ class NovelCaptionViewHolder(bd: CellNovelCaptionBinding) : ListItemViewHolder + + + + + + + + 私信 下载此作者的全部作品 浏览历史 + 作品链接:%1$s + 作者链接:%1$s From c0af7450ac4501c567d34a51539b52ec96ba8eba Mon Sep 17 00:00:00 2001 From: LoxiaLiSA Date: Tue, 11 Feb 2025 15:09:20 +0800 Subject: [PATCH 3/3] aa --- .../ceui/pixiv/ui/chats/SquareFragment.kt | 5 +-- .../ceui/pixiv/ui/detail/ArtworkFragment.kt | 11 ++++++- .../ceui/pixiv/ui/detail/ArtworkViewModel.kt | 12 +++++-- .../main/res/drawable/ic_chevron_right.xml | 11 +++++++ .../res/layout/item_red_section_header.xml | 32 ++++++++++++++----- 5 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 app/src/main/res/drawable/ic_chevron_right.xml diff --git a/app/src/main/java/ceui/pixiv/ui/chats/SquareFragment.kt b/app/src/main/java/ceui/pixiv/ui/chats/SquareFragment.kt index 13e1f0fdc..24f15b507 100644 --- a/app/src/main/java/ceui/pixiv/ui/chats/SquareFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/chats/SquareFragment.kt @@ -170,8 +170,8 @@ class RedSectionHeaderViewHolder(aa: ItemRedSectionHeaderBinding) : override fun onBindViewHolder(holder: RedSectionHeaderHolder, position: Int) { super.onBindViewHolder(holder, position) binding.title.text = holder.title - binding.seeMore.isVisible = holder.type != 0 - binding.seeMore.setOnClick { + binding.seeMoreLayout.isVisible = holder.type != 0 + binding.seeMoreLayout.setOnClick { it.findActionReceiverOrNull()?.seeMore(holder.type) } val liveEndText = holder.liveEndText @@ -195,4 +195,5 @@ object SeeMoreType { const val USER_CREATED_MANGA = 200 const val USER_BOOKMARKED_ILLUST = 201 const val USER_CREATED_NOVEL = 202 + const val RELATED_ILLUST = 203 } diff --git a/app/src/main/java/ceui/pixiv/ui/detail/ArtworkFragment.kt b/app/src/main/java/ceui/pixiv/ui/detail/ArtworkFragment.kt index de8e4fa8c..d4a904bc0 100644 --- a/app/src/main/java/ceui/pixiv/ui/detail/ArtworkFragment.kt +++ b/app/src/main/java/ceui/pixiv/ui/detail/ArtworkFragment.kt @@ -21,6 +21,8 @@ import ceui.loxia.threadSafeArgs import ceui.pixiv.db.EntityWrapper import ceui.pixiv.db.RecordType import ceui.pixiv.ui.blocking.BlockingManager +import ceui.pixiv.ui.chats.SeeMoreAction +import ceui.pixiv.ui.chats.SeeMoreType import ceui.pixiv.ui.comments.CommentsFragmentArgs import ceui.pixiv.ui.common.FitsSystemWindowFragment import ceui.pixiv.ui.common.ListMode @@ -36,13 +38,14 @@ import ceui.pixiv.widgets.showActionMenu import ceui.pixiv.utils.ppppx import ceui.pixiv.utils.setOnClick import ceui.pixiv.ui.common.viewBinding +import ceui.pixiv.ui.related.RelatedIllustsFragmentArgs import ceui.pixiv.ui.works.blurBackground import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import timber.log.Timber import kotlin.getValue -class ArtworkFragment : PixivFragment(R.layout.fragment_pixiv_list), FitsSystemWindowFragment, GalleryActionReceiver { +class ArtworkFragment : PixivFragment(R.layout.fragment_pixiv_list), FitsSystemWindowFragment, GalleryActionReceiver, SeeMoreAction { private val binding by viewBinding(FragmentPixivListBinding::bind) private val safeArgs by threadSafeArgs() @@ -132,4 +135,10 @@ class ArtworkFragment : PixivFragment(R.layout.fragment_pixiv_list), FitsSystemW ).toBundle() ) } + + override fun seeMore(type: Int) { + if (type == SeeMoreType.RELATED_ILLUST) { + pushFragment(R.id.navigation_related_illusts, RelatedIllustsFragmentArgs(safeArgs.illustId).toBundle()) + } + } } \ No newline at end of file diff --git a/app/src/main/java/ceui/pixiv/ui/detail/ArtworkViewModel.kt b/app/src/main/java/ceui/pixiv/ui/detail/ArtworkViewModel.kt index 735cb4a8e..9bdd1026c 100644 --- a/app/src/main/java/ceui/pixiv/ui/detail/ArtworkViewModel.kt +++ b/app/src/main/java/ceui/pixiv/ui/detail/ArtworkViewModel.kt @@ -13,6 +13,7 @@ import ceui.loxia.ObjectPool import ceui.loxia.RefreshHint import ceui.loxia.RefreshState import ceui.pixiv.ui.chats.RedSectionHeaderHolder +import ceui.pixiv.ui.chats.SeeMoreType import ceui.pixiv.ui.common.DataSource import ceui.pixiv.ui.common.HoldersContainer import ceui.pixiv.ui.common.HoldersViewModel @@ -43,7 +44,8 @@ class ArtworkViewModel( if (holders.isNotEmpty()) { // 从现有列表中剔除 LoadingHolder val filteredList = - (_itemHolders.value ?: listOf()).filterNot { it is LoadingHolder }.toMutableList() + (_itemHolders.value ?: listOf()).filterNot { it is LoadingHolder } + .toMutableList() // 添加新数据 filteredList.addAll(holders) @@ -84,7 +86,13 @@ class ArtworkViewModel( result.add(UserInfoHolder(illust.user?.id ?: 0L)) result.add(RedSectionHeaderHolder("简介")) result.add(ArtworkCaptionHolder(illustId)) - result.add(RedSectionHeaderHolder(context.getString(R.string.related_artworks))) + result.add( + RedSectionHeaderHolder( + context.getString(R.string.related_artworks), + type = SeeMoreType.RELATED_ILLUST, + seeMoreString = context.getString(R.string.see_more) + ) + ) result.add(LoadingHolder(_relatedIllustsDataSource.refreshStateImpl) { viewModelScope.launch { _relatedIllustsDataSource.refreshImpl( diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/app/src/main/res/drawable/ic_chevron_right.xml new file mode 100644 index 000000000..3d218ad8b --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_right.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/layout/item_red_section_header.xml b/app/src/main/res/layout/item_red_section_header.xml index 2915e2206..a750d1f8d 100644 --- a/app/src/main/res/layout/item_red_section_header.xml +++ b/app/src/main/res/layout/item_red_section_header.xml @@ -1,5 +1,6 @@ - + @@ -24,18 +25,33 @@ style="@style/textMontserratSemiBold" android:text="Description" android:layout_gravity="center_vertical" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content" /> - + android:layout_height="wrap_content"> + + + + + + \ No newline at end of file