From ff3f4a0d66344a0f6c9e0dc21bb6f0d189fea82b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 5 Apr 2023 10:13:12 +0900 Subject: [PATCH 001/432] =?UTF-8?q?feat:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AE=E7=A8=AE=E5=88=A5=E3=81=8CPleroma?= =?UTF-8?q?=E3=81=A7=E3=81=82=E3=82=8B=E3=81=93=E3=81=A8=E3=82=92=E8=A8=98?= =?UTF-8?q?=E9=8C=B2=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/account/SignOutUseCaseImpl.kt | 4 ++-- .../milktea/data/infrastructure/account/db/AccountRecord.kt | 2 ++ .../java/net/pantasystem/milktea/model/account/Account.kt | 2 +- .../milktea/model/account/MakeDefaultPagesUseCase.kt | 2 +- .../milktea/model/file/UpdateAppFileSensitiveUseCase.kt | 2 +- .../milktea/model/list/UserListTabToggleAddToTabUseCase.kt | 2 +- 6 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/SignOutUseCaseImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/SignOutUseCaseImpl.kt index e0be0efc39..541d2ca833 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/SignOutUseCaseImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/SignOutUseCaseImpl.kt @@ -26,7 +26,7 @@ class SignOutUseCaseImpl @Inject constructor( subscriptionUnRegistration .unregister(account.accountId) } - Account.InstanceType.MASTODON -> {} + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> {} } }.mapCancellableCatching { accountRepository.delete(account) @@ -40,7 +40,7 @@ class SignOutUseCaseImpl @Inject constructor( socketWithAccountProvider.get(account.accountId)?.disconnect() } } - Account.InstanceType.MASTODON -> {} + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> {} } }.mapCancellableCatching { accountStore.initialize() diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/db/AccountRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/db/AccountRecord.kt index 1eabfb36f6..59e6af5f0c 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/db/AccountRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/db/AccountRecord.kt @@ -111,6 +111,7 @@ class AccountInstanceTypeConverter { return when (type) { Account.InstanceType.MISSKEY -> "misskey" Account.InstanceType.MASTODON -> "mastodon" + Account.InstanceType.PLEROMA -> "pleroma" } } @@ -120,6 +121,7 @@ class AccountInstanceTypeConverter { return when (type) { "misskey" -> Account.InstanceType.MISSKEY "mastodon" -> Account.InstanceType.MASTODON + "pleroma" -> Account.InstanceType.PLEROMA else -> throw IllegalArgumentException("未知のアカウント種別です") } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt index d4a5498d12..7018d4627c 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt @@ -17,7 +17,7 @@ data class Account( ) : Serializable { enum class InstanceType { - MISSKEY, MASTODON + MISSKEY, MASTODON, PLEROMA } constructor( diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/MakeDefaultPagesUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/MakeDefaultPagesUseCase.kt index 6054ac154c..ec8313832e 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/MakeDefaultPagesUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/MakeDefaultPagesUseCase.kt @@ -64,7 +64,7 @@ class MakeDefaultPagesUseCase( } defaultPages } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { listOf( PageableTemplate(account).mastodonHomeTimeline(pageDefaultStrings.homeTimeline), PageableTemplate(account).mastodonLocalTimeline(pageDefaultStrings.localTimeline), diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/file/UpdateAppFileSensitiveUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/file/UpdateAppFileSensitiveUseCase.kt index 5c5da8f750..9f02b6209c 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/file/UpdateAppFileSensitiveUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/file/UpdateAppFileSensitiveUseCase.kt @@ -33,7 +33,7 @@ class UpdateAppFileSensitiveUseCase @Inject constructor( ) AppFile.Remote(fileProperty.id) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val fileProperty = filePropertyDataSource.find(appFile.id).getOrThrow() filePropertyDataSource.add( fileProperty.copy( diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/list/UserListTabToggleAddToTabUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/list/UserListTabToggleAddToTabUseCase.kt index 1e863dd616..a17c41c362 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/list/UserListTabToggleAddToTabUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/list/UserListTabToggleAddToTabUseCase.kt @@ -35,7 +35,7 @@ class UserListTabToggleAddToTabUseCase @Inject constructor( Account.InstanceType.MISSKEY -> Pageable.UserListTimeline( listId.userListId ) - Account.InstanceType.MASTODON -> Pageable.Mastodon.ListTimeline( + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> Pageable.Mastodon.ListTimeline( listId.userListId ) } From f33640b054b79ea45b692148c9e68318158819b0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 5 Apr 2023 10:21:44 +0900 Subject: [PATCH 002/432] =?UTF-8?q?feat:=20mastodon=E3=81=A8=E4=BA=92?= =?UTF-8?q?=E6=8F=9B=E6=80=A7=E3=81=8C=E3=81=82=E3=82=8B=E3=81=AE=E3=81=A7?= =?UTF-8?q?=E5=88=86=E5=B2=90=E3=81=AE=E7=A8=AE=E5=88=A5=E3=81=ABpleroma?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/common_android_ui/TextType.kt | 2 +- .../data/infrastructure/ap/ApResolverRepositoryImpl.kt | 2 +- .../milktea/data/infrastructure/drive/uploaders.kt | 2 +- .../list/UserListRepositoryWebAPIImpl.kt | 4 ++-- .../infrastructure/markers/MarkerRepositoryImpl.kt | 4 ++-- .../notes/FavoriteNoteTimelinePagingStoreImpl.kt | 4 ++-- .../infrastructure/notes/NoteCaptureAPIAdapterImpl.kt | 2 +- .../notes/bookmark/BookmarkRepositoryImpl.kt | 4 ++-- .../notes/favorite/FavoriteAPIAdapter.kt | 4 ++-- .../data/infrastructure/notes/impl/NoteApiAdapter.kt | 10 +++++----- .../infrastructure/notes/impl/NoteRepositoryImpl.kt | 10 +++++----- .../notes/reaction/impl/ReactionRepositoryImpl.kt | 4 ++-- .../notification/impl/NotificationRepositoryImpl.kt | 4 +--- .../notification/impl/NotificationStoreImpl.kt | 2 +- .../notification/impl/NotificationStreamingImpl.kt | 2 +- .../data/infrastructure/report/ReportRepositoryImpl.kt | 2 +- .../infrastructure/user/FollowFollowerPagingModel.kt | 2 +- .../infrastructure/user/FollowRequestApiAdapter.kt | 6 +++--- .../milktea/data/infrastructure/user/UserApiAdapter.kt | 4 ++-- .../data/infrastructure/user/block/BlockApiAdapter.kt | 4 ++-- .../infrastructure/user/follow/FollowApiAdapter.kt | 6 +++--- .../data/infrastructure/user/mute/MuteApiAdapter.kt | 4 ++-- .../pantasystem/milktea/account/AccountTabViewModel.kt | 2 +- .../setting/viewmodel/page/PageSettingViewModel.kt | 2 +- .../milktea/user/viewmodel/UserDetailViewModel.kt | 2 +- 25 files changed, 46 insertions(+), 48 deletions(-) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/TextType.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/TextType.kt index 8c321c28b8..116b182efa 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/TextType.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/TextType.kt @@ -36,7 +36,7 @@ fun getTextType(account: Account, note: NoteRelation, instanceEmojis: Map { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { note.note.text?.let { val option = note.note.type as? Note.Type.Mastodon TextType.Mastodon( diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/ap/ApResolverRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/ap/ApResolverRepositoryImpl.kt index 98475abe1d..76897b20e7 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/ap/ApResolverRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/ap/ApResolverRepositoryImpl.kt @@ -53,7 +53,7 @@ class ApResolverRepositoryImpl @Inject constructor( } } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).search( q = uri, resolve = true diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/drive/uploaders.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/drive/uploaders.kt index ec4e765162..af5a95f633 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/drive/uploaders.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/drive/uploaders.kt @@ -72,7 +72,7 @@ class OkHttpFileUploaderProvider( instances[account.accountId] ?: throw IllegalStateException("生成したはずのインスタンスが消滅しました!!") } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { map[account.accountId] = MastodonOkHttpFileUploader( context, account, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/list/UserListRepositoryWebAPIImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/list/UserListRepositoryWebAPIImpl.kt index 779e8abbdd..66fcde4260 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/list/UserListRepositoryWebAPIImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/list/UserListRepositoryWebAPIImpl.kt @@ -48,7 +48,7 @@ class UserListRepositoryWebAPIImpl @Inject constructor( it.toEntity(account) } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).getMyLists() .throwIfHasError() .body() @@ -142,7 +142,7 @@ class UserListRepositoryWebAPIImpl @Inject constructor( .throwIfHasError() res.body()!!.toEntity(account) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val res = mastodonAPIProvider.get(account).getList(userListId.userListId) .throwIfHasError() requireNotNull(res.body()).toModel(account) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/markers/MarkerRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/markers/MarkerRepositoryImpl.kt index 673c1631eb..4d56df089a 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/markers/MarkerRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/markers/MarkerRepositoryImpl.kt @@ -31,7 +31,7 @@ class MarkerRepositoryImpl @Inject constructor( val account = accountRepository.get(accountId).getOrThrow() when(account.instanceType) { Account.InstanceType.MISSKEY -> throw IllegalArgumentException("Not support markers feature when use misskey.") - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).getMarkers(types.map { it.name.lowercase() }).throwIfHasError().body() @@ -51,7 +51,7 @@ class MarkerRepositoryImpl @Inject constructor( val account = accountRepository.get(accountId).getOrThrow() when(account.instanceType) { Account.InstanceType.MISSKEY -> throw IllegalArgumentException("Not support markers feature when use misskey.") - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).saveMarkers( markers = SaveMarkersRequest( home = params.home?.let { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/FavoriteNoteTimelinePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/FavoriteNoteTimelinePagingStoreImpl.kt index ad71e286e2..9c4d992707 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/FavoriteNoteTimelinePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/FavoriteNoteTimelinePagingStoreImpl.kt @@ -94,7 +94,7 @@ internal class FavoriteNoteTimelinePagingStoreImpl( FavoriteType.Misskey(it) } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { // NOTE: ページが末端であるかをチェックしている if (getSinceId() == null && !isEmpty()) { return@runCancellableCatching emptyList() @@ -125,7 +125,7 @@ internal class FavoriteNoteTimelinePagingStoreImpl( FavoriteType.Misskey(it) } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { // NOTE: ページが末端であるかをチェックしている if (getUntilId() == null && !isEmpty()) { return@runCancellableCatching emptyList() diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteCaptureAPIAdapterImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteCaptureAPIAdapterImpl.kt index 7e46c7c2f1..6f39ee9dac 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteCaptureAPIAdapterImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteCaptureAPIAdapterImpl.kt @@ -128,7 +128,7 @@ class NoteCaptureAPIAdapterImpl( }.launchIn(coroutineScope) noteIdWithJob[id] = job } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val job = requireNotNull(streamingAPIProvider.get(account)).connectUser().catch { e -> logger.error("ノート更新イベント受信中にエラー発生", e = e) }.onEach { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/bookmark/BookmarkRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/bookmark/BookmarkRepositoryImpl.kt index 15dba76209..5b0adf3681 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/bookmark/BookmarkRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/bookmark/BookmarkRepositoryImpl.kt @@ -29,7 +29,7 @@ class BookmarkRepositoryImpl @Inject constructor( val account = accountRepository.get(noteId.accountId).getOrThrow() when(account.instanceType) { Account.InstanceType.MISSKEY -> favoriteRepository.create(noteId).getOrThrow() - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).bookmarkStatus(noteId.noteId) .throwIfHasError() .body() @@ -44,7 +44,7 @@ class BookmarkRepositoryImpl @Inject constructor( val account = accountRepository.get(noteId.accountId).getOrThrow() when(account.instanceType) { Account.InstanceType.MISSKEY -> favoriteRepository.delete(noteId).getOrThrow() - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).unbookmarkStatus(noteId.noteId) .throwIfHasError() .body() diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/favorite/FavoriteAPIAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/favorite/FavoriteAPIAdapter.kt index 18e149915b..bc2bd14f81 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/favorite/FavoriteAPIAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/favorite/FavoriteAPIAdapter.kt @@ -27,7 +27,7 @@ class FavoriteAPIAdapter @Inject constructor( .throwIfHasError() SuccessfulResponseData.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val status = mastodonAPIProvider.get(account).favouriteStatus(noteId.noteId) .throwIfHasError() .body() @@ -44,7 +44,7 @@ class FavoriteAPIAdapter @Inject constructor( .throwIfHasError() SuccessfulResponseData.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val status = mastodonAPIProvider.get(account).unfavouriteStatus(noteId.noteId) .throwIfHasError() .body() diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt index 31a941fa0f..79e981b016 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt @@ -61,7 +61,7 @@ class NoteApiAdapter @Inject constructor( val noteDTO = result.getOrThrow() NoteResultType.Misskey(requireNotNull(noteDTO)) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val fileIds = coroutineScope { createNote.files?.map { appFile -> async { @@ -113,7 +113,7 @@ class NoteApiAdapter @Inject constructor( ).throwIfHasError().body() NoteResultType.Misskey(requireNotNull(body)) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account) .getStatus(noteId.noteId) .throwIfHasError().body() @@ -134,7 +134,7 @@ class NoteApiAdapter @Inject constructor( ).throwIfHasError() DeleteNoteResultType.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).deleteStatus(noteId.noteId) .throwIfHasError() .body() @@ -155,7 +155,7 @@ class NoteApiAdapter @Inject constructor( ).throwIfHasError() ToggleThreadMuteResultType.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account) .muteConversation(noteId.noteId) .throwIfHasError() @@ -177,7 +177,7 @@ class NoteApiAdapter @Inject constructor( ).throwIfHasError() ToggleThreadMuteResultType.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).unmuteConversation(noteId.noteId) .throwIfHasError() .body() diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt index f0ccb53e42..2afa766961 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt @@ -52,7 +52,7 @@ class NoteRepositoryImpl @Inject constructor( visibility = n.visibility )).getOrThrow() } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val toot = mastodonAPIProvider.get(account).reblog(noteId.noteId) .throwIfHasError() .body() @@ -67,7 +67,7 @@ class NoteRepositoryImpl @Inject constructor( val account = getAccount.get(noteId.accountId) when(account.instanceType) { Account.InstanceType.MISSKEY -> delete(noteId).getOrThrow() - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val res = mastodonAPIProvider.get(account).unreblog(noteId.noteId) .throwIfHasError() .body() @@ -216,7 +216,7 @@ class NoteRepositoryImpl @Inject constructor( noteDataSourceAdder.addNoteDtoToDataSource(account, it) } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).getStatusesContext(noteId.noteId) .throwIfHasError() .body() @@ -247,7 +247,7 @@ class NoteRepositoryImpl @Inject constructor( noteDataSourceAdder.addNoteDtoToDataSource(account, it) } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).getStatusesContext(noteId.noteId) .throwIfHasError() .body() @@ -314,7 +314,7 @@ class NoteRepositoryImpl @Inject constructor( ) } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { find(noteId).mapCancellableCatching { NoteState( isFavorited = (it.type as Note.Type.Mastodon).favorited ?: false, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionRepositoryImpl.kt index 3ade44e1e2..1c04c04335 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionRepositoryImpl.kt @@ -55,7 +55,7 @@ class ReactionRepositoryImpl @Inject constructor( noteDataSource.add(note.onIReacted(createReaction.reaction)) } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { if (nodeInfoRepository.find(account.getHost()) .getOrThrow().type !is NodeInfo.SoftwareType.Mastodon.Fedibird ) { @@ -98,7 +98,7 @@ class ReactionRepositoryImpl @Inject constructor( && noteDataSource.add(note.onIUnReacted()) .getOrThrow() != AddResult.Canceled)) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { if (nodeInfoRepository.find(account.getHost()) .getOrThrow().type !is NodeInfo.SoftwareType.Mastodon.Fedibird ) { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationRepositoryImpl.kt index 5305974af7..9023342476 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationRepositoryImpl.kt @@ -6,14 +6,12 @@ import net.pantasystem.milktea.api_streaming.Send import net.pantasystem.milktea.api_streaming.toJson import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common.throwIfHasError -import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.infrastructure.notification.db.UnreadNotificationDAO import net.pantasystem.milktea.data.streaming.SocketWithAccountProvider import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.markers.MarkerRepository -import net.pantasystem.milktea.model.markers.Markers import net.pantasystem.milktea.model.markers.SaveMarkerParams import net.pantasystem.milktea.model.notification.Notification import net.pantasystem.milktea.model.notification.NotificationDataSource @@ -40,7 +38,7 @@ class NotificationRepositoryImpl @Inject constructor( ) ).throwIfHasError() } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val latest = unreadNotificationDAO.getLatestUnreadId(accountId) ?: return@runCancellableCatching markerRepository.save( diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationStoreImpl.kt index 965ba08710..aa506c8be5 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationStoreImpl.kt @@ -62,7 +62,7 @@ class NotificationStoreImpl( misskeyAPIProvider = misskeyAPIProvider, ) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { MstNotificationEntityLoader( account = account, mastodonAPIProvider = mastodonAPIProvider, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationStreamingImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationStreamingImpl.kt index 77e7329ea3..f4adde8e4a 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationStreamingImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notification/impl/NotificationStreamingImpl.kt @@ -57,7 +57,7 @@ class NotificationStreamingImpl @Inject constructor( notificationCacheAdder.addAndConvert(account, it.body) } } - Account.InstanceType.MASTODON -> requireNotNull(streamingAPIProvider.get(account)).connectUser() + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> requireNotNull(streamingAPIProvider.get(account)).connectUser() .mapNotNull { (it as? Event.Notification)?.notification }.mapNotNull { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/report/ReportRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/report/ReportRepositoryImpl.kt index 15ac633e4e..65f329aa49 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/report/ReportRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/report/ReportRepositoryImpl.kt @@ -38,7 +38,7 @@ internal class ReportRepositoryImpl @Inject constructor( ) res.throwIfHasError() } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { mastodonAPIProvider.get(account).createReport( CreateReportRequest( accountId = report.userId.id, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/FollowFollowerPagingModel.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/FollowFollowerPagingModel.kt index 5f39aeca56..1251570c95 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/FollowFollowerPagingModel.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/FollowFollowerPagingModel.kt @@ -189,7 +189,7 @@ class FollowFollowerPagingModelImpl( else -> throw IllegalStateException("not support follow follower list") } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { MastodonLoader( requestType, account, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/FollowRequestApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/FollowRequestApiAdapter.kt index ebc17b0e11..135150e8c5 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/FollowRequestApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/FollowRequestApiAdapter.kt @@ -37,7 +37,7 @@ class FollowRequestApiAdapter @Inject constructor( ).throwIfHasError() FollowRequestResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).acceptFollowRequest(userId.id) .throwIfHasError() .body() @@ -58,7 +58,7 @@ class FollowRequestApiAdapter @Inject constructor( ).throwIfHasError() FollowRequestResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).rejectFollowRequest(userId.id) .throwIfHasError() .body() @@ -82,7 +82,7 @@ class FollowRequestApiAdapter @Inject constructor( requireNotNull(body) ) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val res = mastodonAPIProvider.get(account).getFollowRequests( maxId = untilId, minId = sinceId, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserApiAdapter.kt index a78c8b4702..13d8fa4d2f 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserApiAdapter.kt @@ -52,7 +52,7 @@ internal class UserApiAdapterImpl @Inject constructor( detail ) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val res = mastodonAPIProvider.get(account).getAccount(userId.id) .throwIfHasError() .body() @@ -93,7 +93,7 @@ internal class UserApiAdapterImpl @Inject constructor( ) SearchResult.Misskey(body) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = requireNotNull( mastodonAPIProvider.get(account).search( if (host == null) userName else "$userName@$host" diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/block/BlockApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/block/BlockApiAdapter.kt index e461f08cdf..194fc25dfc 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/block/BlockApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/block/BlockApiAdapter.kt @@ -34,7 +34,7 @@ class BlockApiAdapterImpl @Inject constructor( .throwIfHasError() UserActionResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).blockAccount(userId.id) .throwIfHasError() .body() @@ -56,7 +56,7 @@ class BlockApiAdapterImpl @Inject constructor( .throwIfHasError() UserActionResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).unblockAccount(userId.id) .throwIfHasError() .body() diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/follow/FollowApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/follow/FollowApiAdapter.kt index b44f291323..7f91583ffa 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/follow/FollowApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/follow/FollowApiAdapter.kt @@ -35,7 +35,7 @@ internal class FollowApiAdapterImpl @Inject constructor( ).throwIfHasError() UserActionResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { mastodonAPIProvider.get(account).follow(userId.id) .throwIfHasError().body().let { UserActionResult.Mastodon(requireNotNull(it)) @@ -54,7 +54,7 @@ internal class FollowApiAdapterImpl @Inject constructor( .body() UserActionResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { mastodonAPIProvider.get(account).unfollow(userId.id) .throwIfHasError() .body().let { @@ -77,7 +77,7 @@ internal class FollowApiAdapterImpl @Inject constructor( ).throwIfHasError() UserActionResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { mastodonAPIProvider.get(account).unfollow(userId.id).throwIfHasError() .body().let { UserActionResult.Mastodon(requireNotNull(it)) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/mute/MuteApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/mute/MuteApiAdapter.kt index d36a58df4d..bf27341625 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/mute/MuteApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/mute/MuteApiAdapter.kt @@ -41,7 +41,7 @@ internal class MuteApiAdapterImpl @Inject constructor( ).throwIfHasError() UserActionResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).muteAccount( createMute.userId.id, MuteAccountRequest( @@ -68,7 +68,7 @@ internal class MuteApiAdapterImpl @Inject constructor( ).throwIfHasError() UserActionResult.Misskey } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = mastodonAPIProvider.get(account).unmuteAccount(userId.id) .throwIfHasError() .body() diff --git a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabViewModel.kt b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabViewModel.kt index 429065a1eb..8f61b1986d 100644 --- a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabViewModel.kt +++ b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabViewModel.kt @@ -42,7 +42,7 @@ class AccountTabViewModel @Inject constructor( AccountTabTypes.Reactions(userId), ) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { listOf( AccountTabTypes.Account, AccountTabTypes.MastodonUserTimeline(userId), diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt index 77325ffae0..dc7b84e9b5 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt @@ -79,7 +79,7 @@ class PageSettingViewModel @Inject constructor( emptyList() } } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { listOf( PageType.MASTODON_HOME_TIMELINE, PageType.MASTODON_LOCAL_TIMELINE, diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt index df2a9a434a..9178e41788 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt @@ -136,7 +136,7 @@ class UserDetailViewModel @AssistedInject constructor( if (isPublicReaction) UserDetailTabType.Reactions(user.id) else null, ) } - Account.InstanceType.MASTODON -> { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { listOf( UserDetailTabType.MastodonUserTimeline(user.id), UserDetailTabType.MastodonUserTimelineWithReplies(user.id), From c8072f6e969c01f0d4dc224572e2a7273d6a1c31 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 5 Apr 2023 11:15:47 +0900 Subject: [PATCH 003/432] =?UTF-8?q?feat:=20Pleroma=E3=81=A7=E8=AA=8D?= =?UTF-8?q?=E8=A8=BC=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api/mastodon/apps/App.kt | 10 +++++ .../milktea/api/misskey/auth/App.kt | 23 +++++++++++ .../data/infrastructure/account/converter.kt | 16 ++++++++ .../data/infrastructure/auth/Authorization.kt | 17 ++++++++ .../infrastructure/auth/custom/AccessToken.kt | 18 +++++++++ .../auth/custom/CustomAuthBridge.kt | 16 ++++++++ .../auth/custom/CustomAuthStore.kt | 28 +++++++++++++ .../emoji/CustomEmojiApiAdapter.kt | 8 ++++ .../auth/viewmodel/app/AppAuthViewModel.kt | 3 ++ .../auth/viewmodel/app/AuthStateHelper.kt | 39 +++++++++++++++++++ .../auth/viewmodel/app/GetAccessToken.kt | 27 +++++++++++++ .../milktea/auth/viewmodel/app/UIState.kt | 5 +++ .../pantasystem/milktea/model/app/AppType.kt | 20 +++++++++- .../model/instance/InstanceInfoService.kt | 9 +++++ .../model/instance/InstanceInfoType.kt | 8 ++++ .../milktea/model/nodeinfo/NodeInfo.kt | 13 +++++++ .../notes/reaction/ToggleReactionUseCase.kt | 9 +++++ 17 files changed, 268 insertions(+), 1 deletion(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/apps/App.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/apps/App.kt index 1c07324d7b..858d743af1 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/apps/App.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/apps/App.kt @@ -43,6 +43,16 @@ data class App( redirectUri = redirectUri, ) } + + fun toPleromaModel() : AppType.Pleroma { + return AppType.Pleroma( + id = id, + name = name, + clientSecret = clientSecret, + clientId = clientId, + redirectUri = redirectUri, + ) + } } @Serializable diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/auth/App.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/auth/App.kt index 6b21d74dfd..adb51e6145 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/auth/App.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/auth/App.kt @@ -47,6 +47,10 @@ fun AppType.Companion.fromDTO(app: net.pantasystem.milktea.api.mastodon.apps.App return app.toModel() } +fun AppType.Companion.fromPleromaDTO(app: net.pantasystem.milktea.api.mastodon.apps.App): AppType { + return app.toPleromaModel() +} + fun AppType.Mastodon.generateAuthUrl(baseURL: String, scope: String): String { val encodedClientId = URLEncoder.encode(clientId, "utf-8") val encodedRedirectUri = URLEncoder.encode(redirectUri, "utf-8") @@ -55,6 +59,14 @@ fun AppType.Mastodon.generateAuthUrl(baseURL: String, scope: String): String { return "$baseURL/oauth/authorize?client_id=${encodedClientId}&redirect_uri=$encodedRedirectUri&response_type=$encodedResponseType&scope=$encodedScope" } +fun AppType.Pleroma.generateAuthUrl(baseURL: String, scope: String): String { + val encodedClientId = URLEncoder.encode(clientId, "utf-8") + val encodedRedirectUri = URLEncoder.encode(redirectUri, "utf-8") + val encodedResponseType = URLEncoder.encode("code", "utf-8") + val encodedScope = URLEncoder.encode(scope, "utf-8") + return "$baseURL/oauth/authorize?client_id=${encodedClientId}&redirect_uri=$encodedRedirectUri&response_type=$encodedResponseType&scope=$encodedScope" +} + /** * @param scope アプリ作成時に指定したscope * @param code redirectUrl+codeで帰ってきたコード @@ -69,3 +81,14 @@ fun AppType.Mastodon.createObtainToken(scope: String, code: String): ObtainToken grantType = "authorization_code" ) } + +fun AppType.Pleroma.createObtainToken(scope: String, code: String): ObtainToken { + return ObtainToken( + clientId = clientId, + clientSecret = clientSecret, + scope = scope, + redirectUri = redirectUri, + code = code, + grantType = "authorization_code" + ) +} \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/converter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/converter.kt index 0bc5cc9cce..0cd51344fa 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/converter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/converter.kt @@ -27,6 +27,19 @@ fun AccessToken.Mastodon.newAccount( ) } +fun AccessToken.Pleroma.newAccount( + instanceDomain: String +): Account { + return Account( + remoteId = this.account.id, + userName = this.account.username, + instanceDomain = instanceDomain, + token = accessToken, + instanceType = Account.InstanceType.PLEROMA, + pages = emptyList() + ) +} + fun AccessToken.MisskeyIdAndPassword.newAccount(instanceDomain: String): Account { return this.user.newAccount( instanceDomain, @@ -45,6 +58,9 @@ fun AccessToken.newAccount(instanceDomain: String): Account { is AccessToken.MisskeyIdAndPassword -> { this.newAccount(instanceDomain) } + is AccessToken.Pleroma -> { + this.newAccount(instanceDomain) + } } } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/Authorization.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/Authorization.kt index eb1c96179a..cdf5915ae4 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/Authorization.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/Authorization.kt @@ -43,6 +43,16 @@ sealed interface Authorization { } } + data class Pleroma( + override val instanceBaseURL: String, + val client: AppType.Pleroma, + val scope: String + ) : Waiting4UserAuthorization { + override fun generateAuthUrl(): String { + return client.generateAuthUrl(instanceBaseURL, scope) + } + } + } @@ -74,5 +84,12 @@ fun Authorization.Waiting4UserAuthorization.Companion.from(state: TemporarilyAut viaName = state.viaName ) } + is TemporarilyAuthState.Pleroma -> { + Authorization.Waiting4UserAuthorization.Pleroma( + client = state.app, + instanceBaseURL = state.instanceDomain, + scope = state.scope + ) + } } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/AccessToken.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/AccessToken.kt index 2b8b7de42b..525af29fd4 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/AccessToken.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/AccessToken.kt @@ -27,6 +27,14 @@ sealed interface AccessToken { val createdAt: Long, val account: MastodonAccountDTO ) : AccessToken + + data class Pleroma( + override val accessToken: String, + val tokenType: String, + val scope: String, + val createdAt: Long, + val account: MastodonAccountDTO + ) : AccessToken } @@ -46,4 +54,14 @@ fun MastodonAccessToken.toModel(account: MastodonAccountDTO) : AccessToken.Masto scope = scope, account = account ) +} + +fun MastodonAccessToken.toPleromaModel(account: MastodonAccountDTO): AccessToken.Pleroma { + return AccessToken.Pleroma( + accessToken = accessToken, + tokenType = tokenType, + createdAt = createdAt, + scope = scope, + account = account + ) } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/CustomAuthBridge.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/CustomAuthBridge.kt index b031736200..f3ccddf713 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/CustomAuthBridge.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/CustomAuthBridge.kt @@ -23,6 +23,13 @@ sealed interface TemporarilyAuthState { val scope: String, ) : TemporarilyAuthState + + data class Pleroma( + override val instanceDomain: String, + override val enabledDateEnd: Date, + val app: AppType.Pleroma, + val scope: String, + ) : TemporarilyAuthState } fun AppType.Misskey.createAuth(instanceDomain: String, session: Session, timeLimit: Date = Date(System.currentTimeMillis() + 3600 * 1000)): TemporarilyAuthState.Misskey { @@ -43,4 +50,13 @@ fun AppType.Mastodon.createAuth(instanceDomain: String, scope: String, timeLimit enabledDateEnd = timeLimit, app = this ) +} + +fun AppType.Pleroma.createAuth(instanceDomain: String, scope: String, timeLimit: Date = Date(System.currentTimeMillis() + 3600 * 1000)): TemporarilyAuthState.Pleroma { + return TemporarilyAuthState.Pleroma( + scope = scope, + instanceDomain = instanceDomain, + enabledDateEnd = timeLimit, + app = this + ) } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/CustomAuthStore.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/CustomAuthStore.kt index 996b10644e..3e2cc95a3d 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/CustomAuthStore.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/auth/custom/CustomAuthStore.kt @@ -63,6 +63,20 @@ class CustomAuthStore(private val sharedPreferences: SharedPreferences){ apply() } } + is TemporarilyAuthState.Pleroma -> { + sharedPreferences.edit().apply { + putString(MASTODON_SCOPE, customAuthBridge.scope) + putString(INSTANCE_DOMAIN, customAuthBridge.instanceDomain) + putString(REDIRECT_URI, customAuthBridge.app.redirectUri) + + putLong(ENABLED_DATE_END, customAuthBridge.enabledDateEnd.time) + putString(MASTODON_APP_CLIENT_ID, customAuthBridge.app.clientId) + putString(MASTODON_APP_CLIENT_SECRET, customAuthBridge.app.clientSecret) + putString(MASTODON_APP_ID, customAuthBridge.app.id) + putString(MASTODON_APP_NAME, customAuthBridge.app.name) + putString(TYPE, "pleroma") + }.apply() + } } @@ -107,6 +121,20 @@ class CustomAuthStore(private val sharedPreferences: SharedPreferences){ scope = it.getString(MASTODON_SCOPE, null)?: return null ) } + "pleroma" -> { + TemporarilyAuthState.Pleroma( + app = AppType.Pleroma( + clientId = it.getString(MASTODON_APP_CLIENT_ID, null)?: return null, + clientSecret = it.getString(MASTODON_APP_CLIENT_SECRET, null)?: return null, + redirectUri = it.getString(REDIRECT_URI, null)?: return null, + id = it.getString(MASTODON_APP_ID, null)?: return null, + name = it.getString(MASTODON_APP_NAME, null)?: return null, + ), + instanceDomain = instanceDomain, + enabledDateEnd = enabledDate, + scope = it.getString(MASTODON_SCOPE, null)?: return null + ) + } else -> null } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt index 6f1436b0f4..2958b89e8a 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt @@ -32,6 +32,14 @@ internal class CustomEmojiApiAdapterImpl @Inject constructor( it.toEmoji() } } + is NodeInfo.SoftwareType.Pleroma -> { + val emojis = mastodonAPIProvider.get("https://${nodeInfo.host}").getCustomEmojis() + .throwIfHasError() + .body() + emojis?.map { + it.toEmoji() + } + } is NodeInfo.SoftwareType.Misskey -> { if ( nodeInfo.type.getVersion() >= Version("13") diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AppAuthViewModel.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AppAuthViewModel.kt index c548ae83ba..53dfbd5346 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AppAuthViewModel.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AppAuthViewModel.kt @@ -162,6 +162,7 @@ class AppAuthViewModel @Inject constructor( is StateContent.Exist -> { val instanceBase = when (val info = meta.rawContent) { is InstanceType.Mastodon -> "https://${info.instance.uri}" + is InstanceType.Pleroma -> info.instance.uri is InstanceType.Misskey -> info.instance.uri } logger.debug { "instanceBaseUrl: $instanceBase" } @@ -178,6 +179,8 @@ class AppAuthViewModel @Inject constructor( is StateContent.NotExist -> throw IllegalStateException() } }.asLoadingStateFlow() + }.catch { + logger.error("アプリの作成に失敗", it) }.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AuthStateHelper.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AuthStateHelper.kt index 635118c117..49b7cfe41e 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AuthStateHelper.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AuthStateHelper.kt @@ -9,6 +9,7 @@ import net.pantasystem.milktea.api.misskey.MisskeyAPIServiceBuilder import net.pantasystem.milktea.api.misskey.auth.AppSecret import net.pantasystem.milktea.api.misskey.auth.SignInRequest import net.pantasystem.milktea.api.misskey.auth.fromDTO +import net.pantasystem.milktea.api.misskey.auth.fromPleromaDTO import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.auth.viewmodel.Permissions import net.pantasystem.milktea.common.runCancellableCatching @@ -68,6 +69,15 @@ class AuthStateHelper @Inject constructor( scope = "read write" ) } + is AppType.Pleroma -> { + val authState = app.createAuth(instanceBase, "read write") + customAuthStore.setCustomAuthBridge(authState) + Authorization.Waiting4UserAuthorization.Pleroma( + instanceBase, + client = app, + scope = "read write" + ) + } is AppType.Misskey -> { val secret = app.secret val authApi = misskeyAPIServiceBuilder.buildAuthAPI(instanceBase) @@ -108,6 +118,19 @@ class AuthStateHelper @Inject constructor( ?: throw IllegalStateException("Appの作成に失敗しました。") return AppType.fromDTO(app) } + is InstanceType.Pleroma -> { + val app = mastodonAPIProvider.get(url) + .createApp( + CreateApp( + clientName = appName, + redirectUris = CALL_BACK_URL, + scopes = "read write" + ) + ).throwIfHasError().body() + ?: throw IllegalStateException("Appの作成に失敗しました。") + + return AppType.fromPleromaDTO(app) + } is InstanceType.Misskey -> { val version = instanceType.instance.getVersion() val misskeyAPI = misskeyAPIProvider.get(url, version) @@ -134,6 +157,7 @@ class AuthStateHelper @Inject constructor( val misskey: Meta? val mastodon: MastodonInstanceInfo? + val pleroma: MastodonInstanceInfo? suspend fun fetchMeta(): Meta? { return withContext(Dispatchers.IO) { @@ -152,10 +176,17 @@ class AuthStateHelper @Inject constructor( is NodeInfo.SoftwareType.Mastodon -> { mastodon = fetchInstance() misskey = null + pleroma = null } is NodeInfo.SoftwareType.Misskey -> { misskey = fetchMeta() mastodon = null + pleroma = null + } + is NodeInfo.SoftwareType.Pleroma -> { + pleroma = fetchInstance() + misskey = null + mastodon = null } else -> { misskey = fetchMeta() @@ -164,6 +195,7 @@ class AuthStateHelper @Inject constructor( } else { null } + pleroma = null } } @@ -173,6 +205,10 @@ class AuthStateHelper @Inject constructor( if (mastodon != null) { return InstanceType.Mastodon(mastodon, nodeInfo?.type as? NodeInfo.SoftwareType.Mastodon) } + + if (pleroma != null) { + return InstanceType.Pleroma(pleroma, nodeInfo?.type as? NodeInfo.SoftwareType.Pleroma) + } throw IllegalArgumentException() } else { throw IllegalArgumentException("not support pattern url: $url") @@ -215,6 +251,9 @@ class AuthStateHelper @Inject constructor( true ) as User.Detail } + is AccessToken.Pleroma -> { + (a.accessToken as AccessToken.Pleroma).account.toModel(account) + } } userDataSource.add(user) accountStore.addAccount(account) diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/GetAccessToken.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/GetAccessToken.kt index ecf43b81c3..13455efa73 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/GetAccessToken.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/GetAccessToken.kt @@ -13,6 +13,7 @@ import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.infrastructure.auth.Authorization import net.pantasystem.milktea.data.infrastructure.auth.custom.AccessToken import net.pantasystem.milktea.data.infrastructure.auth.custom.toModel +import net.pantasystem.milktea.data.infrastructure.auth.custom.toPleromaModel import javax.inject.Inject @@ -46,6 +47,9 @@ class GetAccessToken @Inject constructor( is Authorization.Waiting4UserAuthorization.Mastodon -> { getAccessToken4Mastodon(a, code!!) } + is Authorization.Waiting4UserAuthorization.Pleroma -> { + getAccessToken4Pleroma(a, code!!) + } } } } @@ -72,4 +76,27 @@ class GetAccessToken @Inject constructor( throw e } } + + private suspend fun getAccessToken4Pleroma( + a: Authorization.Waiting4UserAuthorization.Pleroma, + code: String + ): AccessToken.Pleroma { + try { + logger.debug { "認証種別Mastodon: $a" } + val obtainToken = a.client.createObtainToken(scope = a.scope, code = code) + val accessToken = mastodonAPIProvider.get(a.instanceBaseURL).obtainToken(obtainToken) + .throwIfHasError() + .body() + logger.debug { "accessToken:$accessToken" } + val me = mastodonAPIProvider.get(a.instanceBaseURL, accessToken!!.accessToken) + .verifyCredentials() + .throwIfHasError() + logger.debug { "自身の情報, code=${me.code()}, message=${me.message()}" } + val account = me.body()!! + return accessToken.toPleromaModel(account) + } catch (e: Exception) { + logger.warning("AccessToken取得失敗", e = e) + throw e + } + } } \ No newline at end of file diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/UIState.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/UIState.kt index edf7aa9d1b..09d32c94f2 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/UIState.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/UIState.kt @@ -56,6 +56,11 @@ sealed interface InstanceType { val instance: Meta, override val softwareType: NodeInfo.SoftwareType.Misskey?, ) : InstanceType + + data class Pleroma( + val instance: MastodonInstanceInfo, + override val softwareType: NodeInfo.SoftwareType? + ) : InstanceType } sealed interface GenerateTokenResult { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/app/AppType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/app/AppType.kt index 7cfcf08985..f96be62d14 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/app/AppType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/app/AppType.kt @@ -12,7 +12,7 @@ sealed interface AppType { override val callbackUrl: String?, val isAuthorized: Boolean? = null, val permission: List = emptyList(), - override val secret: String? = null + override val secret: String? = null, ) : AppType companion object; @@ -35,4 +35,22 @@ sealed interface AppType { companion object } + + data class Pleroma( + val id: String, + override val name: String, + + val clientId: String, + + val redirectUri: String, + + val clientSecret: String, + ) : AppType { + override val callbackUrl: String + get() = redirectUri + override val secret: String + get() = clientSecret + + companion object + } } \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoService.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoService.kt index 9c20e185e5..be8e6a817c 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoService.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoService.kt @@ -32,6 +32,11 @@ open class InstanceInfoService @Inject constructor( metaRepository.find(instanceDomain).getOrThrow() ) } + is NodeInfo.SoftwareType.Pleroma -> { + InstanceInfoType.Pleroma( + mastodonInstanceInfoRepository.find(instanceDomain).getOrThrow() + ) + } is NodeInfo.SoftwareType.Other -> throw NoSuchElementException() } } @@ -48,6 +53,10 @@ open class InstanceInfoService @Inject constructor( metaRepository.sync(instanceDomain) customEmojiRepository.sync(it.host).getOrThrow() } + is NodeInfo.SoftwareType.Pleroma -> { + mastodonInstanceInfoRepository.sync(instanceDomain).getOrThrow() + customEmojiRepository.sync(it.host) + } is NodeInfo.SoftwareType.Other -> throw NoSuchElementException() } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt index c76cafecc3..625c627c39 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt @@ -4,9 +4,12 @@ sealed interface InstanceInfoType { data class Misskey(val meta: Meta) : InstanceInfoType data class Mastodon(val info: MastodonInstanceInfo) : InstanceInfoType + data class Pleroma(val info: MastodonInstanceInfo) : InstanceInfoType + val iconUrl: String? get() { return when(this) { is Mastodon -> "https://${info.uri}/favicon.ico" + is Pleroma -> "https://${info.uri}/favicon.ico" is Misskey -> meta.iconUrl } } @@ -14,6 +17,7 @@ sealed interface InstanceInfoType { val uri: String get() { return when(this) { is Mastodon -> "https://${info.uri}" + is Pleroma -> "https://${info.uri}" is Misskey -> meta.uri } } @@ -21,6 +25,7 @@ sealed interface InstanceInfoType { val maxNoteTextLength: Int get() { return when(this) { is Mastodon -> info.configuration?.statuses?.maxCharacters ?: 500 + is Pleroma -> info.configuration?.statuses?.maxCharacters ?: 500 is Misskey -> meta.maxNoteTextLength ?: 3000 } } @@ -28,6 +33,7 @@ sealed interface InstanceInfoType { val maxFileCount: Int get() { return when(this) { is Mastodon -> info.configuration?.statuses?.maxMediaAttachments ?: 4 + is Pleroma -> info.configuration?.statuses?.maxMediaAttachments ?: 4 is Misskey -> if (meta.getVersion() >= Version("12.100.2")) { 16 } else { @@ -37,8 +43,10 @@ sealed interface InstanceInfoType { } val maxReactionsPerAccount: Int get() { + // TODO: Pleromaの場合はリアクション可能な件数の判定方法が異なるのであとで修正する return when(this) { is Mastodon -> info.configuration?.emojiReactions?.maxReactionsPerAccount ?: 0 + is Pleroma -> info.configuration?.emojiReactions?.maxReactionsPerAccount ?: 0 is Misskey -> 1 } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/nodeinfo/NodeInfo.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/nodeinfo/NodeInfo.kt index 2e57988e3d..955e99a7c6 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/nodeinfo/NodeInfo.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/nodeinfo/NodeInfo.kt @@ -52,6 +52,17 @@ data class NodeInfo( } + sealed interface Pleroma : SoftwareType { + data class Normal( + override val name: String, + override val version: String + ) : Pleroma + + data class Akkoma( + override val name: String, + override val version: String + ) : Pleroma + } data class Other( override val version: String, override val name: String @@ -65,6 +76,8 @@ data class NodeInfo( "fedibird" -> SoftwareType.Mastodon.Fedibird(version = software.version, name = software.name) "meisskey" -> SoftwareType.Misskey.Meisskey(version = software.version, name = software.name) "foundkey" -> SoftwareType.Misskey.Foundkey(version = software.version, name = software.name) + "pleroma" -> SoftwareType.Pleroma.Normal(version = software.version, name = software.name) + "akkoma" -> SoftwareType.Pleroma.Akkoma(version = software.version, name = software.name) else -> SoftwareType.Other(version = software.version, name = software.name) } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt index 82197ef2e9..8e87fdfdcd 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt @@ -103,6 +103,15 @@ class ToggleReactionUseCase @Inject constructor( reactionObj.getNameAndHost() } + is InstanceInfoType.Pleroma -> { + val maxCount = instanceType.maxReactionsPerAccount + if (maxCount < 1) { + return null + } + + // TODO: ユニコード絵文字の場合コロンは不要かもしれない + ":${reactionObj.getNameAndHost()}:" + } is InstanceInfoType.Misskey -> { val name = reactionObj.getName() ?: return null From c84cb13e758b131bcdacb5ffebf0ecf7f1872603 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 5 Apr 2023 11:41:39 +0900 Subject: [PATCH 004/432] fix: serializer error --- .../api/mastodon/status/TootPreviewCardDTO.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/TootPreviewCardDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/TootPreviewCardDTO.kt index ba272df154..687f650c4b 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/TootPreviewCardDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/TootPreviewCardDTO.kt @@ -17,29 +17,29 @@ data class TootPreviewCardDTO( val description: String, @SerialName("author_name") - val authorName: String, + val authorName: String? = null, @SerialName("author_url") - val authorUrl: String, + val authorUrl: String? = null, @SerialName("provider_url") val providerUrl: String, @SerialName("html") - val html: String, + val html: String? = null, @SerialName("width") - val width: Int, + val width: Int? = null, @SerialName("height") - val height: Int, + val height: Int? = null, @SerialName("image") val image: String?, @SerialName("embed_url") - val embedUrl: String?, + val embedUrl: String? = null, @SerialName("blurhash") - val blurhash: String? + val blurhash: String? = null, ) \ No newline at end of file From 24ad17415dcd002909cb008dd48c7f3a81d62c96 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 6 Apr 2023 08:48:41 +0900 Subject: [PATCH 005/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 1ad5e86f5d..749c31d168 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -565,6 +565,7 @@ 自動更新を有効にする ログインしてください ファイルが添付されたノートのみ表示する + Milkteaについて diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 7bd408d7b3..6f44ce118a 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -558,6 +558,7 @@ 启用自动更新 请登录 Only Media + 关于Milktea diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index cf1264bc93..74beee49d7 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -559,6 +559,8 @@ Enable automatic updates Please login Only Media + About Milktea + From 91afbed3e25ec5280f7a99065f848f35a193ac9b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 6 Apr 2023 08:49:23 +0900 Subject: [PATCH 006/432] =?UTF-8?q?feat:=20Activity=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/src/main/AndroidManifest.xml | 3 ++ .../activities/AboutMilkteaActivity.kt | 51 +++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt diff --git a/modules/features/setting/src/main/AndroidManifest.xml b/modules/features/setting/src/main/AndroidManifest.xml index 9d4055ff8f..41d3da9404 100644 --- a/modules/features/setting/src/main/AndroidManifest.xml +++ b/modules/features/setting/src/main/AndroidManifest.xml @@ -5,6 +5,9 @@ + diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt new file mode 100644 index 0000000000..da1e0a5eea --- /dev/null +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt @@ -0,0 +1,51 @@ +package net.pantasystem.milktea.setting.activities + +import android.os.Bundle +import androidx.activity.compose.setContent +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.material.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import dagger.hilt.android.AndroidEntryPoint +import net.pantasystem.milktea.common.ui.ApplyTheme +import net.pantasystem.milktea.common_resource.R +import javax.inject.Inject + +@AndroidEntryPoint +class AboutMilkteaActivity : AppCompatActivity() { + + @Inject + internal lateinit var applyTheme: ApplyTheme + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + applyTheme() + + setContent { + Scaffold( + topBar = { + TopAppBar( + navigationIcon = { + IconButton(onClick = { finish() }) { + Icon(Icons.Default.ArrowBack, contentDescription = "navigate up") + } + }, + title = { + Text(stringResource(id = R.string.settings_about_milktea)) + } + ) + } + ) { paddingValues -> + Column( + modifier = Modifier.padding(paddingValues) + ) { + + } + } + } + } +} \ No newline at end of file From 7641536949620dae938dda2a7d789fa93cb29651 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 6 Apr 2023 09:30:53 +0900 Subject: [PATCH 007/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_resource/src/main/res/values-ja/strings.xml | 7 +++++++ .../common_resource/src/main/res/values-zh/strings.xml | 8 ++++++++ modules/common_resource/src/main/res/values/strings.xml | 7 +++++++ 3 files changed, 22 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 749c31d168..439ad2519c 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -457,6 +457,12 @@ リノート非表示ユーザ ブックマーク + FediverseライフにMilkteaはいかが? + 寄付 + プライバシーポリシー + 利用規約 + + アプリケーション名 Milktea @@ -566,6 +572,7 @@ ログインしてください ファイルが添付されたノートのみ表示する Milkteaについて + ソースコード(GitHub) diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 6f44ce118a..2fd8e86681 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -454,6 +454,12 @@ 远程静音 书签 + Would you like Milktea with Fedivierse? + 捐款 + Privacy policy + Terms of service + + 应用名称 Milktea 成功创建应用 @@ -559,6 +565,8 @@ 请登录 Only Media 关于Milktea + Source code(GitHub) + diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 74beee49d7..40d0f1f92d 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -447,6 +447,12 @@ Renote mutes Bookmark + Would you like Milktea with Fediverse? + Donation + Privacy policy + Terms of service + + App name Milktea @@ -560,6 +566,7 @@ Please login Only Media About Milktea + Source code(GitHub) From eb1e0d654b6d8523ae0a65f2a5e11b2f531f5dbe Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 6 Apr 2023 09:31:07 +0900 Subject: [PATCH 008/432] =?UTF-8?q?feat:=20About=20Milktea=E3=81=AE?= =?UTF-8?q?=E7=94=BB=E9=9D=A2=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activities/AboutMilkteaActivity.kt | 121 +++++++++++++++--- 1 file changed, 104 insertions(+), 17 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt index da1e0a5eea..a86c5b50af 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt @@ -1,18 +1,28 @@ package net.pantasystem.milktea.setting.activities +import android.content.Intent +import android.net.Uri import android.os.Bundle import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.rememberAsyncImagePainter +import com.google.android.material.composethemeadapter.MdcTheme import dagger.hilt.android.AndroidEntryPoint import net.pantasystem.milktea.common.ui.ApplyTheme import net.pantasystem.milktea.common_resource.R +import net.pantasystem.milktea.setting.compose.SettingListTileLayout +import java.util.* import javax.inject.Inject @AndroidEntryPoint @@ -25,27 +35,104 @@ class AboutMilkteaActivity : AppCompatActivity() { super.onCreate(savedInstanceState) applyTheme() + val version = getSelfVersion() + val lang = Locale.getDefault().language setContent { - Scaffold( - topBar = { - TopAppBar( - navigationIcon = { - IconButton(onClick = { finish() }) { - Icon(Icons.Default.ArrowBack, contentDescription = "navigate up") + MdcTheme { + Scaffold( + topBar = { + TopAppBar( + navigationIcon = { + IconButton(onClick = { finish() }) { + Icon(Icons.Default.ArrowBack, contentDescription = "navigate up") + } + }, + title = { + Text(stringResource(id = R.string.settings_about_milktea)) } - }, - title = { - Text(stringResource(id = R.string.settings_about_milktea)) + ) + } + ) { paddingValues -> + Column( + modifier = Modifier.padding(paddingValues), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Spacer(modifier = Modifier.height(32.dp)) + Image( + painter = rememberAsyncImagePainter("https://raw.githubusercontent.com/pantasystem/Milktea/master/app/src/main/ic_launcher-web.png"), + contentDescription = "App icon", + modifier = Modifier.size(64.dp) + ) + Spacer(Modifier.height(12.dp)) + Text( + stringResource(id = R.string.app_name), + fontSize = 24.sp, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(12.dp)) + Text(version ?: "") + Spacer(modifier = Modifier.height(12.dp)) + Text(stringResource(id = R.string.milktea_catchphrase)) + Spacer(Modifier.height(16.dp)) + + SettingListTileLayout( + verticalPadding = 12.dp, + onClick = { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/pantasystem/Milktea"))) + } + ) { + Text(stringResource(id = R.string.settings_about_milktea_source_code)) + } + + SettingListTileLayout( + verticalPadding = 12.dp, + onClick = { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://www.patreon.com/pantasystem"))) + } + ) { + Text(stringResource(R.string.donation)) + } + + SettingListTileLayout( + verticalPadding = 12.dp, + onClick = { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/pantasystem/Milktea/blob/develop/privacy_policy_${ + when(lang) { + "zh", "jp", "en" -> lang + else -> "en" + } + }.md"))) + } + ) { + Text(stringResource(id = R.string.privacy_policy)) } - ) - } - ) { paddingValues -> - Column( - modifier = Modifier.padding(paddingValues) - ) { + SettingListTileLayout( + verticalPadding = 12.dp, + onClick = { + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://github.com/pantasystem/Milktea/blob/develop/terms_of_service_${ + when(lang) { + "zh", "jp", "en" -> lang + else -> "en" + } + }.md"))) + } + ) { + Text(stringResource(id = R.string.terms_of_service)) + } + } } } } } + + @Suppress("DEPRECATION") + private fun getSelfVersion(): String? { + val pm = packageManager + return try { + pm.getPackageInfo(packageName, 0).versionName + } catch (e: Exception) { + null + } + } } \ No newline at end of file From 04e409f657d017446538aa854e44541b3052be7e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 6 Apr 2023 09:31:17 +0900 Subject: [PATCH 009/432] =?UTF-8?q?feat:=20About=20Milktea=E3=81=B8?= =?UTF-8?q?=E3=81=AE=E3=83=AA=E3=83=B3=E3=82=AF=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/setting/activities/SettingsActivity.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingsActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingsActivity.kt index 18c1db41f0..b32ce0dbab 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingsActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingsActivity.kt @@ -168,6 +168,20 @@ class SettingsActivity : AppCompatActivity() { Text(stringResource(id = R.string.client_word_mute)) } + SettingListTileLayout( + verticalPadding = 12.dp, + onClick = { + startActivity( + Intent( + this@SettingsActivity, + AboutMilkteaActivity::class.java + ) + ) + } + ) { + Text(stringResource(id = R.string.settings_about_milktea)) + } + SettingListTileLayout( verticalPadding = 12.dp, onClick = { From bdc207bbd72f6c9cee049ff6ec364ba3cc110ff3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 6 Apr 2023 09:38:03 +0900 Subject: [PATCH 010/432] =?UTF-8?q?feat:=20=E3=82=B9=E3=82=AF=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E5=8F=AF=E8=83=BD=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/setting/activities/AboutMilkteaActivity.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt index a86c5b50af..bc14fe1c12 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/AboutMilkteaActivity.kt @@ -7,6 +7,8 @@ import androidx.activity.compose.setContent import androidx.appcompat.app.AppCompatActivity import androidx.compose.foundation.Image import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack @@ -54,7 +56,11 @@ class AboutMilkteaActivity : AppCompatActivity() { } ) { paddingValues -> Column( - modifier = Modifier.padding(paddingValues), + modifier = Modifier + .padding(paddingValues) + .verticalScroll( + rememberScrollState() + ), horizontalAlignment = Alignment.CenterHorizontally, ) { Spacer(modifier = Modifier.height(32.dp)) From 8e5695baa463ef785f9dedf43f3e14862e7d4e3e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 9 Apr 2023 21:56:20 +0900 Subject: [PATCH 011/432] =?UTF-8?q?feat:=20=E3=83=AD=E3=82=B0=E3=82=A2?= =?UTF-8?q?=E3=82=A6=E3=83=88=E6=99=82=E3=81=AB=E3=83=97=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A5=E9=80=9A=E7=9F=A5=E8=B3=BC=E8=AA=AD=E8=A7=A3=E9=99=A4?= =?UTF-8?q?=E3=82=92=E8=A1=8C=E3=81=A3=E3=81=9F=E6=99=82=E3=81=ABstatus=20?= =?UTF-8?q?code=20403,=20410,=20401=E3=81=8C=E8=BF=94=E3=81=A3=E3=81=A6?= =?UTF-8?q?=E3=81=8D=E3=81=9F=E6=99=82=E3=81=AF=E3=81=9D=E3=81=AE=E3=81=BE?= =?UTF-8?q?=E3=81=BE=E3=83=AD=E3=82=B0=E3=82=A2=E3=82=A6=E3=83=88=E3=82=92?= =?UTF-8?q?=E7=B6=9A=E8=A1=8C=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sw/register/SubscriptionUnRegistration.kt | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/sw/register/SubscriptionUnRegistration.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/sw/register/SubscriptionUnRegistration.kt index 2542b987bd..8104e50adc 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/sw/register/SubscriptionUnRegistration.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/sw/register/SubscriptionUnRegistration.kt @@ -8,6 +8,7 @@ import com.google.firebase.messaging.FirebaseMessaging import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import net.pantasystem.milktea.api.misskey.register.UnSubscription +import net.pantasystem.milktea.common.APIError import net.pantasystem.milktea.common.throwIfHasError import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.model.account.AccountRepository @@ -42,12 +43,24 @@ class SubscriptionUnRegistrationImpl @Inject constructor( endpointBase = endpointBase, auth = auth, ).build() - apiProvider.swUnRegister( - UnSubscription( - i = account.token, - endpoint = endpoint - ) - ).throwIfHasError() + try { + apiProvider.swUnRegister( + UnSubscription( + i = account.token, + endpoint = endpoint + ) + ).throwIfHasError() + } catch (e: APIError.ForbiddenException) { + return@withContext + } + catch (e: APIError.AuthenticationException) { + return@withContext + } catch (e: APIError.SomethingException) { + if (e.statusCode == 410) { + return@withContext + } + throw e + } } else -> { From f8d2243a52fe8b65db3598e52ead29153fe7ee40 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 12:02:50 +0900 Subject: [PATCH 012/432] =?UTF-8?q?feat:=20io=E3=81=A7=E4=BD=95=E6=95=85?= =?UTF-8?q?=E3=81=8B=E7=A2=BA=E7=8E=87=E3=81=A7=E8=AA=8D=E8=A8=BC=E9=96=A2?= =?UTF-8?q?=E9=80=A3=E3=81=AE=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=AB=E5=A4=B1=E6=95=97=E3=81=99=E3=82=8B=E3=81=AE=E3=81=A7?= =?UTF-8?q?=E3=80=81=20=E8=A4=87=E6=95=B0=E5=9B=9E=E3=83=AA=E3=82=AF?= =?UTF-8?q?=E3=82=A8=E3=82=B9=E3=83=88=E3=82=92=E3=83=AA=E3=83=88=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=80=81=E3=81=AA=E3=82=93=E3=81=A8=E3=81=8B=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/viewmodel/app/AuthStateHelper.kt | 33 ++++++++++++------- .../auth/viewmodel/app/GetAccessToken.kt | 29 +++++++++++----- 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AuthStateHelper.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AuthStateHelper.kt index 49b7cfe41e..fad1f1068e 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AuthStateHelper.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AuthStateHelper.kt @@ -2,16 +2,15 @@ package net.pantasystem.milktea.auth.viewmodel.app import android.util.Log import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import net.pantasystem.milktea.api.mastodon.apps.CreateApp import net.pantasystem.milktea.api.misskey.I import net.pantasystem.milktea.api.misskey.MisskeyAPIServiceBuilder -import net.pantasystem.milktea.api.misskey.auth.AppSecret -import net.pantasystem.milktea.api.misskey.auth.SignInRequest -import net.pantasystem.milktea.api.misskey.auth.fromDTO -import net.pantasystem.milktea.api.misskey.auth.fromPleromaDTO +import net.pantasystem.milktea.api.misskey.auth.* import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.auth.viewmodel.Permissions +import net.pantasystem.milktea.common.APIError import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common.throwIfHasError import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider @@ -80,13 +79,8 @@ class AuthStateHelper @Inject constructor( } is AppType.Misskey -> { val secret = app.secret - val authApi = misskeyAPIServiceBuilder.buildAuthAPI(instanceBase) - val session = authApi.generateSession( - AppSecret( - secret!! - ) - ).body() - ?: throw IllegalStateException("セッションの作成に失敗しました。") + val session = generateMisskeySession(instanceBase, secret) + customAuthStore.setCustomAuthBridge( app.createAuth(instanceBase, session) ) @@ -294,4 +288,21 @@ class AuthStateHelper @Inject constructor( fun checkUrlPattern(url: String): Boolean { return urlPattern.matcher(url).find() } + + private suspend fun generateMisskeySession(instanceBase: String, secret: String?, retryCount: Int = 0): Session { + val authApi = misskeyAPIServiceBuilder.buildAuthAPI(instanceBase) + try { + return authApi.generateSession( + AppSecret( + secret!! + ) + ).throwIfHasError().body()!! + } catch (e: APIError) { + if (retryCount < 100) { + delay(100) + return generateMisskeySession(instanceBase, secret, retryCount + 1) + } + throw e + } + } } \ No newline at end of file diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/GetAccessToken.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/GetAccessToken.kt index 13455efa73..c5a8a98fff 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/GetAccessToken.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/GetAccessToken.kt @@ -1,10 +1,12 @@ package net.pantasystem.milktea.auth.viewmodel.app import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.withContext import net.pantasystem.milktea.api.misskey.MisskeyAPIServiceBuilder import net.pantasystem.milktea.api.misskey.auth.UserKey import net.pantasystem.milktea.api.misskey.auth.createObtainToken +import net.pantasystem.milktea.common.APIError import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common.throwIfHasError @@ -33,15 +35,7 @@ class GetAccessToken @Inject constructor( withContext(Dispatchers.IO) { when (a) { is Authorization.Waiting4UserAuthorization.Misskey -> { - val accessToken = - misskeyAPIServiceBuilder.buildAuthAPI(a.instanceBaseURL).getAccessToken( - UserKey( - appSecret = a.appSecret, - a.session.token - ) - ) - .throwIfHasError().body() - ?: throw IllegalStateException("response bodyがありません。") + val accessToken = getMisskeyAccessToken(a) accessToken.toModel(a.appSecret) } is Authorization.Waiting4UserAuthorization.Mastodon -> { @@ -99,4 +93,21 @@ class GetAccessToken @Inject constructor( throw e } } + + private suspend fun getMisskeyAccessToken(a: Authorization.Waiting4UserAuthorization.Misskey, retryCount: Int = 0): net.pantasystem.milktea.api.misskey.auth.AccessToken { + return try { + misskeyAPIServiceBuilder.buildAuthAPI(a.instanceBaseURL).getAccessToken( + UserKey( + appSecret = a.appSecret, + a.session.token + ) + ).throwIfHasError().body()!! + } catch (e: APIError) { + if (retryCount > 25) { + throw e + } + delay(100) + getMisskeyAccessToken(a, retryCount + 1) + } + } } \ No newline at end of file From 7708ba414f635d64780e93bda2cd11bcd860803e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 13:21:36 +0900 Subject: [PATCH 013/432] =?UTF-8?q?feat:=20pleroma=E3=81=AE=E3=83=87?= =?UTF-8?q?=E3=83=BC=E3=82=BF=E3=81=AE=E8=BF=BD=E5=8A=A0=E3=83=87=E3=83=BC?= =?UTF-8?q?=E3=82=BF=E3=82=92=E3=83=87=E3=82=B3=E3=83=BC=E3=83=89=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api/mastodon/instance/Instance.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/instance/Instance.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/instance/Instance.kt index 989a330a78..b2af757932 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/instance/Instance.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/instance/Instance.kt @@ -28,6 +28,9 @@ data class Instance( @SerialName("fedibird_capabilities") val fedibirdCapabilities: List? = null, + + @SerialName("pleroma") + val pleroma: Pleroma? = null, ) { @Serializable data class Configuration( @@ -68,4 +71,15 @@ data class Instance( data class Urls( @SerialName("streaming_api") val streamingApi: String ) + + @Serializable + data class Pleroma( + @SerialName("metadata") val metadata: Metadata, + + ) { + @Serializable + data class Metadata( + @SerialName("features") val features: List, + ) + } } From 256e3e7c628b8828ad091a9fe9c61a78541d8eb7 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 13:43:34 +0900 Subject: [PATCH 014/432] =?UTF-8?q?feat:=20=E3=82=A4=E3=83=B3=E3=82=B9?= =?UTF-8?q?=E3=82=BF=E3=83=B3=E3=82=B9=E6=83=85=E5=A0=B1=E3=81=ABPleroma?= =?UTF-8?q?=E3=81=AE=E8=BF=BD=E5=8A=A0=E6=83=85=E5=A0=B1=E3=82=92=E8=A8=98?= =?UTF-8?q?=E9=8C=B2=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/DataBase.kt | 5 ++- .../infrastructure/TootEntityConverters.kt | 9 ++++ .../MastodonInstanceInfoRepositoryImpl.kt | 21 ++++++++++ .../instance/db/MastodonInstanceInfoDAO.kt | 8 ++++ .../instance/db/MastodonInstanceInfoRecord.kt | 42 ++++++++++++++++++- .../model/instance/MastodonInstanceInfo.kt | 12 +++++- 6 files changed, 93 insertions(+), 4 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt index 5b044769fe..8fa98bf259 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt @@ -114,8 +114,10 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut RenoteMuteRecord::class, FedibirdCapabilitiesRecord::class, + + PleromaMetadataFeatures::class, ], - version = 43, + version = 44, exportSchema = true, autoMigrations = [ AutoMigration(from = 11, to = 12), @@ -150,6 +152,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut AutoMigration(from = 40, to = 41), AutoMigration(from = 41, to = 42), AutoMigration(from = 42, to = 43), + AutoMigration(from = 43, to = 44), ], views = [UserView::class, GroupMemberView::class, UserListMemberView::class] ) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/TootEntityConverters.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/TootEntityConverters.kt index bc53de1b20..c9d1ef9aea 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/TootEntityConverters.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/TootEntityConverters.kt @@ -239,5 +239,14 @@ fun Instance.toModel(): MastodonInstanceInfo { ) }, fedibirdCapabilities = fedibirdCapabilities, + pleroma = pleroma?.let { pleroma -> + MastodonInstanceInfo.Pleroma( + metadata = pleroma.metadata.let { m -> + MastodonInstanceInfo.Pleroma.Metadata( + features = m.features + ) + } + ) + } ) } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MastodonInstanceInfoRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MastodonInstanceInfoRepositoryImpl.kt index 2179cccf36..6ebfb0e000 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MastodonInstanceInfoRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MastodonInstanceInfoRepositoryImpl.kt @@ -81,9 +81,20 @@ class MastodonInstanceInfoRepositoryImpl @Inject constructor( } ) } + instanceInfo.pleroma?.let { pleroma -> + mastodonInstanceInfoDAO.insertPleromaMetadataFeatures( + pleroma.metadata.features.map { + PleromaMetadataFeatures( + type = it, + uri = instanceInfo.uri, + ) + } + ) + } } else { mastodonInstanceInfoDAO.update(MastodonInstanceInfoRecord.from(instanceInfo)) mastodonInstanceInfoDAO.clearFedibirdCapabilities(instanceInfo.uri) + mastodonInstanceInfoDAO.clearPleromaMetadataFeatures(instanceInfo.uri) instanceInfo.fedibirdCapabilities?.let { capabilities -> mastodonInstanceInfoDAO.insertFedibirdCapabilities( capabilities.map { @@ -91,6 +102,16 @@ class MastodonInstanceInfoRepositoryImpl @Inject constructor( } ) } + instanceInfo.pleroma?.let { pleroma -> + mastodonInstanceInfoDAO.insertPleromaMetadataFeatures( + pleroma.metadata.features.map { + PleromaMetadataFeatures( + type = it, + uri = instanceInfo.uri, + ) + } + ) + } } } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/db/MastodonInstanceInfoDAO.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/db/MastodonInstanceInfoDAO.kt index aec1047fff..c4e7a75927 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/db/MastodonInstanceInfoDAO.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/db/MastodonInstanceInfoDAO.kt @@ -35,4 +35,12 @@ abstract class MastodonInstanceInfoDAO { @Insert(onConflict = OnConflictStrategy.IGNORE) abstract fun insertFedibirdCapabilities(list: List): List + @Query(""" + delete from pleroma_metadata_features where uri = :uri + """) + abstract fun clearPleromaMetadataFeatures(uri: String) + + @Insert(onConflict = OnConflictStrategy.IGNORE) + abstract fun insertPleromaMetadataFeatures(list: List): List + } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/db/MastodonInstanceInfoRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/db/MastodonInstanceInfoRecord.kt index e4dae56ffa..8f98a914ea 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/db/MastodonInstanceInfoRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/db/MastodonInstanceInfoRecord.kt @@ -90,6 +90,28 @@ data class FedibirdCapabilitiesRecord( val uri: String ) +@Entity( + tableName = "pleroma_metadata_features", + foreignKeys = [ + ForeignKey( + parentColumns = ["uri"], + childColumns = ["uri"], + entity = MastodonInstanceInfoRecord::class, + onDelete = ForeignKey.CASCADE, + onUpdate = ForeignKey.CASCADE, + ) + ], + indices = [Index("uri")], + primaryKeys = ["uri", "type"] +) +data class PleromaMetadataFeatures( + @ColumnInfo(name = "type") + val type: String, + + @ColumnInfo(name = "uri") + val uri: String +) + data class MastodonInstanceInfoRelated( @Embedded val info: MastodonInstanceInfoRecord, @Relation( @@ -97,7 +119,14 @@ data class MastodonInstanceInfoRelated( entityColumn = "uri", entity = FedibirdCapabilitiesRecord::class ) - val fedibirdCapabilities: List? + val fedibirdCapabilities: List?, + + @Relation( + parentColumn = "uri", + entityColumn = "uri", + entity = PleromaMetadataFeatures::class + ) + val pleromaMetadataFeatures: List? ) fun MastodonInstanceInfoRecord.Companion.from(model: MastodonInstanceInfo): MastodonInstanceInfoRecord { @@ -176,6 +205,15 @@ fun MastodonInstanceInfoRelated.toModel(): MastodonInstanceInfo { } ) }, - fedibirdCapabilities = fedibirdCapabilities?.map { it.type } + fedibirdCapabilities = fedibirdCapabilities?.map { it.type }, + pleroma = pleromaMetadataFeatures?.let { + MastodonInstanceInfo.Pleroma( + metadata = MastodonInstanceInfo.Pleroma.Metadata( + features = it.map { feature -> + feature.type + } + ) + ) + } ) } \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt index c3641909f7..e2db3b0fef 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt @@ -10,6 +10,7 @@ data class MastodonInstanceInfo( val urls: Urls, val configuration: Configuration?, val fedibirdCapabilities: List?, + val pleroma: Pleroma?, ) { companion object; @@ -45,5 +46,14 @@ data class MastodonInstanceInfo( // リアクションを使用可能か? val isReactionAvailable: Boolean - get() = fedibirdCapabilities?.contains("emoji_reaction") ?: false + get() = (fedibirdCapabilities?.contains("emoji_reaction") ?: false) + || (pleroma?.metadata?.features?.contains("pleroma_emoji_reactions") ?: false) + + data class Pleroma( + val metadata: Metadata, + ) { + data class Metadata( + val features: List + ) + } } From fccb95c2d417a5d1e4ac248280a16c4ced171275 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 13:43:43 +0900 Subject: [PATCH 015/432] =?UTF-8?q?feat:=20=E3=82=B9=E3=82=AD=E3=83=BC?= =?UTF-8?q?=E3=83=9E=E3=82=92=E7=94=9F=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../44.json | 3854 +++++++++++++++++ 1 file changed, 3854 insertions(+) create mode 100644 modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/44.json diff --git a/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/44.json b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/44.json new file mode 100644 index 0000000000..15326bb361 --- /dev/null +++ b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/44.json @@ -0,0 +1,3854 @@ +{ + "formatVersion": 1, + "database": { + "version": 44, + "identityHash": "8a4611b8efe95f3974bd13cef6b9205c", + "entities": [ + { + "tableName": "connection_information", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT NOT NULL, `instanceBaseUrl` TEXT NOT NULL, `encryptedI` TEXT NOT NULL, `viaName` TEXT, `createdAt` TEXT NOT NULL, `isDirect` INTEGER NOT NULL, `updatedAt` TEXT NOT NULL, PRIMARY KEY(`accountId`, `encryptedI`, `instanceBaseUrl`), FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceBaseUrl", + "columnName": "instanceBaseUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedI", + "columnName": "encryptedI", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "viaName", + "columnName": "viaName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirect", + "columnName": "isDirect", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "encryptedI", + "instanceBaseUrl" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "reaction_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "reaction_user_setting", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`reaction`, `instance_domain`))", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "reaction", + "instance_domain" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "page", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT, `title` TEXT NOT NULL, `pageNumber` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `global_timeline_with_files` INTEGER, `global_timeline_type` TEXT, `local_timeline_with_files` INTEGER, `local_timeline_exclude_nsfw` INTEGER, `local_timeline_type` TEXT, `hybrid_timeline_withFiles` INTEGER, `hybrid_timeline_includeLocalRenotes` INTEGER, `hybrid_timeline_includeMyRenotes` INTEGER, `hybrid_timeline_includeRenotedMyRenotes` INTEGER, `hybrid_timeline_type` TEXT, `home_timeline_withFiles` INTEGER, `home_timeline_includeLocalRenotes` INTEGER, `home_timeline_includeMyRenotes` INTEGER, `home_timeline_includeRenotedMyRenotes` INTEGER, `home_timeline_type` TEXT, `user_list_timeline_listId` TEXT, `user_list_timeline_withFiles` INTEGER, `user_list_timeline_includeLocalRenotes` INTEGER, `user_list_timeline_includeMyRenotes` INTEGER, `user_list_timeline_includeRenotedMyRenotes` INTEGER, `user_list_timeline_type` TEXT, `mention_following` INTEGER, `mention_visibility` TEXT, `mention_type` TEXT, `show_noteId` TEXT, `show_type` TEXT, `tag_tag` TEXT, `tag_reply` INTEGER, `tag_renote` INTEGER, `tag_withFiles` INTEGER, `tag_poll` INTEGER, `tag_type` TEXT, `featured_offset` INTEGER, `featured_type` TEXT, `notification_following` INTEGER, `notification_markAsRead` INTEGER, `notification_type` TEXT, `user_userId` TEXT, `user_includeReplies` INTEGER, `user_includeMyRenotes` INTEGER, `user_withFiles` INTEGER, `user_type` TEXT, `search_query` TEXT, `search_host` TEXT, `search_userId` TEXT, `search_type` TEXT, `favorite_type` TEXT, `antenna_antennaId` TEXT, `antenna_type` TEXT, FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageNumber", + "columnName": "pageNumber", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.withFiles", + "columnName": "global_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.type", + "columnName": "global_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localTimeline.withFiles", + "columnName": "local_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.excludeNsfw", + "columnName": "local_timeline_exclude_nsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.type", + "columnName": "local_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.withFiles", + "columnName": "hybrid_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeLocalRenotes", + "columnName": "hybrid_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeMyRenotes", + "columnName": "hybrid_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeRenotedMyRenotes", + "columnName": "hybrid_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.type", + "columnName": "hybrid_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "homeTimeline.withFiles", + "columnName": "home_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeLocalRenotes", + "columnName": "home_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeMyRenotes", + "columnName": "home_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeRenotedMyRenotes", + "columnName": "home_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.type", + "columnName": "home_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.listId", + "columnName": "user_list_timeline_listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.withFiles", + "columnName": "user_list_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeLocalRenotes", + "columnName": "user_list_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeMyRenotes", + "columnName": "user_list_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeRenotedMyRenotes", + "columnName": "user_list_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.type", + "columnName": "user_list_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.following", + "columnName": "mention_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mention.visibility", + "columnName": "mention_visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.type", + "columnName": "mention_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.noteId", + "columnName": "show_noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.type", + "columnName": "show_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.tag", + "columnName": "tag_tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.reply", + "columnName": "tag_reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.renote", + "columnName": "tag_renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.withFiles", + "columnName": "tag_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.poll", + "columnName": "tag_poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.type", + "columnName": "tag_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "featured.offset", + "columnName": "featured_offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "featured.type", + "columnName": "featured_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notification.following", + "columnName": "notification_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.markAsRead", + "columnName": "notification_markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.type", + "columnName": "notification_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.userId", + "columnName": "user_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeReplies", + "columnName": "user_includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeMyRenotes", + "columnName": "user_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.withFiles", + "columnName": "user_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.type", + "columnName": "user_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.query", + "columnName": "search_query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.host", + "columnName": "search_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.userId", + "columnName": "search_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.type", + "columnName": "search_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "favorite.type", + "columnName": "favorite_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.antennaId", + "columnName": "antenna_antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.type", + "columnName": "antenna_type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_page_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "poll_choice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`choice` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`choice`, `weight`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "choice", + "columnName": "choice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "choice", + "weight", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_poll_choice_table_draft_note_id_choice", + "unique": false, + "columnNames": [ + "draft_note_id", + "choice" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_poll_choice_table_draft_note_id_choice` ON `${TABLE_NAME}` (`draft_note_id`, `choice`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "user_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, PRIMARY KEY(`userId`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_user_id_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_id_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_file_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL DEFAULT 'name none', `remote_file_id` TEXT, `file_path` TEXT, `is_sensitive` INTEGER, `type` TEXT, `thumbnailUrl` TEXT, `draft_note_id` INTEGER NOT NULL, `folder_id` TEXT, `file_id` INTEGER PRIMARY KEY AUTOINCREMENT, FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'name none'" + }, + { + "fieldPath": "remoteFileId", + "columnName": "remote_file_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileId", + "columnName": "file_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "file_id" + ] + }, + "indices": [ + { + "name": "index_draft_file_table_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_table_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_note_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `visibility` TEXT NOT NULL, `text` TEXT, `cw` TEXT, `viaMobile` INTEGER, `localOnly` INTEGER, `noExtractMentions` INTEGER, `noExtractHashtags` INTEGER, `noExtractEmojis` INTEGER, `replyId` TEXT, `renoteId` TEXT, `channelId` TEXT, `scheduleWillPostAt` TEXT, `draft_note_id` INTEGER PRIMARY KEY AUTOINCREMENT, `isSensitive` INTEGER, `multiple` INTEGER, `expiresAt` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cw", + "columnName": "cw", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "viaMobile", + "columnName": "viaMobile", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localOnly", + "columnName": "localOnly", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractMentions", + "columnName": "noExtractMentions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractHashtags", + "columnName": "noExtractHashtags", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractEmojis", + "columnName": "noExtractEmojis", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "replyId", + "columnName": "replyId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "renoteId", + "columnName": "renoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "scheduleWillPostAt", + "columnName": "scheduleWillPostAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.multiple", + "columnName": "multiple", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.expiresAt", + "columnName": "expiresAt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_draft_note_table_accountId_text", + "unique": false, + "columnNames": [ + "accountId", + "text" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_note_table_accountId_text` ON `${TABLE_NAME}` (`accountId`, `text`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "url_preview", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `icon` TEXT, `description` TEXT, `thumbnail` TEXT, `siteName` TEXT, PRIMARY KEY(`url`))", + "fields": [ + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnail", + "columnName": "thumbnail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "siteName", + "columnName": "siteName", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "url" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "account_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remoteId` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `userName` TEXT NOT NULL, `encryptedToken` TEXT NOT NULL, `instanceType` TEXT NOT NULL DEFAULT 'misskey', `accountId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedToken", + "columnName": "encryptedToken", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceType", + "columnName": "instanceType", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'misskey'" + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "accountId" + ] + }, + "indices": [ + { + "name": "index_account_table_remoteId", + "unique": false, + "columnNames": [ + "remoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_remoteId` ON `${TABLE_NAME}` (`remoteId`)" + }, + { + "name": "index_account_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_account_table_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_userName` ON `${TABLE_NAME}` (`userName`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "page_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `title` TEXT NOT NULL, `weight` INTEGER NOT NULL, `pageId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `withFiles` INTEGER, `excludeNsfw` INTEGER, `includeLocalRenotes` INTEGER, `includeMyRenotes` INTEGER, `includeRenotedMyRenotes` INTEGER, `listId` TEXT, `following` INTEGER, `visibility` TEXT, `noteId` TEXT, `tag` TEXT, `reply` INTEGER, `renote` INTEGER, `poll` INTEGER, `offset` INTEGER, `markAsRead` INTEGER, `userId` TEXT, `includeReplies` INTEGER, `query` TEXT, `host` TEXT, `antennaId` TEXT, `channelId` TEXT, `clipId` TEXT)", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageParams.type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageParams.withFiles", + "columnName": "withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.excludeNsfw", + "columnName": "excludeNsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeLocalRenotes", + "columnName": "includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeMyRenotes", + "columnName": "includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeRenotedMyRenotes", + "columnName": "includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.listId", + "columnName": "listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.following", + "columnName": "following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.tag", + "columnName": "tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.reply", + "columnName": "reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.renote", + "columnName": "renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.poll", + "columnName": "poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.offset", + "columnName": "offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.markAsRead", + "columnName": "markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.includeReplies", + "columnName": "includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.query", + "columnName": "query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.antennaId", + "columnName": "antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.clipId", + "columnName": "clipId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "pageId" + ] + }, + "indices": [ + { + "name": "index_page_table_weight", + "unique": false, + "columnNames": [ + "weight" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_weight` ON `${TABLE_NAME}` (`weight`)" + }, + { + "name": "index_page_table_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "meta_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `bannerUrl` TEXT, `cacheRemoteFiles` INTEGER, `description` TEXT, `disableGlobalTimeline` INTEGER, `disableLocalTimeline` INTEGER, `disableRegistration` INTEGER, `driveCapacityPerLocalUserMb` INTEGER, `driveCapacityPerRemoteUserMb` INTEGER, `enableDiscordIntegration` INTEGER, `enableEmail` INTEGER, `enableEmojiReaction` INTEGER, `enableGithubIntegration` INTEGER, `enableRecaptcha` INTEGER, `enableServiceWorker` INTEGER, `enableTwitterIntegration` INTEGER, `errorImageUrl` TEXT, `feedbackUrl` TEXT, `iconUrl` TEXT, `maintainerEmail` TEXT, `maintainerName` TEXT, `mascotImageUrl` TEXT, `maxNoteTextLength` INTEGER, `name` TEXT, `recaptchaSiteKey` TEXT, `secure` INTEGER, `swPublicKey` TEXT, `toSUrl` TEXT, `version` TEXT NOT NULL, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cacheRemoteFiles", + "columnName": "cacheRemoteFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "disableGlobalTimeline", + "columnName": "disableGlobalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableLocalTimeline", + "columnName": "disableLocalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableRegistration", + "columnName": "disableRegistration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerLocalUserMb", + "columnName": "driveCapacityPerLocalUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerRemoteUserMb", + "columnName": "driveCapacityPerRemoteUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableDiscordIntegration", + "columnName": "enableDiscordIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmail", + "columnName": "enableEmail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmojiReaction", + "columnName": "enableEmojiReaction", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableGithubIntegration", + "columnName": "enableGithubIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableRecaptcha", + "columnName": "enableRecaptcha", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableServiceWorker", + "columnName": "enableServiceWorker", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableTwitterIntegration", + "columnName": "enableTwitterIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "errorImageUrl", + "columnName": "errorImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "feedbackUrl", + "columnName": "feedbackUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerEmail", + "columnName": "maintainerEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerName", + "columnName": "maintainerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mascotImageUrl", + "columnName": "mascotImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maxNoteTextLength", + "columnName": "maxNoteTextLength", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recaptchaSiteKey", + "columnName": "recaptchaSiteKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secure", + "columnName": "secure", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "swPublicKey", + "columnName": "swPublicKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toSUrl", + "columnName": "toSUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "emoji_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `host` TEXT, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` TEXT, PRIMARY KEY(`name`, `instanceDomain`), FOREIGN KEY(`instanceDomain`) REFERENCES `meta_table`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_emoji_table_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [ + { + "table": "meta_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "instanceDomain" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "emoji_alias_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`alias` TEXT NOT NULL, `name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, PRIMARY KEY(`alias`, `name`, `instanceDomain`), FOREIGN KEY(`name`, `instanceDomain`) REFERENCES `emoji_table`(`name`, `instanceDomain`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "alias", + "columnName": "alias", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "alias", + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_alias_table_name_instanceDomain", + "unique": false, + "columnNames": [ + "name", + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_alias_table_name_instanceDomain` ON `${TABLE_NAME}` (`name`, `instanceDomain`)" + } + ], + "foreignKeys": [ + { + "table": "emoji_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "name", + "instanceDomain" + ], + "referencedColumns": [ + "name", + "instanceDomain" + ] + } + ] + }, + { + "tableName": "unread_notifications_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, PRIMARY KEY(`accountId`, `notificationId`), FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "nicknames", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`nickname` TEXT NOT NULL, `username` TEXT NOT NULL, `host` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "nickname", + "columnName": "nickname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_nicknames_username_host", + "unique": true, + "columnNames": [ + "username", + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_nicknames_username_host` ON `${TABLE_NAME}` (`username`, `host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "utf8_emojis_by_amio", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`codes` TEXT NOT NULL, `name` TEXT NOT NULL, `char` TEXT NOT NULL, PRIMARY KEY(`codes`))", + "fields": [ + { + "fieldPath": "codes", + "columnName": "codes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "charCode", + "columnName": "char", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "codes" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "drive_file_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `relatedAccountId` INTEGER NOT NULL, `createdAt` TEXT, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `md5` TEXT, `size` INTEGER, `url` TEXT NOT NULL, `isSensitive` INTEGER NOT NULL, `thumbnailUrl` TEXT, `folderId` TEXT, `userId` TEXT, `comment` TEXT, `blurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`relatedAccountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "relatedAccountId", + "columnName": "relatedAccountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "md5", + "columnName": "md5", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folderId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "blurhash", + "columnName": "blurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_drive_file_v1_serverId_relatedAccountId", + "unique": true, + "columnNames": [ + "serverId", + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_drive_file_v1_serverId_relatedAccountId` ON `${TABLE_NAME}` (`serverId`, `relatedAccountId`)" + }, + { + "name": "index_drive_file_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_drive_file_v1_relatedAccountId", + "unique": false, + "columnNames": [ + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_relatedAccountId` ON `${TABLE_NAME}` (`relatedAccountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "relatedAccountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "draft_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`draftNoteId` INTEGER NOT NULL, `filePropertyId` INTEGER, `localFileId` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`filePropertyId`) REFERENCES `drive_file_v1`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`localFileId`) REFERENCES `draft_local_file_v2_table`(`localFileId`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "draftNoteId", + "columnName": "draftNoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filePropertyId", + "columnName": "filePropertyId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId", + "unique": true, + "columnNames": [ + "draftNoteId", + "filePropertyId", + "localFileId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId` ON `${TABLE_NAME}` (`draftNoteId`, `filePropertyId`, `localFileId`)" + }, + { + "name": "index_draft_file_v2_table_draftNoteId", + "unique": false, + "columnNames": [ + "draftNoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId` ON `${TABLE_NAME}` (`draftNoteId`)" + }, + { + "name": "index_draft_file_v2_table_localFileId", + "unique": false, + "columnNames": [ + "localFileId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_localFileId` ON `${TABLE_NAME}` (`localFileId`)" + }, + { + "name": "index_draft_file_v2_table_filePropertyId", + "unique": false, + "columnNames": [ + "filePropertyId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_filePropertyId` ON `${TABLE_NAME}` (`filePropertyId`)" + } + ], + "foreignKeys": [ + { + "table": "drive_file_v1", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "filePropertyId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "draft_local_file_v2_table", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "localFileId" + ], + "referencedColumns": [ + "localFileId" + ] + } + ] + }, + { + "tableName": "draft_local_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `file_path` TEXT NOT NULL, `is_sensitive` INTEGER, `type` TEXT NOT NULL, `thumbnailUrl` TEXT, `folder_id` TEXT, `file_size` INTEGER, `comment` TEXT, `localFileId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "localFileId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "group_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `ownerId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "ownerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_v1_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_group_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_group_v1_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_v1_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "group_member_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`groupId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`groupId`) REFERENCES `group_v1`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_member_v1_groupId", + "unique": false, + "columnNames": [ + "groupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_member_v1_groupId` ON `${TABLE_NAME}` (`groupId`)" + }, + { + "name": "index_group_member_v1_groupId_userId", + "unique": true, + "columnNames": [ + "groupId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_member_v1_groupId_userId` ON `${TABLE_NAME}` (`groupId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "group_v1", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "groupId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `userName` TEXT NOT NULL, `name` TEXT, `avatarUrl` TEXT, `isCat` INTEGER, `isBot` INTEGER, `host` TEXT NOT NULL, `isSameHost` INTEGER NOT NULL, `avatarBlurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatarUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isCat", + "columnName": "isCat", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isBot", + "columnName": "isBot", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSameHost", + "columnName": "isSameHost", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "avatarBlurhash", + "columnName": "avatarBlurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_serverId_accountId", + "unique": true, + "columnNames": [ + "serverId", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_serverId_accountId` ON `${TABLE_NAME}` (`serverId`, `accountId`)" + }, + { + "name": "index_user_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_userName` ON `${TABLE_NAME}` (`userName`)" + }, + { + "name": "index_user_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_host", + "unique": false, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_detailed_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_detailed_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_detailed_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_emoji", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_emoji_name_userId", + "unique": true, + "columnNames": [ + "name", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_emoji_name_userId` ON `${TABLE_NAME}` (`name`, `userId`)" + }, + { + "name": "index_user_emoji_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_emoji_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "pinned_note_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_pinned_note_id_noteId_userId", + "unique": true, + "columnNames": [ + "noteId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_pinned_note_id_noteId_userId` ON `${TABLE_NAME}` (`noteId`, `userId`)" + }, + { + "name": "index_pinned_note_id_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pinned_note_id_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`faviconUrl` TEXT, `iconUrl` TEXT, `name` TEXT, `softwareName` TEXT, `softwareVersion` TEXT, `themeColor` TEXT, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "faviconUrl", + "columnName": "faviconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareName", + "columnName": "softwareName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareVersion", + "columnName": "softwareVersion", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_instance_info_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_instance_info_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_profile_field", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `value` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_profile_field_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_profile_field_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "word_filter_regex_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pattern` TEXT NOT NULL, `parentId` INTEGER NOT NULL, PRIMARY KEY(`parentId`), FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "pattern", + "columnName": "pattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentId" + ] + }, + "indices": [ + { + "name": "index_word_filter_regex_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_regex_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_word_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`word` TEXT NOT NULL, `parentId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "word", + "columnName": "word", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_word_filter_word_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_word_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_list", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_list_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_user_list_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_list_member", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userListId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userListId`) REFERENCES `user_list`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userListId", + "columnName": "userListId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_member_userListId", + "unique": false, + "columnNames": [ + "userListId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_member_userListId` ON `${TABLE_NAME}` (`userListId`)" + }, + { + "name": "index_user_list_member_userListId_userId", + "unique": true, + "columnNames": [ + "userListId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_member_userListId_userId` ON `${TABLE_NAME}` (`userListId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "user_list", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userListId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "instance_info_v1_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `host` TEXT NOT NULL, `name` TEXT, `description` TEXT, `clientMaxBodyByteSize` INTEGER, `iconUrl` TEXT, `themeColor` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "clientMaxBodyByteSize", + "columnName": "clientMaxBodyByteSize", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_instance_info_v1_table_host", + "unique": true, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_instance_info_v1_table_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "search_histories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `keyword` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keyword", + "columnName": "keyword", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_search_histories_keyword_accountId", + "unique": true, + "columnNames": [ + "keyword", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_search_histories_keyword_accountId` ON `${TABLE_NAME}` (`keyword`, `accountId`)" + }, + { + "name": "index_search_histories_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_search_histories_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_info_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_info_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_info_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_related_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_related_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_related_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nodeinfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `nodeInfoVersion` TEXT NOT NULL, `name` TEXT NOT NULL, `version` TEXT NOT NULL, PRIMARY KEY(`host`))", + "fields": [ + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nodeInfoVersion", + "columnName": "nodeInfoVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "host" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `title` TEXT NOT NULL, `description` TEXT NOT NULL, `email` TEXT NOT NULL, `version` TEXT NOT NULL, `urls_streamingApi` TEXT, `configuration_statuses_maxCharacters` INTEGER, `configuration_statuses_maxMediaAttachments` INTEGER, `configuration_polls_maxOptions` INTEGER, `configuration_polls_maxCharactersPerOption` INTEGER, `configuration_polls_minExpiration` INTEGER, `configuration_polls_maxExpiration` INTEGER, `configuration_emoji_reactions_myReactions` INTEGER, `configuration_emoji_reactions_maxReactionsPerAccount` INTEGER, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "urls.streamingApi", + "columnName": "urls_streamingApi", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxCharacters", + "columnName": "configuration_statuses_maxCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxMediaAttachments", + "columnName": "configuration_statuses_maxMediaAttachments", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxOptions", + "columnName": "configuration_polls_maxOptions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxCharactersPerOption", + "columnName": "configuration_polls_maxCharactersPerOption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.minExpiration", + "columnName": "configuration_polls_minExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxExpiration", + "columnName": "configuration_polls_maxExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactions", + "columnName": "configuration_emoji_reactions_myReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactionsPerAccount", + "columnName": "configuration_emoji_reactions_maxReactionsPerAccount", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "custom_emojis", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT, `name` TEXT NOT NULL, `emojiHost` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "emojiHost", + "columnName": "emojiHost", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_custom_emojis_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_custom_emojis_emojiHost", + "unique": false, + "columnNames": [ + "emojiHost" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost` ON `${TABLE_NAME}` (`emojiHost`)" + }, + { + "name": "index_custom_emojis_category", + "unique": false, + "columnNames": [ + "category" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_category` ON `${TABLE_NAME}` (`category`)" + }, + { + "name": "index_custom_emojis_emojiHost_name", + "unique": true, + "columnNames": [ + "emojiHost", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost_name` ON `${TABLE_NAME}` (`emojiHost`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "custom_emoji_aliases", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`emojiId` INTEGER NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`emojiId`, `value`), FOREIGN KEY(`emojiId`) REFERENCES `custom_emojis`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "emojiId", + "columnName": "emojiId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "emojiId", + "value" + ] + }, + "indices": [ + { + "name": "index_custom_emoji_aliases_emojiId_value", + "unique": false, + "columnNames": [ + "emojiId", + "value" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emoji_aliases_emojiId_value` ON `${TABLE_NAME}` (`emojiId`, `value`)" + } + ], + "foreignKeys": [ + { + "table": "custom_emojis", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "emojiId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "notification_json_cache_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, `json` TEXT NOT NULL, `key` TEXT, `weight` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `notificationId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "json", + "columnName": "json", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [ + { + "name": "index_notification_json_cache_v1_key", + "unique": false, + "columnNames": [ + "key" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notification_json_cache_v1_key` ON `${TABLE_NAME}` (`key`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_word_filters_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `filterId` TEXT NOT NULL, `phrase` TEXT NOT NULL, `wholeWord` INTEGER NOT NULL, `expiresAt` TEXT, `irreversible` INTEGER NOT NULL, `isContextHome` INTEGER NOT NULL, `isContextNotifications` INTEGER NOT NULL, `isContextPublic` INTEGER NOT NULL, `isContextThread` INTEGER NOT NULL, `isContextAccount` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `filterId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filterId", + "columnName": "filterId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phrase", + "columnName": "phrase", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "wholeWord", + "columnName": "wholeWord", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expiresAt", + "columnName": "expiresAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "irreversible", + "columnName": "irreversible", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextHome", + "columnName": "isContextHome", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextNotifications", + "columnName": "isContextNotifications", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextPublic", + "columnName": "isContextPublic", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextThread", + "columnName": "isContextThread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextAccount", + "columnName": "isContextAccount", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "filterId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "renote_mute_users", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `postedAt` TEXT, PRIMARY KEY(`userId`, `accountId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "postedAt", + "columnName": "postedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "accountId" + ] + }, + "indices": [ + { + "name": "index_renote_mute_users_postedAt", + "unique": false, + "columnNames": [ + "postedAt" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_postedAt` ON `${TABLE_NAME}` (`postedAt`)" + }, + { + "name": "index_renote_mute_users_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_fedibird_capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_mastodon_instance_fedibird_capabilities_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mastodon_instance_fedibird_capabilities_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "pleroma_metadata_features", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_pleroma_metadata_features_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pleroma_metadata_features_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + } + ], + "views": [ + { + "viewName": "user_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select user.*, nicknames.nickname from user left join nicknames on user.userName = nicknames.username and user.host = nicknames.host" + }, + { + "viewName": "group_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.groupId, u.id as userId, u.avatarUrl, u.serverId from group_member_v1 as m \n inner join group_v1 as g\n inner join user as u\n on m.groupId = g.id\n and m.userId = u.serverId\n and g.accountId = u.accountId" + }, + { + "viewName": "user_list_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.userListId, u.id as userId, u.avatarUrl, u.serverId from user_list_member as m \n inner join user_list as ul\n inner join user as u\n on m.userListId = ul.id\n and m.userId = u.serverId\n and ul.accountId = u.accountId" + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '8a4611b8efe95f3974bd13cef6b9205c')" + ] + } +} \ No newline at end of file From df70ca92f958e737c8b8dc6947bfa90596380171 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 13:59:01 +0900 Subject: [PATCH 016/432] =?UTF-8?q?feat:=20Pleroma=E3=81=A7=E3=82=82?= =?UTF-8?q?=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=EF=BC=88=E5=AF=BE?= =?UTF-8?q?=E5=BF=9C=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=82=B9=E3=82=BF=E3=83=B3=E3=82=B9=E3=81=AE=E3=81=BF=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/converters/TootDTOEntityConverter.kt | 2 +- .../milktea/model/instance/MastodonInstanceInfo.kt | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/TootDTOEntityConverter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/TootDTOEntityConverter.kt index f5a81b308c..03cf2bffcc 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/TootDTOEntityConverter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/TootDTOEntityConverter.kt @@ -79,7 +79,7 @@ class TootDTOEntityConverter @Inject constructor( FileProperty.Id(account.accountId, it.id) }, poll = poll.toPoll(), - maxReactionsPerAccount = instanceInfoResult.getOrNull()?.configuration?.emojiReactions?.maxReactionsPerAccount ?: 0, + maxReactionsPerAccount = instanceInfoResult.getOrNull()?.maxReactionsPerAccount ?: 0, type = Note.Type.Mastodon( favorited = favourited, reblogged = reblogged, diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt index e2db3b0fef..f1fc9e8748 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt @@ -49,6 +49,11 @@ data class MastodonInstanceInfo( get() = (fedibirdCapabilities?.contains("emoji_reaction") ?: false) || (pleroma?.metadata?.features?.contains("pleroma_emoji_reactions") ?: false) + val maxReactionsPerAccount: Int + get() = configuration?.emojiReactions?.maxReactionsPerAccount + ?: Int.MAX_VALUE.takeIf { + pleroma?.metadata?.features?.contains("pleroma_emoji_reactions") == true + } ?: 0 data class Pleroma( val metadata: Metadata, ) { From efe9e4dc21dcc3acf4c065b3f28474e0e0d4a897 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 14:04:04 +0900 Subject: [PATCH 017/432] =?UTF-8?q?fix:=20Pleroma=E3=81=AE=E3=82=AB?= =?UTF-8?q?=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92?= =?UTF-8?q?=E5=8F=96=E5=BE=97=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt index 2958b89e8a..98e2f69977 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt @@ -33,7 +33,7 @@ internal class CustomEmojiApiAdapterImpl @Inject constructor( } } is NodeInfo.SoftwareType.Pleroma -> { - val emojis = mastodonAPIProvider.get("https://${nodeInfo.host}").getCustomEmojis() + val emojis = mastodonAPIProvider.get(nodeInfo.host).getCustomEmojis() .throwIfHasError() .body() emojis?.map { From 015a7536188c1b57f858877848cf1631e289969e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 14:32:31 +0900 Subject: [PATCH 018/432] fix --- .../pantasystem/milktea/note/view/NoteCardActionHandler.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteCardActionHandler.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteCardActionHandler.kt index a86ed6f5e3..b8ee79923c 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteCardActionHandler.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteCardActionHandler.kt @@ -52,7 +52,10 @@ class NoteCardActionHandler( ) } is NoteCardAction.OnReactionButtonClicked -> { - if (action.note.currentNote.value.isReacted) { + if ( + action.note.currentNote.value.isReacted + && !action.note.currentNote.value.canReaction + ) { notesViewModel.deleteReactions(action.note.toShowNote.note.id) return } From beddf19328084e86950f104c0588876b2c776786 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 14:57:41 +0900 Subject: [PATCH 019/432] =?UTF-8?q?feat:=20=E3=82=AB=E3=83=86=E3=82=B4?= =?UTF-8?q?=E3=83=AA=E3=83=BC=E3=81=AB=E3=81=8B=E3=82=89=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=81=8C=E5=85=A5=E3=82=8B=E3=82=B1=E3=83=BC=E3=82=B9=E3=81=8C?= =?UTF-8?q?=E3=81=82=E3=81=A3=E3=81=9F=E3=81=AE=E3=81=A7=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/note/EmojiPickerUiState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt index 78ddd6fd6e..98eede617c 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt @@ -149,7 +149,7 @@ data class EmojiPickerUiState( } private val otherEmojis = customEmojis.filter { - it.category == null + it.category.isNullOrBlank() }.map { EmojiType.CustomEmoji(it) } From 25227d199f22d088a2c8e07ecb97ec5cf741a84f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 10 Apr 2023 15:21:22 +0900 Subject: [PATCH 020/432] fix: test --- .../model/notes/reaction/ToggleReactionUseCaseTest.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt b/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt index 943591af1d..d6c7b1043f 100644 --- a/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt +++ b/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt @@ -565,7 +565,8 @@ class ToggleReactionUseCaseTest { polls = null, statuses = null, ), - fedibirdCapabilities = listOf("emoji_reaction") + fedibirdCapabilities = listOf("emoji_reaction"), + pleroma = null, ) ) ) @@ -671,7 +672,8 @@ class ToggleReactionUseCaseTest { polls = null, statuses = null, ), - fedibirdCapabilities = listOf("emoji_reaction") + fedibirdCapabilities = listOf("emoji_reaction"), + pleroma = null, ) ) ) From 808e5e803052d580e5631a93ef122712e10e6c22 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 11 Apr 2023 11:17:33 +0900 Subject: [PATCH 021/432] =?UTF-8?q?feat:=20=E6=9D=A1=E4=BB=B6=E5=BC=8F?= =?UTF-8?q?=E3=81=8C=E9=80=86=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=9F=E3=81=AE=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/features/note/src/main/res/layout/item_detail_note.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/note/src/main/res/layout/item_detail_note.xml b/modules/features/note/src/main/res/layout/item_detail_note.xml index 0c99144e37..5dc137debc 100644 --- a/modules/features/note/src/main/res/layout/item_detail_note.xml +++ b/modules/features/note/src/main/res/layout/item_detail_note.xml @@ -476,7 +476,7 @@ android:onClick="@{ ()-> noteCardActionListener.onReactionButtonClicked(note) }" android:padding="@dimen/note_bottom_action_icon_padding_size" android:scaleType="centerCrop" - app:isReacted="@{note.currentNote.canReaction}" + app:isReacted="@{!note.currentNote.canReaction}" isAcceptingOnlyFavoriteReaction="@{note.currentNote.acceptingOnlyLikeReaction}" tools:srcCompat="@drawable/ic_add_circle_outline_black_24dp" android:visibility="@{note.currentNote.supportEmojiReaction ? View.VISIBLE : View.GONE }" From dae079a41552de826a89cbf383d8542155ebea56 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 12 Apr 2023 12:57:08 +0900 Subject: [PATCH 022/432] =?UTF-8?q?feat:=20=E3=82=A2=E3=83=B3=E3=83=86?= =?UTF-8?q?=E3=83=8A=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=81=AE=E5=8F=96=E5=BE=97=E3=82=AF=E3=82=A8=E3=83=AA=E7=A0=B4?= =?UTF-8?q?=E5=A3=8A=E7=9A=84=E5=A4=89=E6=9B=B4=E3=81=AE=E3=81=9F=E3=82=81?= =?UTF-8?q?=20=E9=87=8D=E8=A4=87=E3=81=97=E3=81=A6=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=A6?= =?UTF-8?q?=E3=81=97=E3=81=BE=E3=81=86=E4=B8=8D=E5=85=B7=E5=90=88=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/notes/TimelinePagingStoreImpl.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelinePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelinePagingStoreImpl.kt index f476bc22cb..699b54929b 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelinePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelinePagingStoreImpl.kt @@ -21,6 +21,7 @@ import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.account.page.SincePaginate import net.pantasystem.milktea.model.account.page.UntilPaginate import net.pantasystem.milktea.model.instance.MetaRepository +import net.pantasystem.milktea.model.instance.Version import net.pantasystem.milktea.model.notes.Note import retrofit2.Response @@ -84,6 +85,17 @@ internal class TimelinePagingStoreImpl( return@runCancellableCatching emptyList() } + // NOTE: sinceIdが13.11.0で削除される破壊的変更が行われてしまったのでその判定を行なっている + // https://github.com/misskey-dev/misskey/commit/b53d6c7f8ca1a712eab44967e8d05a0cc7bcc034#diff-883a3f5d77794cf2344c96727836aabc74c19f57db5e2e0cd485fdb6d3af7efeL77 + // **** YOU MISSKEY + if (pageableTimeline is Pageable.Antenna) { + val account = getAccount() + val meta = metaRepository.find(account.normalizedInstanceUri).getOrThrow() + if (meta.getVersion() >= Version("13.11.0")) { + return@runCancellableCatching emptyList() + } + } + val builder = NoteRequest.Builder( i = getAccount.invoke().token, pageable = pageableTimeline, From dd64d1087c5f9c6ec57d9dc7e10d6c89254813cd Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 17 Apr 2023 12:40:12 +0900 Subject: [PATCH 023/432] =?UTF-8?q?feat:=20=E3=82=A2=E3=83=B3=E3=83=86?= =?UTF-8?q?=E3=83=8A=E3=83=9A=E3=83=BC=E3=82=B8=E3=83=8D=E3=83=BC=E3=82=B7?= =?UTF-8?q?=E3=83=A7=E3=83=B3=E3=81=AE=E4=B8=8D=E5=85=B7=E5=90=88=E3=81=8C?= =?UTF-8?q?13.11.3=E3=81=A7=E4=BF=AE=E6=AD=A3=E3=81=95=E3=82=8C=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/infrastructure/notes/TimelinePagingStoreImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelinePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelinePagingStoreImpl.kt index 699b54929b..b22184537e 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelinePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelinePagingStoreImpl.kt @@ -87,11 +87,11 @@ internal class TimelinePagingStoreImpl( // NOTE: sinceIdが13.11.0で削除される破壊的変更が行われてしまったのでその判定を行なっている // https://github.com/misskey-dev/misskey/commit/b53d6c7f8ca1a712eab44967e8d05a0cc7bcc034#diff-883a3f5d77794cf2344c96727836aabc74c19f57db5e2e0cd485fdb6d3af7efeL77 - // **** YOU MISSKEY + // sinceId削除の件は不具合で13.11.3で修正された if (pageableTimeline is Pageable.Antenna) { val account = getAccount() val meta = metaRepository.find(account.normalizedInstanceUri).getOrThrow() - if (meta.getVersion() >= Version("13.11.0")) { + if (meta.getVersion() >= Version("13.11.0") && meta.getVersion() < Version("13.11.3")) { return@runCancellableCatching emptyList() } } From 81ed5d3b8f648790ceb64b577cfd16e8b59934a9 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 17 Apr 2023 12:41:26 +0900 Subject: [PATCH 024/432] fix --- modules/common_resource/src/main/res/values-ja/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 439ad2519c..a0149413c9 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -338,7 +338,7 @@ 説明 画像を選択 ドライブから画像を選択 - 端末から画像を選択 + ドライブから画像を選択 ノートの作成に成功しました センシティブ From 5199e2f352f21e303878fb738de25ae5464b005d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 17 Apr 2023 13:43:55 +0900 Subject: [PATCH 025/432] =?UTF-8?q?feat:=20=E3=82=B9=E3=83=AC=E3=83=83?= =?UTF-8?q?=E3=83=89=E3=81=AE=E6=A7=8B=E9=80=A0=E3=82=92ObjectBox=E3=81=A7?= =?UTF-8?q?=E8=A1=A8=E7=8F=BE=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/data/objectbox-models/default.json | 48 +++++++++++++++++-- .../data/objectbox-models/default.json.bak | 7 ++- .../notes/impl/db/ThreadRecord.kt | 25 ++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/ThreadRecord.kt diff --git a/modules/data/objectbox-models/default.json b/modules/data/objectbox-models/default.json index e3b0ef778e..ff836a5648 100644 --- a/modules/data/objectbox-models/default.json +++ b/modules/data/objectbox-models/default.json @@ -274,11 +274,53 @@ } ], "relations": [] + }, + { + "id": "2:2221534449032746185", + "lastPropertyId": "4:3861751215772392457", + "name": "ThreadRecord", + "properties": [ + { + "id": "1:6898596592219565017", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:2473069054411856439", + "name": "targetNoteId", + "type": 9 + }, + { + "id": "3:2251620119095233921", + "name": "accountId", + "type": 6 + }, + { + "id": "4:3861751215772392457", + "name": "targetNoteIdAndAccountId", + "indexId": "5:7310482946990627063", + "type": 9, + "flags": 2080 + } + ], + "relations": [ + { + "id": "1:5368363319508289421", + "name": "ancestors", + "targetId": "1:4355718382021751829" + }, + { + "id": "2:5971800600708341253", + "name": "descendants", + "targetId": "1:4355718382021751829" + } + ] } ], - "lastEntityId": "1:4355718382021751829", - "lastIndexId": "4:873220635513493863", - "lastRelationId": "0:0", + "lastEntityId": "2:2221534449032746185", + "lastIndexId": "5:7310482946990627063", + "lastRelationId": "2:5971800600708341253", "lastSequenceId": "0:0", "modelVersion": 5, "modelVersionParserMinimum": 5, diff --git a/modules/data/objectbox-models/default.json.bak b/modules/data/objectbox-models/default.json.bak index 5d9cab48aa..e3b0ef778e 100644 --- a/modules/data/objectbox-models/default.json.bak +++ b/modules/data/objectbox-models/default.json.bak @@ -5,7 +5,7 @@ "entities": [ { "id": "1:4355718382021751829", - "lastPropertyId": "50:4252978610725343033", + "lastPropertyId": "51:1047146758580551890", "name": "NoteRecord", "properties": [ { @@ -266,6 +266,11 @@ "id": "50:4252978610725343033", "name": "myReactions", "type": 30 + }, + { + "id": "51:1047146758580551890", + "name": "maxReactionsPerAccount", + "type": 5 } ], "relations": [] diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/ThreadRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/ThreadRecord.kt new file mode 100644 index 0000000000..3fa057c21e --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/ThreadRecord.kt @@ -0,0 +1,25 @@ +package net.pantasystem.milktea.data.infrastructure.notes.impl.db + +import io.objectbox.annotation.Entity +import io.objectbox.annotation.Id +import io.objectbox.annotation.Index +import io.objectbox.annotation.Unique +import io.objectbox.relation.ToMany + +@Entity +data class ThreadRecord( + @Id + var id: Long = 0, + + + var targetNoteId: String = "", + var accountId: Long = 0L, + + @Index + @Unique + var targetNoteIdAndAccountId: String = "", +) { + lateinit var ancestors: ToMany + + lateinit var descendants: ToMany +} \ No newline at end of file From 5ab5f3f3448373b60f74daa6105987c2e533977d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 17 Apr 2023 14:18:27 +0900 Subject: [PATCH 026/432] =?UTF-8?q?feat:=20=E3=82=B9=E3=83=AC=E3=83=83?= =?UTF-8?q?=E3=83=89=E3=81=AE=E6=A7=8B=E9=80=A0=E3=82=92=E8=A1=A8=E7=8F=BE?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AEModel=E3=82=92?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/model/notes/NoteThreadContext.kt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteThreadContext.kt diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteThreadContext.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteThreadContext.kt new file mode 100644 index 0000000000..db86bf8267 --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteThreadContext.kt @@ -0,0 +1,6 @@ +package net.pantasystem.milktea.model.notes + +data class NoteThreadContext( + val ancestors: List, + val descendants: List, +) \ No newline at end of file From aea739542c546284a8559d3ae3dcd16f75d58d85 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 17 Apr 2023 14:18:43 +0900 Subject: [PATCH 027/432] =?UTF-8?q?feat:=20ThreadRecord=E3=82=92=E6=89=B1?= =?UTF-8?q?=E3=81=86=E3=81=9F=E3=82=81=E3=81=AEDAO=E3=82=92=E4=BD=9C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/impl/db/NoteThreadRecordDAO.kt | 140 ++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt new file mode 100644 index 0000000000..d1865845f2 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt @@ -0,0 +1,140 @@ +package net.pantasystem.milktea.data.infrastructure.notes.impl.db + +import io.objectbox.Box +import io.objectbox.BoxStore +import io.objectbox.kotlin.awaitCallInTx +import io.objectbox.kotlin.boxFor +import io.objectbox.kotlin.inValues +import io.objectbox.kotlin.toFlow +import io.objectbox.query.QueryBuilder +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import net.pantasystem.milktea.model.notes.Note +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +open class NoteThreadRecordDAO @Inject constructor( + private val boxStore: BoxStore, +) { + + private val noteThreadContextBox: Box by lazy { + boxStore.boxFor() + } + + private val noteBox: Box by lazy { + boxStore.boxFor() + } + + suspend fun add(context: ThreadRecord) { + boxStore.awaitCallInTx { + val exists = noteThreadContextBox.query().equal( + ThreadRecord_.targetNoteIdAndAccountId, + context.targetNoteIdAndAccountId, + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().findFirst() + when (exists) { + null -> noteThreadContextBox.put(context) + else -> noteThreadContextBox.put(context.copy(id = exists.id)) + } + } + + } + + suspend fun appendBlank(noteId: Note.Id): ThreadRecord { + return boxStore.awaitCallInTx { + val exists = noteThreadContextBox.query().equal( + ThreadRecord_.targetNoteIdAndAccountId, + NoteRecord.generateAccountAndNoteId(noteId), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().findFirst() + when (exists) { + null -> { + val new = ThreadRecord( + targetNoteId = noteId.noteId, + accountId = noteId.accountId, + targetNoteIdAndAccountId = NoteRecord.generateAccountAndNoteId(noteId) + ) + noteThreadContextBox.put(new) + new + } + else -> exists + } + }!! + } + + suspend fun appendAncestor(threadTarget: Note.Id, appendTarget: Note.Id) { + appendBlank(threadTarget) + boxStore.awaitCallInTx { + val context = requireNotNull(findBy(threadTarget)) + context.ancestors.add(requireNotNull(findByNote(appendTarget))) + } + } + + suspend fun appendDescendant(threadTarget: Note.Id, appendTarget: Note.Id) { + appendBlank(threadTarget) + boxStore.awaitCallInTx { + val context = requireNotNull(findBy(threadTarget)) + context.descendants.add(requireNotNull(findByNote(appendTarget))) + } + } + + suspend fun appendDescendants(threadTarget: Note.Id, appendTargets: List) { + appendBlank(threadTarget) + boxStore.awaitCallInTx { + val context = requireNotNull(findBy(threadTarget)) + context.descendants.addAll(findByNotes(appendTargets)) + } + } + + suspend fun appendAncestors(threadTarget: Note.Id, appendTargets: List) { + appendBlank(threadTarget) + boxStore.awaitCallInTx { + val context = requireNotNull(findBy(threadTarget)) + context.ancestors.addAll(findByNotes(appendTargets)) + } + } + + suspend fun clearRelation(targetNote: Note.Id) { + boxStore.awaitCallInTx { + findBy(targetNote)?.also { + it.ancestors.clear() + it.descendants.clear() + } + } + } + + fun findBy(noteId: Note.Id): ThreadRecord? { + return noteThreadContextBox.query().equal( + ThreadRecord_.targetNoteIdAndAccountId, + NoteRecord.generateAccountAndNoteId(noteId), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().findFirst() + } + + private fun findByNote(noteId: Note.Id): NoteRecord? { + return noteBox.query().equal( + NoteRecord_.accountIdAndNoteId, + NoteRecord.generateAccountAndNoteId(noteId), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().findFirst() + } + + private fun findByNotes(noteIds: List): List { + return noteBox.query().inValues( + NoteRecord_.accountIdAndNoteId, noteIds.map { + NoteRecord.generateAccountAndNoteId(it) + }.toTypedArray(), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().find() + } + + @OptIn(ExperimentalCoroutinesApi::class) + fun observeBy(noteId: Note.Id): Flow> { + return noteThreadContextBox.query().equal( + ThreadRecord_.targetNoteIdAndAccountId, + NoteRecord.generateAccountAndNoteId(noteId), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().subscribe().toFlow() + } +} \ No newline at end of file From cb1ab09320f16b69af47c7d29663c80722be98fb Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 17 Apr 2023 14:41:01 +0900 Subject: [PATCH 028/432] =?UTF-8?q?feat:=20=E3=82=B9=E3=83=AC=E3=83=83?= =?UTF-8?q?=E3=83=89=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8B=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=82=92=E3=81=BE=E3=81=A8=E3=82=81=E3=81=A6=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/impl/NoteRepositoryImpl.kt | 108 +++++++++++------- .../milktea/model/notes/NoteRepository.kt | 8 +- 2 files changed, 71 insertions(+), 45 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt index 2afa766961..15df332d91 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt @@ -2,12 +2,16 @@ package net.pantasystem.milktea.data.infrastructure.notes.impl import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.asFlow +import kotlinx.coroutines.flow.map +import net.pantasystem.milktea.api.misskey.notes.NoteDTO import net.pantasystem.milktea.api.misskey.notes.NoteRequest import net.pantasystem.milktea.common.* import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.infrastructure.notes.NoteDataSourceAdder +import net.pantasystem.milktea.data.infrastructure.notes.impl.db.NoteThreadRecordDAO import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.GetAccount import net.pantasystem.milktea.model.drive.FilePropertyDataSource @@ -28,6 +32,7 @@ class NoteRepositoryImpl @Inject constructor( val noteDataSourceAdder: NoteDataSourceAdder, val getAccount: GetAccount, private val noteApiAdapter: NoteApiAdapter, + private val noteThreadRecordDAO: NoteThreadRecordDAO, @IODispatcher private val ioDispatcher: CoroutineDispatcher ) : NoteRepository { @@ -200,65 +205,59 @@ class NoteRepositoryImpl @Inject constructor( } } - override suspend fun syncChildren(noteId: Note.Id): Result = runCancellableCatching { + override suspend fun syncThreadContext(noteId: Note.Id): Result = runCancellableCatching { withContext(ioDispatcher) { val account = getAccount.get(noteId.accountId) when(account.instanceType) { Account.InstanceType.MISSKEY -> { - val dtoList = misskeyAPIProvider.get(account).children( - NoteRequest( - i = account.token, - noteId = noteId.noteId, - limit = 100, - ) - ).throwIfHasError().body()!! - dtoList.map { + val descendants = requireNotNull( + misskeyAPIProvider.get(account).conversation( + NoteRequest( + i = account.token, + noteId = noteId.noteId, + ) + ).throwIfHasError().body() + ).map { noteDataSourceAdder.addNoteDtoToDataSource(account, it) } + noteThreadRecordDAO.clearRelation(noteId) + noteThreadRecordDAO.appendDescendants(noteId, descendants.map { it.id }) + syncRecursiveThreadContext4Misskey(noteId, noteId) } Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { - val body = mastodonAPIProvider.get(account).getStatusesContext(noteId.noteId) - .throwIfHasError() - .body() - requireNotNull(body).let { - it.ancestors + it.descendants - }.map { + val body = requireNotNull( + mastodonAPIProvider.get(account).getStatusesContext(noteId.noteId) + .throwIfHasError() + .body() + ) + val ancestors = body.ancestors.map { noteDataSourceAdder.addTootStatusDtoIntoDataSource(account, it) } - } - } - - } - } - - override suspend fun syncConversation(noteId: Note.Id): Result = runCancellableCatching { - withContext(ioDispatcher) { - val account = getAccount.get(noteId.accountId) - when(account.instanceType) { - Account.InstanceType.MISSKEY -> { - val dtoList = misskeyAPIProvider.get(account).conversation( - NoteRequest( - i = account.token, - noteId = noteId.noteId, - ) - ).throwIfHasError().body()!! - dtoList.map { - noteDataSourceAdder.addNoteDtoToDataSource(account, it) - } - } - Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { - val body = mastodonAPIProvider.get(account).getStatusesContext(noteId.noteId) - .throwIfHasError() - .body() - requireNotNull(body).let { - it.ancestors + it.descendants - }.map { + val descendants = body.descendants.map { noteDataSourceAdder.addTootStatusDtoIntoDataSource(account, it) } + noteThreadRecordDAO.clearRelation(noteId) + noteThreadRecordDAO.appendAncestors(noteId, ancestors.map { it.id }) + noteThreadRecordDAO.appendDescendants(noteId, descendants.map { it.id }) } } + } + } + @OptIn(FlowPreview::class) + override fun observeThreadContext(noteId: Note.Id): Flow { + return suspend { + noteThreadRecordDAO.appendBlank(noteId) + }.asFlow().map { record -> + NoteThreadContext( + descendants = record.descendants.map { + it.toModel() + }, + ancestors = record.ancestors.map { + it.toModel() + } + ) } } @@ -342,4 +341,27 @@ class NoteRepositoryImpl @Inject constructor( is NoteResultType.Misskey -> noteDataSourceAdder.addNoteDtoToDataSource(account, type.note) } } + + private suspend fun getMisskeyDescendants(targetNoteId: Note.Id): List { + val account = getAccount.get(targetNoteId.accountId) + return requireNotNull(misskeyAPIProvider.get(account).children(NoteRequest( + i = account.token, + noteId = targetNoteId.noteId + )).throwIfHasError().body()) + } + + private suspend fun syncRecursiveThreadContext4Misskey(targetNoteId: Note.Id, appendTo: Note.Id) { + val account = getAccount.get(appendTo.accountId) + val ancestors = getMisskeyDescendants(targetNoteId).map { + noteDataSourceAdder.addNoteDtoToDataSource(account, it) + } + noteThreadRecordDAO.appendAncestors(appendTo, ancestors.map { it.id }) + coroutineScope { + ancestors.map { note -> + async { + syncRecursiveThreadContext4Misskey(note.id, appendTo) + } + } + } + } } \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteRepository.kt index 9c2be4b120..44b07c4de1 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteRepository.kt @@ -19,9 +19,11 @@ interface NoteRepository { suspend fun vote(noteId: Note.Id, choice: Poll.Choice): Result - suspend fun syncConversation(noteId: Note.Id): Result +// suspend fun syncConversation(noteId: Note.Id): Result +// +// suspend fun syncChildren(noteId: Note.Id): Result - suspend fun syncChildren(noteId: Note.Id): Result + suspend fun syncThreadContext(noteId: Note.Id): Result suspend fun sync(noteId: Note.Id): Result @@ -35,4 +37,6 @@ interface NoteRepository { fun observeOne(noteId: Note.Id): Flow + fun observeThreadContext(noteId: Note.Id): Flow + } \ No newline at end of file From c505ad97a52de91fd5e0f8e0a28052c48e37d8db Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 17 Apr 2023 15:08:30 +0900 Subject: [PATCH 029/432] =?UTF-8?q?feat:=20=E6=96=B0=E3=81=97=E3=81=84?= =?UTF-8?q?=E4=BB=95=E7=B5=84=E3=81=BF=E3=82=92=E5=88=A9=E7=94=A8=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/impl/db/NoteThreadRecordDAO.kt | 41 +++-------- .../detail/viewmodel/NoteDetailViewModel.kt | 69 ++++++------------- 2 files changed, 30 insertions(+), 80 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt index d1865845f2..5ff307b770 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt @@ -26,7 +26,7 @@ open class NoteThreadRecordDAO @Inject constructor( boxStore.boxFor() } - suspend fun add(context: ThreadRecord) { + open suspend fun add(context: ThreadRecord) { boxStore.awaitCallInTx { val exists = noteThreadContextBox.query().equal( ThreadRecord_.targetNoteIdAndAccountId, @@ -41,7 +41,7 @@ open class NoteThreadRecordDAO @Inject constructor( } - suspend fun appendBlank(noteId: Note.Id): ThreadRecord { + open suspend fun appendBlank(noteId: Note.Id): ThreadRecord { return boxStore.awaitCallInTx { val exists = noteThreadContextBox.query().equal( ThreadRecord_.targetNoteIdAndAccountId, @@ -63,48 +63,35 @@ open class NoteThreadRecordDAO @Inject constructor( }!! } - suspend fun appendAncestor(threadTarget: Note.Id, appendTarget: Note.Id) { - appendBlank(threadTarget) - boxStore.awaitCallInTx { - val context = requireNotNull(findBy(threadTarget)) - context.ancestors.add(requireNotNull(findByNote(appendTarget))) - } - } - - suspend fun appendDescendant(threadTarget: Note.Id, appendTarget: Note.Id) { - appendBlank(threadTarget) - boxStore.awaitCallInTx { - val context = requireNotNull(findBy(threadTarget)) - context.descendants.add(requireNotNull(findByNote(appendTarget))) - } - } - - suspend fun appendDescendants(threadTarget: Note.Id, appendTargets: List) { + open suspend fun appendDescendants(threadTarget: Note.Id, appendTargets: List) { appendBlank(threadTarget) boxStore.awaitCallInTx { val context = requireNotNull(findBy(threadTarget)) context.descendants.addAll(findByNotes(appendTargets)) + noteThreadContextBox.put(context) } } - suspend fun appendAncestors(threadTarget: Note.Id, appendTargets: List) { + open suspend fun appendAncestors(threadTarget: Note.Id, appendTargets: List) { appendBlank(threadTarget) boxStore.awaitCallInTx { val context = requireNotNull(findBy(threadTarget)) context.ancestors.addAll(findByNotes(appendTargets)) + noteThreadContextBox.put(context) } } - suspend fun clearRelation(targetNote: Note.Id) { + open suspend fun clearRelation(targetNote: Note.Id) { boxStore.awaitCallInTx { findBy(targetNote)?.also { it.ancestors.clear() it.descendants.clear() + noteThreadContextBox.put(it) } } } - fun findBy(noteId: Note.Id): ThreadRecord? { + open fun findBy(noteId: Note.Id): ThreadRecord? { return noteThreadContextBox.query().equal( ThreadRecord_.targetNoteIdAndAccountId, NoteRecord.generateAccountAndNoteId(noteId), @@ -112,14 +99,6 @@ open class NoteThreadRecordDAO @Inject constructor( ).build().findFirst() } - private fun findByNote(noteId: Note.Id): NoteRecord? { - return noteBox.query().equal( - NoteRecord_.accountIdAndNoteId, - NoteRecord.generateAccountAndNoteId(noteId), - QueryBuilder.StringOrder.CASE_SENSITIVE - ).build().findFirst() - } - private fun findByNotes(noteIds: List): List { return noteBox.query().inValues( NoteRecord_.accountIdAndNoteId, noteIds.map { @@ -130,7 +109,7 @@ open class NoteThreadRecordDAO @Inject constructor( } @OptIn(ExperimentalCoroutinesApi::class) - fun observeBy(noteId: Note.Id): Flow> { + open fun observeBy(noteId: Note.Id): Flow> { return noteThreadContextBox.query().equal( ThreadRecord_.targetNoteIdAndAccountId, NoteRecord.generateAccountAndNoteId(noteId), diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt index 3d0a373568..bd94badf9b 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt @@ -10,7 +10,7 @@ import dagger.assisted.AssistedInject import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import net.pantasystem.milktea.app_store.notes.NoteTranslationStore -import net.pantasystem.milktea.common.runCancellableCatching +import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.CurrentAccountWatcher import net.pantasystem.milktea.model.account.page.Pageable @@ -34,6 +34,7 @@ class NoteDetailViewModel @AssistedInject constructor( private val emojiRepository: CustomEmojiRepository, private val noteWordFilterService: WordFilterService, planeNoteViewDataCacheFactory: PlaneNoteViewDataCache.Factory, + private val loggerFactory: Logger.Factory, @Assisted val show: Pageable.Show, @Assisted val accountId: Long? = null, ) : ViewModel() { @@ -43,6 +44,10 @@ class NoteDetailViewModel @AssistedInject constructor( fun create(show: Pageable.Show, accountId: Long?): NoteDetailViewModel } + private val logger by lazy { + loggerFactory.create("NoteDetailVM") + } + companion object; private val currentAccountWatcher: CurrentAccountWatcher = @@ -60,32 +65,23 @@ class NoteDetailViewModel @AssistedInject constructor( emit(null) } - @OptIn(ExperimentalCoroutinesApi::class) - private val conversationNotes = note.filterNotNull().map { note -> - recursiveParentNotes(note.id).getOrThrow() - }.flatMapLatest { notes -> - noteDataSource.observeIn(notes.map { it.id }) - }.onStart { - emit(emptyList()) - } @OptIn(ExperimentalCoroutinesApi::class) - private val repliesMap = note.filterNotNull().flatMapLatest { current -> - noteDataSource.observeRecursiveReplies(current.id).map { list -> - list.groupBy { - it.replyId - } - } - }.onStart { - emit(emptyMap()) - } - - val notes = combine(note, conversationNotes, repliesMap) { note, conversation, repliesMap -> - val relatedConversation = noteRelationGetter.getIn(conversation.map { it.id }).filterNot { + val threadContext = note.filterNotNull().flatMapLatest { + noteRepository.observeThreadContext(it.id) + }.catch { + logger.error("ThreadContextの取得に失敗", it) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), NoteThreadContext(emptyList(), emptyList())) + + val notes = combine(note, threadContext) { note, thread -> + val relatedConversation = noteRelationGetter.getIn(thread.descendants.map { it.id }).filterNot { noteWordFilterService.isShouldFilterNote(show, it) }.map { NoteType.Conversation(it) } + val repliesMap = thread.ancestors.groupBy { + it.replyId + } val relatedChildren = noteRelationGetter.getIn((repliesMap[note?.id] ?: emptyList()).map { it.id }).filterNot { @@ -154,8 +150,9 @@ class NoteDetailViewModel @AssistedInject constructor( val account = currentAccountWatcher.getAccount() val note = noteRepository.find(Note.Id(account.accountId, show.noteId)) .getOrThrow() - noteRepository.syncConversation(note.id).getOrThrow() - recursiveSync(note.id).getOrThrow() +// noteRepository.syncConversation(note.id).getOrThrow() + noteRepository.syncThreadContext(note.id).getOrThrow() +// recursiveSync(note.id).getOrThrow() noteRepository.sync(note.id) } catch (e: Exception) { Log.w("NoteDetailViewModel", "loadDetail失敗", e) @@ -164,32 +161,6 @@ class NoteDetailViewModel @AssistedInject constructor( } - - private suspend fun recursiveSync(noteId: Note.Id): Result = runCancellableCatching { - coroutineScope { - noteRepository.syncChildren(noteId).also { - noteDataSource.findByReplyId(noteId).getOrThrow().map { - async { - recursiveSync(it.id).getOrNull() - } - }.awaitAll() - } - } - - - } - - - private suspend fun recursiveParentNotes(noteId: Note.Id?): Result> = - runCancellableCatching { - noteId ?: return Result.success(emptyList()) - val current = noteDataSource.get(noteId).getOrNull() - when (val replyId = current?.replyId) { - null -> return Result.success(emptyList()) - else -> recursiveParentNotes(replyId).getOrThrow() + current - } - } - suspend fun getUrl(): String { val account = currentAccountWatcher.getAccount() return "${account.normalizedInstanceUri}/notes/${show.noteId}" From 004656c15166b355a2b11532241b32ed4ae2c173 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 17 Apr 2023 15:27:43 +0900 Subject: [PATCH 030/432] fix --- .../infrastructure/notes/impl/NoteRepositoryImpl.kt | 12 ++++++------ .../note/detail/viewmodel/NoteDetailViewModel.kt | 4 ++-- .../milktea/model/notes/NoteThreadContext.kt | 4 ++++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt index 15df332d91..dceed062b8 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt @@ -210,7 +210,7 @@ class NoteRepositoryImpl @Inject constructor( val account = getAccount.get(noteId.accountId) when(account.instanceType) { Account.InstanceType.MISSKEY -> { - val descendants = requireNotNull( + val ancestors = requireNotNull( misskeyAPIProvider.get(account).conversation( NoteRequest( i = account.token, @@ -221,7 +221,7 @@ class NoteRepositoryImpl @Inject constructor( noteDataSourceAdder.addNoteDtoToDataSource(account, it) } noteThreadRecordDAO.clearRelation(noteId) - noteThreadRecordDAO.appendDescendants(noteId, descendants.map { it.id }) + noteThreadRecordDAO.appendAncestors(noteId, ancestors.map { it.id }) syncRecursiveThreadContext4Misskey(noteId, noteId) } Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { @@ -352,16 +352,16 @@ class NoteRepositoryImpl @Inject constructor( private suspend fun syncRecursiveThreadContext4Misskey(targetNoteId: Note.Id, appendTo: Note.Id) { val account = getAccount.get(appendTo.accountId) - val ancestors = getMisskeyDescendants(targetNoteId).map { + val descendants = getMisskeyDescendants(targetNoteId).map { noteDataSourceAdder.addNoteDtoToDataSource(account, it) } - noteThreadRecordDAO.appendAncestors(appendTo, ancestors.map { it.id }) + noteThreadRecordDAO.appendDescendants(appendTo, descendants.map { it.id }) coroutineScope { - ancestors.map { note -> + descendants.map { note -> async { syncRecursiveThreadContext4Misskey(note.id, appendTo) } - } + }.awaitAll() } } } \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt index bd94badf9b..cacd26428a 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt @@ -74,12 +74,12 @@ class NoteDetailViewModel @AssistedInject constructor( }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), NoteThreadContext(emptyList(), emptyList())) val notes = combine(note, threadContext) { note, thread -> - val relatedConversation = noteRelationGetter.getIn(thread.descendants.map { it.id }).filterNot { + val relatedConversation = noteRelationGetter.getIn(thread.ancestors.map { it.id }).filterNot { noteWordFilterService.isShouldFilterNote(show, it) }.map { NoteType.Conversation(it) } - val repliesMap = thread.ancestors.groupBy { + val repliesMap = thread.descendants.groupBy { it.replyId } val relatedChildren = noteRelationGetter.getIn((repliesMap[note?.id] ?: emptyList()).map { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteThreadContext.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteThreadContext.kt index db86bf8267..f047471412 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteThreadContext.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteThreadContext.kt @@ -1,5 +1,9 @@ package net.pantasystem.milktea.model.notes +/** + * @param ancestors 親投稿 + * @param descendants 子投稿 + */ data class NoteThreadContext( val ancestors: List, val descendants: List, From 57ab0924241f4aa49e9df0c28f7794b30c7496fc Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 18 Apr 2023 23:46:50 +0900 Subject: [PATCH 031/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E5=8F=96=E5=BE=97=E3=81=AE=E3=81=9F=E3=82=81=E3=81=AE?= =?UTF-8?q?=E3=83=AA=E3=82=AF=E3=82=A8=E3=82=B9=E3=83=88=E3=82=AA=E3=83=96?= =?UTF-8?q?=E3=82=B8=E3=82=A7=E3=82=AF=E3=83=88=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api/misskey/MisskeyAPI.kt | 2 +- .../misskey/notes/GetNoteChildrenRequest.kt | 11 ++ .../notes/impl/NoteRepositoryImpl.kt | 176 ++++++++++-------- 3 files changed, 112 insertions(+), 77 deletions(-) create mode 100644 modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/GetNoteChildrenRequest.kt diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt index 28197fefff..89b85d1a6d 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt @@ -153,7 +153,7 @@ interface MisskeyAPI { suspend fun showNote(@Body requestNote: NoteRequest): Response @POST("api/notes/children") - suspend fun children(@Body noteRequest: NoteRequest): Response> + suspend fun children(@Body req: GetNoteChildrenRequest): Response> @POST("api/notes/conversation") suspend fun conversation(@Body noteRequest: NoteRequest): Response> diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/GetNoteChildrenRequest.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/GetNoteChildrenRequest.kt new file mode 100644 index 0000000000..f339287cdc --- /dev/null +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/GetNoteChildrenRequest.kt @@ -0,0 +1,11 @@ +package net.pantasystem.milktea.api.misskey.notes + +import kotlinx.serialization.SerialName + +@kotlinx.serialization.Serializable +data class GetNoteChildrenRequest( + @SerialName("i") val i: String, + @SerialName("noteId") val noteId: String, + @SerialName("limit") val limit: Int, + @SerialName("depth") val depth: Int, +) \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt index dceed062b8..5949e56c50 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt @@ -4,6 +4,7 @@ import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.map +import net.pantasystem.milktea.api.misskey.notes.GetNoteChildrenRequest import net.pantasystem.milktea.api.misskey.notes.NoteDTO import net.pantasystem.milktea.api.misskey.notes.NoteRequest import net.pantasystem.milktea.common.* @@ -33,7 +34,7 @@ class NoteRepositoryImpl @Inject constructor( val getAccount: GetAccount, private val noteApiAdapter: NoteApiAdapter, private val noteThreadRecordDAO: NoteThreadRecordDAO, - @IODispatcher private val ioDispatcher: CoroutineDispatcher + @IODispatcher private val ioDispatcher: CoroutineDispatcher, ) : NoteRepository { private val logger = loggerFactory.create("NoteRepositoryImpl") @@ -45,23 +46,28 @@ class NoteRepositoryImpl @Inject constructor( } } - override suspend fun renote(noteId: Note.Id): Result = runCancellableCatching{ + override suspend fun renote(noteId: Note.Id): Result = runCancellableCatching { withContext(ioDispatcher) { val account = getAccount.get(noteId.accountId) - when(account.instanceType) { + when (account.instanceType) { Account.InstanceType.MISSKEY -> { val n = find(noteId).getOrThrow() - create(CreateNote( - author = account, renoteId = noteId, - text = null, - visibility = n.visibility - )).getOrThrow() + create( + CreateNote( + author = account, renoteId = noteId, + text = null, + visibility = n.visibility + ) + ).getOrThrow() } Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val toot = mastodonAPIProvider.get(account).reblog(noteId.noteId) .throwIfHasError() .body() - noteDataSourceAdder.addTootStatusDtoIntoDataSource(account, requireNotNull(toot)) + noteDataSourceAdder.addTootStatusDtoIntoDataSource( + account, + requireNotNull(toot) + ) } } } @@ -70,7 +76,7 @@ class NoteRepositoryImpl @Inject constructor( override suspend fun unrenote(noteId: Note.Id): Result = runCancellableCatching { withContext(ioDispatcher) { val account = getAccount.get(noteId.accountId) - when(account.instanceType) { + when (account.instanceType) { Account.InstanceType.MISSKEY -> delete(noteId).getOrThrow() Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val res = mastodonAPIProvider.get(account).unreblog(noteId.noteId) @@ -83,12 +89,15 @@ class NoteRepositoryImpl @Inject constructor( } } - override suspend fun delete(noteId: Note.Id): Result = runCancellableCatching{ + override suspend fun delete(noteId: Note.Id): Result = runCancellableCatching { withContext(ioDispatcher) { val account = getAccount.get(noteId.accountId) val note = find(noteId).getOrThrow() - when(val result = noteApiAdapter.delete(noteId)) { - is DeleteNoteResultType.Mastodon -> noteDataSourceAdder.addTootStatusDtoIntoDataSource(account, result.status) + when (val result = noteApiAdapter.delete(noteId)) { + is DeleteNoteResultType.Mastodon -> noteDataSourceAdder.addTootStatusDtoIntoDataSource( + account, + result.status + ) DeleteNoteResultType.Misskey -> note } } @@ -149,32 +158,31 @@ class NoteRepositoryImpl @Inject constructor( } - override suspend fun vote(noteId: Note.Id, choice: Poll.Choice): Result = runCancellableCatching { - withContext(ioDispatcher) { - val account = getAccount.get(noteId.accountId) - val note = find(noteId).getOrThrow() - when(val type = note.type) { - is Note.Type.Mastodon -> { - mastodonAPIProvider.get(account).voteOnPoll( - requireNotNull(type.pollId), - choices = listOf(choice.index) - ) - } - is Note.Type.Misskey -> { - misskeyAPIProvider.get(account).vote( - Vote( - i = getAccount.get(noteId.accountId).token, - choice = choice.index, - noteId = noteId.noteId + override suspend fun vote(noteId: Note.Id, choice: Poll.Choice): Result = + runCancellableCatching { + withContext(ioDispatcher) { + val account = getAccount.get(noteId.accountId) + val note = find(noteId).getOrThrow() + when (val type = note.type) { + is Note.Type.Mastodon -> { + mastodonAPIProvider.get(account).voteOnPoll( + requireNotNull(type.pollId), + choices = listOf(choice.index) ) - ).throwIfHasError() + } + is Note.Type.Misskey -> { + misskeyAPIProvider.get(account).vote( + Vote( + i = getAccount.get(noteId.accountId).token, + choice = choice.index, + noteId = noteId.noteId + ) + ).throwIfHasError() + } } - } + } } - } - - private suspend fun fetchIn(noteIds: List) { @@ -208,7 +216,7 @@ class NoteRepositoryImpl @Inject constructor( override suspend fun syncThreadContext(noteId: Note.Id): Result = runCancellableCatching { withContext(ioDispatcher) { val account = getAccount.get(noteId.accountId) - when(account.instanceType) { + when (account.instanceType) { Account.InstanceType.MISSKEY -> { val ancestors = requireNotNull( misskeyAPIProvider.get(account).conversation( @@ -271,7 +279,7 @@ class NoteRepositoryImpl @Inject constructor( override suspend fun createThreadMute(noteId: Note.Id): Result = runCancellableCatching { withContext(ioDispatcher) { val account = getAccount.get(noteId.accountId) - when(val result = noteApiAdapter.createThreadMute(noteId)) { + when (val result = noteApiAdapter.createThreadMute(noteId)) { is ToggleThreadMuteResultType.Mastodon -> { noteDataSourceAdder.addTootStatusDtoIntoDataSource(account, result.status) } @@ -283,7 +291,7 @@ class NoteRepositoryImpl @Inject constructor( override suspend fun deleteThreadMute(noteId: Note.Id): Result = runCancellableCatching { withContext(ioDispatcher) { val account = getAccount.get(noteId.accountId) - when(val result = noteApiAdapter.deleteThreadMute(noteId)) { + when (val result = noteApiAdapter.deleteThreadMute(noteId)) { is ToggleThreadMuteResultType.Mastodon -> { noteDataSourceAdder.addTootStatusDtoIntoDataSource(account, result.status) } @@ -292,40 +300,41 @@ class NoteRepositoryImpl @Inject constructor( } } - override suspend fun findNoteState(noteId: Note.Id): Result = runCancellableCatching { - withContext(ioDispatcher) { - val account = getAccount.get(noteId.accountId) - when(account.instanceType) { - Account.InstanceType.MISSKEY -> { - misskeyAPIProvider.get(account.normalizedInstanceUri).noteState( - NoteRequest( - i = account.token, - noteId = noteId.noteId - ) - ).throwIfHasError().body()!!.let { - NoteState( - isFavorited = it.isFavorited, - isMutedThread = it.isMutedThread, - isWatching = when(val watching = it.isWatching) { - null -> NoteState.Watching.None - else -> NoteState.Watching.Some(watching) - } - ) + override suspend fun findNoteState(noteId: Note.Id): Result = + runCancellableCatching { + withContext(ioDispatcher) { + val account = getAccount.get(noteId.accountId) + when (account.instanceType) { + Account.InstanceType.MISSKEY -> { + misskeyAPIProvider.get(account.normalizedInstanceUri).noteState( + NoteRequest( + i = account.token, + noteId = noteId.noteId + ) + ).throwIfHasError().body()!!.let { + NoteState( + isFavorited = it.isFavorited, + isMutedThread = it.isMutedThread, + isWatching = when (val watching = it.isWatching) { + null -> NoteState.Watching.None + else -> NoteState.Watching.Some(watching) + } + ) + } + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + find(noteId).mapCancellableCatching { + NoteState( + isFavorited = (it.type as Note.Type.Mastodon).favorited ?: false, + isMutedThread = (it.type as Note.Type.Mastodon).muted ?: false, + isWatching = NoteState.Watching.None, + ) + }.getOrThrow() } } - Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { - find(noteId).mapCancellableCatching { - NoteState( - isFavorited = (it.type as Note.Type.Mastodon).favorited ?: false, - isMutedThread = (it.type as Note.Type.Mastodon).muted ?: false, - isWatching = NoteState.Watching.None, - ) - }.getOrThrow() - } - } + } } - } override fun observeIn(noteIds: List): Flow> { return noteDataSource.observeIn(noteIds) @@ -336,21 +345,36 @@ class NoteRepositoryImpl @Inject constructor( } private suspend fun convertAndAdd(account: Account, type: NoteResultType): Note { - return when(type) { - is NoteResultType.Mastodon -> noteDataSourceAdder.addTootStatusDtoIntoDataSource(account, type.status) - is NoteResultType.Misskey -> noteDataSourceAdder.addNoteDtoToDataSource(account, type.note) + return when (type) { + is NoteResultType.Mastodon -> noteDataSourceAdder.addTootStatusDtoIntoDataSource( + account, + type.status + ) + is NoteResultType.Misskey -> noteDataSourceAdder.addNoteDtoToDataSource( + account, + type.note + ) } } private suspend fun getMisskeyDescendants(targetNoteId: Note.Id): List { val account = getAccount.get(targetNoteId.accountId) - return requireNotNull(misskeyAPIProvider.get(account).children(NoteRequest( - i = account.token, - noteId = targetNoteId.noteId - )).throwIfHasError().body()) + return requireNotNull( + misskeyAPIProvider.get(account).children( + GetNoteChildrenRequest( + i = account.token, + noteId = targetNoteId.noteId, + limit = 30, + depth = 2, + ) + ).throwIfHasError().body() + ) } - private suspend fun syncRecursiveThreadContext4Misskey(targetNoteId: Note.Id, appendTo: Note.Id) { + private suspend fun syncRecursiveThreadContext4Misskey( + targetNoteId: Note.Id, + appendTo: Note.Id, + ) { val account = getAccount.get(appendTo.accountId) val descendants = getMisskeyDescendants(targetNoteId).map { noteDataSourceAdder.addNoteDtoToDataSource(account, it) From 4c55ff8987beb6cc9f56ce52d8bfcc7c60b9f6ed Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 19 Apr 2023 17:27:13 +0900 Subject: [PATCH 032/432] =?UTF-8?q?feat:=20NoteDataSource=E7=B5=8C?= =?UTF-8?q?=E7=94=B1=E3=81=A7Thread=E3=81=AE=E7=8A=B6=E6=85=8B=E3=82=92?= =?UTF-8?q?=E4=BF=9D=E6=8C=81=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/impl/InMemoryNoteDataSource.kt | 20 ++++-- .../notes/impl/NoteRepositoryImpl.kt | 17 +---- .../notes/impl/ObjectBoxNoteDataSource.kt | 68 ++++++++++++++----- .../milktea/model/notes/NoteDataSource.kt | 9 ++- 4 files changed, 77 insertions(+), 37 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/InMemoryNoteDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/InMemoryNoteDataSource.kt index e0ee2f4724..5ccdc2762f 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/InMemoryNoteDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/InMemoryNoteDataSource.kt @@ -142,11 +142,6 @@ class InMemoryNoteDataSource @Inject constructor( }.distinctUntilChanged() } - override fun observeRecursiveReplies(noteId: Note.Id): Flow> { - return _state.map { - it.map.values.toList() - } - } override suspend fun findByReplyId(id: Note.Id): Result> { return Result.success( @@ -188,6 +183,21 @@ class InMemoryNoteDataSource @Inject constructor( } } + override suspend fun addNoteThreadContext( + noteId: Note.Id, + context: NoteThreadContext + ): Result = Result.success(Unit) + + override fun observeNoteThreadContext(noteId: Note.Id): Flow { + return emptyFlow() + } + + override suspend fun findNoteThreadContext(noteId: Note.Id): Result = Result.success( + NoteThreadContext(emptyList(), emptyList()) + ) + + override suspend fun clearNoteThreadContext(noteId: Note.Id): Result = Result.success(Unit) + private fun publish(ev: NoteDataSource.Event) = runBlocking { listenersLock.withLock { listeners.forEach { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt index 5949e56c50..e07cc1c140 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt @@ -2,8 +2,7 @@ package net.pantasystem.milktea.data.infrastructure.notes.impl import kotlinx.coroutines.* import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.asFlow -import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.filterNotNull import net.pantasystem.milktea.api.misskey.notes.GetNoteChildrenRequest import net.pantasystem.milktea.api.misskey.notes.NoteDTO import net.pantasystem.milktea.api.misskey.notes.NoteRequest @@ -253,20 +252,8 @@ class NoteRepositoryImpl @Inject constructor( } } - @OptIn(FlowPreview::class) override fun observeThreadContext(noteId: Note.Id): Flow { - return suspend { - noteThreadRecordDAO.appendBlank(noteId) - }.asFlow().map { record -> - NoteThreadContext( - descendants = record.descendants.map { - it.toModel() - }, - ancestors = record.ancestors.map { - it.toModel() - } - ) - } + return noteDataSource.observeNoteThreadContext(noteId).filterNotNull() } override suspend fun sync(noteId: Note.Id): Result = runCancellableCatching { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt index 7d23bc7525..11c4b67dc9 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt @@ -7,18 +7,16 @@ import io.objectbox.kotlin.boxFor import io.objectbox.kotlin.inValues import io.objectbox.kotlin.toFlow import io.objectbox.query.QueryBuilder -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.* import kotlinx.coroutines.flow.* -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.data.infrastructure.notes.impl.db.NoteRecord import net.pantasystem.milktea.data.infrastructure.notes.impl.db.NoteRecord_ +import net.pantasystem.milktea.data.infrastructure.notes.impl.db.NoteThreadRecordDAO import net.pantasystem.milktea.model.AddResult import net.pantasystem.milktea.model.notes.* import net.pantasystem.milktea.model.user.User @@ -27,6 +25,7 @@ import javax.inject.Inject class ObjectBoxNoteDataSource @Inject constructor( private val boxStore: BoxStore, + private val noteThreadRecordDAO: NoteThreadRecordDAO, @IODispatcher val coroutineDispatcher: CoroutineDispatcher, loggerFactory: Logger.Factory ) : NoteDataSource { @@ -106,12 +105,6 @@ class ObjectBoxNoteDataSource @Inject constructor( } } - override fun observeRecursiveReplies(noteId: Note.Id): Flow> { - return changedIdFlow.map { - recursiveFindReplies(noteId).getOrThrow() - } - } - override suspend fun exists(noteId: Note.Id): Boolean { return noteBox.query().equal( NoteRecord_.accountIdAndNoteId, @@ -250,6 +243,55 @@ class ObjectBoxNoteDataSource @Inject constructor( } } + @OptIn(FlowPreview::class) + override fun observeNoteThreadContext(noteId: Note.Id): Flow { + return suspend { + noteThreadRecordDAO.appendBlank(noteId) + }.asFlow().map { record -> + NoteThreadContext( + descendants = record.descendants.map { + it.toModel() + }, + ancestors = record.ancestors.map { + it.toModel() + } + ) + } + } + + override suspend fun addNoteThreadContext( + noteId: Note.Id, + context: NoteThreadContext + ): Result = runCancellableCatching { + withContext(coroutineDispatcher) { + noteThreadRecordDAO.clearRelation(noteId) + noteThreadRecordDAO.appendBlank(noteId) + noteThreadRecordDAO.appendAncestors(noteId, context.ancestors.map { it.id }) + noteThreadRecordDAO.appendDescendants(noteId, context.descendants.map { it.id }) + } + } + + override suspend fun clearNoteThreadContext(noteId: Note.Id): Result = runCancellableCatching{ + withContext(coroutineDispatcher) { + noteThreadRecordDAO.clearRelation(noteId) + } + } + + override suspend fun findNoteThreadContext(noteId: Note.Id): Result = runCancellableCatching { + withContext(coroutineDispatcher) { + noteThreadRecordDAO.findBy(noteId)?.let { record -> + NoteThreadContext( + ancestors = record.ancestors.mapNotNull { + it?.toModel() + }, + descendants = record.descendants.mapNotNull { + it?.toModel() + } + ) + } ?: NoteThreadContext(emptyList(), emptyList()) + } + } + private fun publish(ev: NoteDataSource.Event) = runBlocking { listenersLock.withLock { listeners.forEach { @@ -259,12 +301,6 @@ class ObjectBoxNoteDataSource @Inject constructor( changedIdFlow.value = UUID.randomUUID().toString() } - private suspend fun recursiveFindReplies(noteId: Note.Id): Result> = runCancellableCatching { - val children = findByReplyId(noteId).getOrThrow() - children + children.map { - recursiveFindReplies(it.id).getOrThrow() - }.flatten() - } private suspend fun onAdded(note: Note) { lock.withLock { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteDataSource.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteDataSource.kt index 1604988cf4..5cf32da20b 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteDataSource.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/NoteDataSource.kt @@ -75,6 +75,12 @@ interface NoteDataSource { suspend fun addAll(notes: List): Result> suspend fun clear(): Result + suspend fun addNoteThreadContext(noteId: Note.Id, context: NoteThreadContext): Result + + suspend fun clearNoteThreadContext(noteId: Note.Id): Result + + suspend fun findNoteThreadContext(noteId: Note.Id): Result + /** * 投稿者のuserIdに基づいて削除をします * @param userId 対称のUser#id @@ -86,6 +92,7 @@ interface NoteDataSource { fun observeOne(noteId: Note.Id): Flow - fun observeRecursiveReplies(noteId: Note.Id): Flow> + fun observeNoteThreadContext(noteId: Note.Id): Flow + } \ No newline at end of file From 3e75e96a0f87d05de385b2539abc6926eb5decfa Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 19 Apr 2023 17:32:58 +0900 Subject: [PATCH 033/432] =?UTF-8?q?feat:=20NoteDataSource=E7=B5=8C?= =?UTF-8?q?=E7=94=B1=E3=81=A7=E4=BF=9D=E6=8C=81=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/impl/NoteRepositoryImpl.kt | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt index e07cc1c140..25ef2a5a45 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteRepositoryImpl.kt @@ -11,7 +11,6 @@ import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.infrastructure.notes.NoteDataSourceAdder -import net.pantasystem.milktea.data.infrastructure.notes.impl.db.NoteThreadRecordDAO import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.GetAccount import net.pantasystem.milktea.model.drive.FilePropertyDataSource @@ -32,7 +31,6 @@ class NoteRepositoryImpl @Inject constructor( val noteDataSourceAdder: NoteDataSourceAdder, val getAccount: GetAccount, private val noteApiAdapter: NoteApiAdapter, - private val noteThreadRecordDAO: NoteThreadRecordDAO, @IODispatcher private val ioDispatcher: CoroutineDispatcher, ) : NoteRepository { @@ -227,8 +225,11 @@ class NoteRepositoryImpl @Inject constructor( ).map { noteDataSourceAdder.addNoteDtoToDataSource(account, it) } - noteThreadRecordDAO.clearRelation(noteId) - noteThreadRecordDAO.appendAncestors(noteId, ancestors.map { it.id }) + noteDataSource.clearNoteThreadContext(noteId) + noteDataSource.addNoteThreadContext(noteId, NoteThreadContext( + ancestors = ancestors, + descendants = emptyList() + )) syncRecursiveThreadContext4Misskey(noteId, noteId) } Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { @@ -244,9 +245,11 @@ class NoteRepositoryImpl @Inject constructor( val descendants = body.descendants.map { noteDataSourceAdder.addTootStatusDtoIntoDataSource(account, it) } - noteThreadRecordDAO.clearRelation(noteId) - noteThreadRecordDAO.appendAncestors(noteId, ancestors.map { it.id }) - noteThreadRecordDAO.appendDescendants(noteId, descendants.map { it.id }) + noteDataSource.clearNoteThreadContext(noteId) + noteDataSource.addNoteThreadContext(noteId, NoteThreadContext( + ancestors = ancestors, + descendants = descendants + )) } } } @@ -366,7 +369,13 @@ class NoteRepositoryImpl @Inject constructor( val descendants = getMisskeyDescendants(targetNoteId).map { noteDataSourceAdder.addNoteDtoToDataSource(account, it) } - noteThreadRecordDAO.appendDescendants(appendTo, descendants.map { it.id }) + val tc = noteDataSource.findNoteThreadContext(targetNoteId).getOrThrow() + noteDataSource.addNoteThreadContext( + targetNoteId, + tc.copy( + descendants = tc.descendants + descendants + ) + ) coroutineScope { descendants.map { note -> async { From 1c96ee93601ccbf479db4a371db1ba2ba29ab692 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 19 Apr 2023 19:05:05 +0900 Subject: [PATCH 034/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=83=97=E3=83=A9?= =?UTF-8?q?=E3=82=A4=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8B=E3=82=A4=E3=83=99?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=92=E5=8F=97=E3=81=91=E5=8F=96=E3=82=8C?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api_streaming/events.kt | 8 ++++ .../milktea/data/di/module/NoteModule.kt | 6 +++ .../notes/ReplyStreamingImpl.kt | 47 +++++++++++++++++++ .../milktea/model/notes/ReplyStreaming.kt | 8 ++++ 4 files changed, 69 insertions(+) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/ReplyStreamingImpl.kt create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/notes/ReplyStreaming.kt diff --git a/modules/api_streaming/src/main/java/net/pantasystem/milktea/api_streaming/events.kt b/modules/api_streaming/src/main/java/net/pantasystem/milktea/api_streaming/events.kt index 93888a3cbc..ad2646a81d 100644 --- a/modules/api_streaming/src/main/java/net/pantasystem/milktea/api_streaming/events.kt +++ b/modules/api_streaming/src/main/java/net/pantasystem/milktea/api_streaming/events.kt @@ -256,6 +256,14 @@ sealed class ChannelBody : StreamingEvent(){ override val id: String ) : Main() + @Serializable + @SerialName("reply") + data class Reply( + @SerialName("id") + override val id: String, + @SerialName("body") + val body: NoteDTO + ) : Main() } } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt index d5306ff874..58184792d6 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt @@ -8,6 +8,7 @@ import net.pantasystem.milktea.app_store.notes.NoteTranslationStore import net.pantasystem.milktea.app_store.notes.TimelineStore import net.pantasystem.milktea.data.infrastructure.notes.NoteStreamingImpl import net.pantasystem.milktea.data.infrastructure.notes.NoteTranslationStoreImpl +import net.pantasystem.milktea.data.infrastructure.notes.ReplyStreamingImpl import net.pantasystem.milktea.data.infrastructure.notes.TimelineStoreImpl import net.pantasystem.milktea.data.infrastructure.notes.draft.DraftNoteRepositoryImpl import net.pantasystem.milktea.data.infrastructure.notes.impl.DraftNoteServiceImpl @@ -17,6 +18,7 @@ import net.pantasystem.milktea.data.infrastructure.notes.renote.RenotesPagingSer import net.pantasystem.milktea.model.notes.NoteDataSource import net.pantasystem.milktea.model.notes.NoteRepository import net.pantasystem.milktea.model.notes.NoteStreaming +import net.pantasystem.milktea.model.notes.ReplyStreaming import net.pantasystem.milktea.model.notes.draft.DraftNoteRepository import net.pantasystem.milktea.model.notes.draft.DraftNoteService import net.pantasystem.milktea.model.notes.renote.RenotesPagingService @@ -54,6 +56,10 @@ abstract class NoteBindModule{ @Singleton abstract fun provideDraftNoteRepository(impl: DraftNoteRepositoryImpl): DraftNoteRepository + @Binds + @Singleton + abstract fun bindReplyStreaming(impl: ReplyStreamingImpl): ReplyStreaming + } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/ReplyStreamingImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/ReplyStreamingImpl.kt new file mode 100644 index 0000000000..ef4225b4f4 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/ReplyStreamingImpl.kt @@ -0,0 +1,47 @@ +package net.pantasystem.milktea.data.infrastructure.notes + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* +import net.pantasystem.milktea.api_streaming.ChannelBody +import net.pantasystem.milktea.api_streaming.channel.ChannelAPI +import net.pantasystem.milktea.api_streaming.mastodon.Event +import net.pantasystem.milktea.data.streaming.ChannelAPIWithAccountProvider +import net.pantasystem.milktea.data.streaming.StreamingAPIProvider +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.notes.Note +import net.pantasystem.milktea.model.notes.ReplyStreaming +import javax.inject.Inject + +class ReplyStreamingImpl @Inject constructor( + private val channelAPIProvider: ChannelAPIWithAccountProvider, + private val noteDataSourceAdder: NoteDataSourceAdder, + private val streamingAPIProvider: StreamingAPIProvider, +) : ReplyStreaming { + @OptIn(ExperimentalCoroutinesApi::class) + override fun connect(getAccount: suspend () -> Account): Flow { + return flow { + emit(getAccount()) + }.flatMapLatest { ac -> + when(ac.instanceType) { + Account.InstanceType.MISSKEY -> { + requireNotNull(channelAPIProvider.get(ac)).connect(ChannelAPI.Type.Main).map { + it as ChannelBody.Main.Reply + }.map { + it.body + }.map { + noteDataSourceAdder.addNoteDtoToDataSource(ac, it) + } + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + requireNotNull(streamingAPIProvider.get(ac)).connectUser().mapNotNull { + (it as? Event.Update)?.status + }.filter { + it.inReplyToId != null + }.map { + noteDataSourceAdder.addTootStatusDtoIntoDataSource(ac, it) + } + } + } + } + } +} \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/ReplyStreaming.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/ReplyStreaming.kt new file mode 100644 index 0000000000..88217c25b2 --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/ReplyStreaming.kt @@ -0,0 +1,8 @@ +package net.pantasystem.milktea.model.notes + +import kotlinx.coroutines.flow.Flow +import net.pantasystem.milktea.model.account.Account + +interface ReplyStreaming { + fun connect(getAccount: suspend ()-> Account): Flow +} \ No newline at end of file From b8a27bf8f05c7121a7e728c1f550cf9a151849dc Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 19 Apr 2023 19:32:17 +0900 Subject: [PATCH 035/432] =?UTF-8?q?fix:=20=E6=AD=A3=E5=B8=B8=E3=81=AB?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E5=95=8F?= =?UTF-8?q?=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/impl/ObjectBoxNoteDataSource.kt | 19 ++++++++-- .../notes/impl/db/NoteThreadRecordDAO.kt | 37 +++---------------- 2 files changed, 22 insertions(+), 34 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt index 11c4b67dc9..186597f2a8 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt @@ -265,9 +265,13 @@ class ObjectBoxNoteDataSource @Inject constructor( ): Result = runCancellableCatching { withContext(coroutineDispatcher) { noteThreadRecordDAO.clearRelation(noteId) - noteThreadRecordDAO.appendBlank(noteId) - noteThreadRecordDAO.appendAncestors(noteId, context.ancestors.map { it.id }) - noteThreadRecordDAO.appendDescendants(noteId, context.descendants.map { it.id }) + val record = noteThreadRecordDAO.appendBlank(noteId) + + record.ancestors.clear() + record.ancestors.addAll(findByNotes(context.ancestors.map { it.id })) + record.descendants.clear() + record.descendants.addAll(findByNotes(context.descendants.map { it.id })) + noteThreadRecordDAO.update(record) } } @@ -307,4 +311,13 @@ class ObjectBoxNoteDataSource @Inject constructor( deleteNoteIds.remove(note.id) } } + + private fun findByNotes(noteIds: List): List { + return noteBox.query().inValues( + NoteRecord_.accountIdAndNoteId, noteIds.map { + NoteRecord.generateAccountAndNoteId(it) + }.toTypedArray(), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().find() + } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt index 5ff307b770..fdd462d456 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteThreadRecordDAO.kt @@ -4,7 +4,6 @@ import io.objectbox.Box import io.objectbox.BoxStore import io.objectbox.kotlin.awaitCallInTx import io.objectbox.kotlin.boxFor -import io.objectbox.kotlin.inValues import io.objectbox.kotlin.toFlow import io.objectbox.query.QueryBuilder import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -22,10 +21,6 @@ open class NoteThreadRecordDAO @Inject constructor( boxStore.boxFor() } - private val noteBox: Box by lazy { - boxStore.boxFor() - } - open suspend fun add(context: ThreadRecord) { boxStore.awaitCallInTx { val exists = noteThreadContextBox.query().equal( @@ -41,6 +36,12 @@ open class NoteThreadRecordDAO @Inject constructor( } + open suspend fun update(context: ThreadRecord) { + boxStore.awaitCallInTx { + noteThreadContextBox.put(context) + } + } + open suspend fun appendBlank(noteId: Note.Id): ThreadRecord { return boxStore.awaitCallInTx { val exists = noteThreadContextBox.query().equal( @@ -63,23 +64,6 @@ open class NoteThreadRecordDAO @Inject constructor( }!! } - open suspend fun appendDescendants(threadTarget: Note.Id, appendTargets: List) { - appendBlank(threadTarget) - boxStore.awaitCallInTx { - val context = requireNotNull(findBy(threadTarget)) - context.descendants.addAll(findByNotes(appendTargets)) - noteThreadContextBox.put(context) - } - } - - open suspend fun appendAncestors(threadTarget: Note.Id, appendTargets: List) { - appendBlank(threadTarget) - boxStore.awaitCallInTx { - val context = requireNotNull(findBy(threadTarget)) - context.ancestors.addAll(findByNotes(appendTargets)) - noteThreadContextBox.put(context) - } - } open suspend fun clearRelation(targetNote: Note.Id) { boxStore.awaitCallInTx { @@ -99,15 +83,6 @@ open class NoteThreadRecordDAO @Inject constructor( ).build().findFirst() } - private fun findByNotes(noteIds: List): List { - return noteBox.query().inValues( - NoteRecord_.accountIdAndNoteId, noteIds.map { - NoteRecord.generateAccountAndNoteId(it) - }.toTypedArray(), - QueryBuilder.StringOrder.CASE_SENSITIVE - ).build().find() - } - @OptIn(ExperimentalCoroutinesApi::class) open fun observeBy(noteId: Note.Id): Flow> { return noteThreadContextBox.query().equal( From ea5b8c158323ddb36d10572799aebc16a6109102 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 19 Apr 2023 19:32:28 +0900 Subject: [PATCH 036/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=83=AB?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=81=A7=E6=9B=B4=E6=96=B0=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/viewmodel/NoteDetailViewModel.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt index cacd26428a..8fff951b59 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/viewmodel/NoteDetailViewModel.kt @@ -22,6 +22,7 @@ import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewDataCache +@OptIn(FlowPreview::class) class NoteDetailViewModel @AssistedInject constructor( accountRepository: AccountRepository, private val noteCaptureAdapter: NoteCaptureAPIAdapter, @@ -35,6 +36,7 @@ class NoteDetailViewModel @AssistedInject constructor( private val noteWordFilterService: WordFilterService, planeNoteViewDataCacheFactory: PlaneNoteViewDataCache.Factory, private val loggerFactory: Logger.Factory, + private val noteReplyStreaming: ReplyStreaming, @Assisted val show: Pageable.Show, @Assisted val accountId: Long? = null, ) : ViewModel() { @@ -159,6 +161,29 @@ class NoteDetailViewModel @AssistedInject constructor( } } + viewModelScope.launch { + noteReplyStreaming.connect { currentAccountWatcher.getAccount() }.mapNotNull { reply -> + logger.debug { + "reply:${reply.id}" + } + val account = currentAccountWatcher.getAccount() + val note = noteRepository.find(Note.Id(account.accountId, show.noteId)) + .getOrThrow() + val context = noteDataSource.findNoteThreadContext(note.id).getOrThrow() + val isRelatedReply = context.descendants.any { + it.id == reply.id + } || note.id == reply.replyId + if (isRelatedReply) { + val updatedContext = context.copy( + descendants = context.descendants + reply + ) + noteDataSource.addNoteThreadContext(note.id, updatedContext).getOrThrow() + } + }.catch { + logger.error("observe reply error", it) + }.collect() + } + } suspend fun getUrl(): String { From 53c7e22f040a2af07ce7fcb71a180e5e93ee8644 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 13:35:17 +0900 Subject: [PATCH 037/432] =?UTF-8?q?feat:=20repository=E3=81=AEinterface?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/notes/reaction/ReactionUserRepository.kt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt new file mode 100644 index 0000000000..5ca056c78c --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt @@ -0,0 +1,11 @@ +package net.pantasystem.milktea.model.notes.reaction + +import kotlinx.coroutines.flow.Flow +import net.pantasystem.milktea.model.notes.Note +import net.pantasystem.milktea.model.user.User + +interface ReactionUserRepository { + suspend fun syncBy(noteId: Note.Id, reaction: String): Result + suspend fun observeBy(noteId: Note.Id, reaction: String): Flow> + suspend fun findBy(noteId: Note.Id, reaction: String): Result> +} \ No newline at end of file From 984caa2ed02b4f1317174c7a9fa147a949d399cb Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 13:50:56 +0900 Subject: [PATCH 038/432] =?UTF-8?q?feat:=20mastodon=E3=81=A7=E3=82=82?= =?UTF-8?q?=E5=8B=95=E4=BD=9C=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/user/UserRepositoryImpl.kt | 67 +++++++++++++------ 1 file changed, 46 insertions(+), 21 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt index 8b34f3a15d..56a086d9a0 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt @@ -1,15 +1,16 @@ package net.pantasystem.milktea.data.infrastructure.user -import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.withContext +import kotlinx.coroutines.* import net.pantasystem.milktea.api.misskey.users.* import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common.throwIfHasError import net.pantasystem.milktea.common_android.hilt.IODispatcher +import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.converters.UserDTOEntityConverter +import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.drive.FilePropertyDataSource import net.pantasystem.milktea.model.user.User @@ -27,6 +28,7 @@ internal class UserRepositoryImpl @Inject constructor( val misskeyAPIProvider: MisskeyAPIProvider, val loggerFactory: Logger.Factory, val userApiAdapter: UserApiAdapter, + private val mastodonAPIProvider: MastodonAPIProvider, val userDTOEntityConverter: UserDTOEntityConverter, @IODispatcher val ioDispatcher: CoroutineDispatcher, ) : UserRepository { @@ -170,26 +172,49 @@ internal class UserRepositoryImpl @Inject constructor( override suspend fun syncIn(userIds: List): Result> { return runCancellableCatching { withContext(ioDispatcher) { - val accountId = userIds.map { it.accountId }.distinct().firstOrNull() - if (accountId == null) { - emptyList() - } else { - val account = accountRepository.get(accountId) - .getOrThrow() - val users = misskeyAPIProvider.get(account) - .showUsers( - RequestUser( - i = account.token, - userIds = userIds.map { it.id }, - detail = true - ) - ).throwIfHasError() - .body()!!.map { - userDTOEntityConverter.convert(account, it, true) - } - userDataSource.addAll(users) - users.map { it.id } + val accountIds = userIds.map { it.accountId }.distinct() + if (accountIds.isEmpty()) { + return@withContext emptyList() } + coroutineScope { + accountIds.map { accountId -> + async { + val account = accountRepository.get(accountId).getOrThrow() + when(account.instanceType) { + Account.InstanceType.MISSKEY -> { + val users = misskeyAPIProvider.get(account) + .showUsers( + RequestUser( + i = account.token, + userIds = userIds.filter { it.accountId == accountId }.map { it.id }, + detail = true + ) + ).throwIfHasError() + .body()!!.map { + userDTOEntityConverter.convert(account, it, true) + } + userDataSource.addAll(users) + users.map { it.id } + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + val users = userIds.filter { it.accountId == accountId }.map { it.id }.map { + async { + requireNotNull( + mastodonAPIProvider.get(account) + .getAccount(it) + .throwIfHasError() + .body() + ).toModel(account) + } + }.awaitAll() + userDataSource.addAll(users) + users.map { it.id } + } + } + + } + }.awaitAll() + }.flatten() } } } From c2fc11e10519181eae5f8de7130d0a952b505482 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 13:56:00 +0900 Subject: [PATCH 039/432] =?UTF-8?q?feat:=20=E3=83=98=E3=83=83=E3=83=80?= =?UTF-8?q?=E3=83=BC=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/reaction/ReactionUserRepository.kt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt index 5ca056c78c..616628bb69 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt @@ -5,7 +5,24 @@ import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.user.User interface ReactionUserRepository { + /** + * 内部的に保持しているリアクションしたユーザの履歴の状態をリモートと同期する + * @param reaction 同期する対象のリアクションでカスタム絵文字の場合は:emoji_type@.:あるいは:emoji_type@host:の形式で与えられる + * @param noteId 同期する対象のNoteのId + */ suspend fun syncBy(noteId: Note.Id, reaction: String): Result + + /** + * 内部的に保持しているリアクションしたユーザの履歴の状態をobserveする + * @param reaction 同期する対象のリアクションでカスタム絵文字の場合は:emoji_type@.:あるいは:emoji_type@host:の形式で与えられる + * @param noteId 同期する対象のNoteのId + */ suspend fun observeBy(noteId: Note.Id, reaction: String): Flow> + + /** + * 内部的に保持しているリアクションしたユーザの履歴の現在の状態を参照する + * @param reaction 同期する対象のリアクションでカスタム絵文字の場合は:emoji_type@.:あるいは:emoji_type@host:の形式で与えられる + * @param noteId 同期する対象のNoteのId + */ suspend fun findBy(noteId: Note.Id, reaction: String): Result> } \ No newline at end of file From 360bb7c30ac1d53fcac6f8c887f95ee4f2ca9862 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:26:36 +0900 Subject: [PATCH 040/432] =?UTF-8?q?feat:=20DAO=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/reaction/impl/ReactionUserDAO.kt | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserDAO.kt diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserDAO.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserDAO.kt new file mode 100644 index 0000000000..1ac70d8145 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserDAO.kt @@ -0,0 +1,85 @@ +package net.pantasystem.milktea.data.infrastructure.notes.reaction.impl + +import io.objectbox.Box +import io.objectbox.BoxStore +import io.objectbox.kotlin.boxFor +import io.objectbox.kotlin.toFlow +import io.objectbox.query.QueryBuilder +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import net.pantasystem.milktea.model.notes.Note +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ReactionUserDAO @Inject constructor( + private val boxStore: BoxStore, +) { + + private val reactionBox: Box by lazy { + boxStore.boxFor() + } + + fun findBy(noteId: Note.Id, reaction: String): ReactionUsersRecord? { + return reactionBox.query().equal( + ReactionUsersRecord_.accountIdAndNoteIdAndReaction, + ReactionUsersRecord.generateUniqueId(noteId, reaction), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().findFirst() + } + + fun update(noteId: Note.Id, reaction: String, accountIds: List) { + val record = createEmptyIfNotExists(noteId, reaction) + record.accountIds = accountIds.toMutableList() + reactionBox.put(record) + } + + fun appendAccountIds(noteId: Note.Id, reaction: String, accountIds: List) { + val record = createEmptyIfNotExists(noteId, reaction) + record.accountIds.addAll(accountIds) + reactionBox.put(record) + } + + fun remove(noteId: Note.Id, reaction: String) { + findBy(noteId, reaction)?.let { + reactionBox.remove(it) + } + } + + fun createEmptyIfNotExists(noteId: Note.Id, reaction: String): ReactionUsersRecord { + return when (val exists = findBy(noteId, reaction)) { + null -> { + reactionBox.put( + ReactionUsersRecord( + accountId = noteId.accountId, + noteId = noteId.noteId, + accountIdAndNoteIdAndReaction = ReactionUsersRecord.generateUniqueId( + noteId, + reaction + ), + reaction = reaction, + ) + ) + requireNotNull(findBy(noteId, reaction)) + } + else -> exists + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + fun observeBy(noteId: Note.Id, reaction: String): Flow { + return reactionBox.query() + .equal(ReactionUsersRecord_.accountId, noteId.accountId) + .equal(ReactionUsersRecord_.noteId, noteId.noteId, QueryBuilder.StringOrder.CASE_SENSITIVE) + .equal(ReactionUsersRecord_.reaction, reaction, QueryBuilder.StringOrder.CASE_SENSITIVE) + .build() + .subscribe() + .toFlow().map { + it.firstOrNull() + } + } + + + +} \ No newline at end of file From 3190d6d0c4a82814648480fa8b5d1d8b2f374ab2 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:27:28 +0900 Subject: [PATCH 041/432] =?UTF-8?q?feat:=20Repository=E3=82=92=E5=AE=9F?= =?UTF-8?q?=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/ReactionUserRepositoryImpl.kt | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt new file mode 100644 index 0000000000..a3cdc90065 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt @@ -0,0 +1,86 @@ +package net.pantasystem.milktea.data.infrastructure.notes.reaction.impl + +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* +import net.pantasystem.milktea.api.misskey.notes.reaction.ReactionHistoryDTO +import net.pantasystem.milktea.api.misskey.notes.reaction.RequestReactionHistoryDTO +import net.pantasystem.milktea.common.runCancellableCatching +import net.pantasystem.milktea.common.throwIfHasError +import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider +import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.notes.Note +import net.pantasystem.milktea.model.notes.reaction.ReactionUserRepository +import net.pantasystem.milktea.model.user.User +import net.pantasystem.milktea.model.user.UserDataSource +import net.pantasystem.milktea.model.user.UserRepository +import javax.inject.Inject + +class ReactionUserRepositoryImpl @Inject constructor( + private val accountRepository: AccountRepository, + private val misskeyAPIProvider: MisskeyAPIProvider, + private val mastodonAPIProvider: MastodonAPIProvider, + private val userRepository: UserRepository, + private val userDataSource: UserDataSource, + private val dao: ReactionUserDAO, +) : ReactionUserRepository { + + override suspend fun syncBy(noteId: Note.Id, reaction: String): Result = runCancellableCatching { + val account = accountRepository.get(noteId.accountId).getOrThrow() + dao.remove(noteId, reaction) + dao.createEmptyIfNotExists(noteId, reaction) + when(account.instanceType) { + Account.InstanceType.MISSKEY -> { + var reactions: List + do { + reactions = requireNotNull( + misskeyAPIProvider.get(account).reactions( + RequestReactionHistoryDTO( + i = account.token, + noteId = noteId.noteId, + type = reaction, + ) + ).throwIfHasError().body() + ) + dao.appendAccountIds(noteId, reaction, reactions.map { it.user.id }) + } while (reactions.isNotEmpty()) + + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + val resBody = requireNotNull( + mastodonAPIProvider.get(account).getStatus(noteId.noteId) + .throwIfHasError() + .body() + ) + val emojiReaction = resBody.emojiReactions?.firstOrNull { + it.reaction == reaction + } + val accountIds = emojiReaction?.accountIds?: emptyList() + userRepository.syncIn(accountIds.map { + User.Id(account.accountId, it) + }) + dao.update(noteId, reaction, accountIds) + } + + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + override suspend fun observeBy(noteId: Note.Id, reaction: String): Flow> { + return flow { + dao.createEmptyIfNotExists(noteId, reaction) + emit(accountRepository.get(noteId.accountId).getOrThrow()) + }.flatMapLatest { account -> + dao.observeBy(noteId, reaction).filterNotNull().flatMapLatest { + userDataSource.observeIn(account.accountId, it.accountIds) + } + } + } + + override suspend fun findBy(noteId: Note.Id, reaction: String): Result> = runCancellableCatching { + val accountIds = dao.findBy(noteId, reaction)?.accountIds ?: emptyList() + userDataSource.getIn(noteId.accountId, accountIds).getOrThrow() + } + +} \ No newline at end of file From a93eabe3a4d0e2e64bcf8e9f6d5a9d9af2c765df Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:27:35 +0900 Subject: [PATCH 042/432] =?UTF-8?q?feat:=20Record=E3=82=92=E4=BD=9C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reaction/impl/ReactionUsersRecord.kt | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUsersRecord.kt diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUsersRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUsersRecord.kt new file mode 100644 index 0000000000..740f782872 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUsersRecord.kt @@ -0,0 +1,32 @@ +package net.pantasystem.milktea.data.infrastructure.notes.reaction.impl + +import io.objectbox.annotation.Entity +import io.objectbox.annotation.Id +import io.objectbox.annotation.Index +import io.objectbox.annotation.Unique +import net.pantasystem.milktea.model.notes.Note + +@Entity +data class ReactionUsersRecord( + @Id var id: Long = 0L, + + @Index var accountId: Long = 0L, + + @Index + var noteId: String = "", + + @Unique + @Index + var accountIdAndNoteIdAndReaction: String = "", + + var reaction: String = "", + + var accountIds: MutableList = mutableListOf() +) { + + companion object { + fun generateUniqueId(noteId: Note.Id, reaction: String): String { + return "${noteId.accountId}-${noteId.noteId}-$reaction" + } + } +} \ No newline at end of file From 0576e9e7d59dc7adf006301ab749a6fb6c9fe862 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:27:41 +0900 Subject: [PATCH 043/432] =?UTF-8?q?feat:=20=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt b/app/src/main/java/jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt index e140a3bd10..be47f19170 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt @@ -7,7 +7,7 @@ import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import io.objectbox.BoxStore -import net.pantasystem.milktea.data.infrastructure.notes.impl.db.MyObjectBox +import net.pantasystem.milktea.data.infrastructure.notes.MyObjectBox import javax.inject.Singleton @Module From aea7eef065096fc45c93bd5eb46b9337b06699b1 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:41:17 +0900 Subject: [PATCH 044/432] =?UTF-8?q?feat:=20null=E3=82=92=E8=A8=B1=E5=AE=B9?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/di/module/ReactionModule.kt | 6 +++++ .../notes/reaction/impl/ReactionUserDAO.kt | 24 +++++++++---------- .../impl/ReactionUserRepositoryImpl.kt | 6 ++--- .../reaction/impl/ReactionUsersRecord.kt | 9 +++++-- .../notes/reaction/ReactionUserRepository.kt | 6 ++--- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/ReactionModule.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/ReactionModule.kt index 06207ac85a..b2c7315b25 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/ReactionModule.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/ReactionModule.kt @@ -7,10 +7,12 @@ import dagger.hilt.components.SingletonComponent import net.pantasystem.milktea.data.infrastructure.emoji.UserEmojiConfigRepositoryImpl import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.InMemoryReactionHistoryDataSource import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.ReactionHistoryPaginatorImpl +import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.ReactionUserRepositoryImpl import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.history.ReactionHistoryRepositoryImpl import net.pantasystem.milktea.model.emoji.UserEmojiConfigRepository import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryDataSource import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryPaginator +import net.pantasystem.milktea.model.notes.reaction.ReactionUserRepository import net.pantasystem.milktea.model.notes.reaction.history.ReactionHistoryRepository import javax.inject.Singleton @@ -33,4 +35,8 @@ abstract class ReactionModule { @Binds @Singleton abstract fun bindUserEmojiConfigRepository(impl: UserEmojiConfigRepositoryImpl): UserEmojiConfigRepository + + @Binds + @Singleton + abstract fun bindReactionUserRepository(impl: ReactionUserRepositoryImpl): ReactionUserRepository } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserDAO.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserDAO.kt index 1ac70d8145..cbbfc43e0e 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserDAO.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserDAO.kt @@ -21,7 +21,7 @@ class ReactionUserDAO @Inject constructor( boxStore.boxFor() } - fun findBy(noteId: Note.Id, reaction: String): ReactionUsersRecord? { + fun findBy(noteId: Note.Id, reaction: String?): ReactionUsersRecord? { return reactionBox.query().equal( ReactionUsersRecord_.accountIdAndNoteIdAndReaction, ReactionUsersRecord.generateUniqueId(noteId, reaction), @@ -29,25 +29,25 @@ class ReactionUserDAO @Inject constructor( ).build().findFirst() } - fun update(noteId: Note.Id, reaction: String, accountIds: List) { + fun update(noteId: Note.Id, reaction: String?, accountIds: List) { val record = createEmptyIfNotExists(noteId, reaction) record.accountIds = accountIds.toMutableList() reactionBox.put(record) } - fun appendAccountIds(noteId: Note.Id, reaction: String, accountIds: List) { + fun appendAccountIds(noteId: Note.Id, reaction: String?, accountIds: List) { val record = createEmptyIfNotExists(noteId, reaction) record.accountIds.addAll(accountIds) reactionBox.put(record) } - fun remove(noteId: Note.Id, reaction: String) { + fun remove(noteId: Note.Id, reaction: String?) { findBy(noteId, reaction)?.let { reactionBox.remove(it) } } - fun createEmptyIfNotExists(noteId: Note.Id, reaction: String): ReactionUsersRecord { + fun createEmptyIfNotExists(noteId: Note.Id, reaction: String?): ReactionUsersRecord { return when (val exists = findBy(noteId, reaction)) { null -> { reactionBox.put( @@ -58,7 +58,7 @@ class ReactionUserDAO @Inject constructor( noteId, reaction ), - reaction = reaction, + reaction = reaction ?: "", ) ) requireNotNull(findBy(noteId, reaction)) @@ -68,11 +68,13 @@ class ReactionUserDAO @Inject constructor( } @OptIn(ExperimentalCoroutinesApi::class) - fun observeBy(noteId: Note.Id, reaction: String): Flow { + fun observeBy(noteId: Note.Id, reaction: String?): Flow { return reactionBox.query() - .equal(ReactionUsersRecord_.accountId, noteId.accountId) - .equal(ReactionUsersRecord_.noteId, noteId.noteId, QueryBuilder.StringOrder.CASE_SENSITIVE) - .equal(ReactionUsersRecord_.reaction, reaction, QueryBuilder.StringOrder.CASE_SENSITIVE) + .equal( + ReactionUsersRecord_.accountIdAndNoteIdAndReaction, + ReactionUsersRecord.generateUniqueId(noteId, reaction), + QueryBuilder.StringOrder.CASE_SENSITIVE + ) .build() .subscribe() .toFlow().map { @@ -80,6 +82,4 @@ class ReactionUserDAO @Inject constructor( } } - - } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt index a3cdc90065..b5d499a86a 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt @@ -26,7 +26,7 @@ class ReactionUserRepositoryImpl @Inject constructor( private val dao: ReactionUserDAO, ) : ReactionUserRepository { - override suspend fun syncBy(noteId: Note.Id, reaction: String): Result = runCancellableCatching { + override suspend fun syncBy(noteId: Note.Id, reaction: String?): Result = runCancellableCatching { val account = accountRepository.get(noteId.accountId).getOrThrow() dao.remove(noteId, reaction) dao.createEmptyIfNotExists(noteId, reaction) @@ -67,7 +67,7 @@ class ReactionUserRepositoryImpl @Inject constructor( } @OptIn(ExperimentalCoroutinesApi::class) - override suspend fun observeBy(noteId: Note.Id, reaction: String): Flow> { + override suspend fun observeBy(noteId: Note.Id, reaction: String?): Flow> { return flow { dao.createEmptyIfNotExists(noteId, reaction) emit(accountRepository.get(noteId.accountId).getOrThrow()) @@ -78,7 +78,7 @@ class ReactionUserRepositoryImpl @Inject constructor( } } - override suspend fun findBy(noteId: Note.Id, reaction: String): Result> = runCancellableCatching { + override suspend fun findBy(noteId: Note.Id, reaction: String?): Result> = runCancellableCatching { val accountIds = dao.findBy(noteId, reaction)?.accountIds ?: emptyList() userDataSource.getIn(noteId.accountId, accountIds).getOrThrow() } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUsersRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUsersRecord.kt index 740f782872..c274ee4417 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUsersRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUsersRecord.kt @@ -25,8 +25,13 @@ data class ReactionUsersRecord( ) { companion object { - fun generateUniqueId(noteId: Note.Id, reaction: String): String { - return "${noteId.accountId}-${noteId.noteId}-$reaction" + fun generateUniqueId(noteId: Note.Id, reaction: String?): String { + return if (reaction == null) { + "${noteId.accountId}-${noteId.noteId}" + } else { + "${noteId.accountId}-${noteId.noteId}-$reaction" + } + } } } \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt index 616628bb69..e939cff91f 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt @@ -10,19 +10,19 @@ interface ReactionUserRepository { * @param reaction 同期する対象のリアクションでカスタム絵文字の場合は:emoji_type@.:あるいは:emoji_type@host:の形式で与えられる * @param noteId 同期する対象のNoteのId */ - suspend fun syncBy(noteId: Note.Id, reaction: String): Result + suspend fun syncBy(noteId: Note.Id, reaction: String? = null): Result /** * 内部的に保持しているリアクションしたユーザの履歴の状態をobserveする * @param reaction 同期する対象のリアクションでカスタム絵文字の場合は:emoji_type@.:あるいは:emoji_type@host:の形式で与えられる * @param noteId 同期する対象のNoteのId */ - suspend fun observeBy(noteId: Note.Id, reaction: String): Flow> + suspend fun observeBy(noteId: Note.Id, reaction: String? = null): Flow> /** * 内部的に保持しているリアクションしたユーザの履歴の現在の状態を参照する * @param reaction 同期する対象のリアクションでカスタム絵文字の場合は:emoji_type@.:あるいは:emoji_type@host:の形式で与えられる * @param noteId 同期する対象のNoteのId */ - suspend fun findBy(noteId: Note.Id, reaction: String): Result> + suspend fun findBy(noteId: Note.Id, reaction: String? = null): Result> } \ No newline at end of file From 87b9a167d75e39fc6fbbf1e832cb8630633ffca6 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:48:03 +0900 Subject: [PATCH 045/432] =?UTF-8?q?feat:=20=E5=85=A8=E3=81=A6=E3=81=AE?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=97=E3=81=AE=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E5=8F=96=E5=BE=97=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reaction/impl/ReactionUserRepositoryImpl.kt | 13 ++++++++++--- .../model/notes/reaction/ReactionUserRepository.kt | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt index b5d499a86a..d0c7a60523 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt @@ -44,7 +44,7 @@ class ReactionUserRepositoryImpl @Inject constructor( ).throwIfHasError().body() ) dao.appendAccountIds(noteId, reaction, reactions.map { it.user.id }) - } while (reactions.isNotEmpty()) + } while (reactions.isEmpty()) } Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { @@ -56,7 +56,14 @@ class ReactionUserRepositoryImpl @Inject constructor( val emojiReaction = resBody.emojiReactions?.firstOrNull { it.reaction == reaction } - val accountIds = emojiReaction?.accountIds?: emptyList() + val accountIds = if (reaction == null) { + emojiReaction?.accountIds + } else { + resBody.emojiReactions?.map { + it.accountIds + }?.flatten() + } ?: emptyList() + userRepository.syncIn(accountIds.map { User.Id(account.accountId, it) }) @@ -67,7 +74,7 @@ class ReactionUserRepositoryImpl @Inject constructor( } @OptIn(ExperimentalCoroutinesApi::class) - override suspend fun observeBy(noteId: Note.Id, reaction: String?): Flow> { + override fun observeBy(noteId: Note.Id, reaction: String?): Flow> { return flow { dao.createEmptyIfNotExists(noteId, reaction) emit(accountRepository.get(noteId.accountId).getOrThrow()) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt index e939cff91f..6f778ad602 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionUserRepository.kt @@ -17,7 +17,7 @@ interface ReactionUserRepository { * @param reaction 同期する対象のリアクションでカスタム絵文字の場合は:emoji_type@.:あるいは:emoji_type@host:の形式で与えられる * @param noteId 同期する対象のNoteのId */ - suspend fun observeBy(noteId: Note.Id, reaction: String? = null): Flow> + fun observeBy(noteId: Note.Id, reaction: String? = null): Flow> /** * 内部的に保持しているリアクションしたユーザの履歴の現在の状態を参照する From c305d858cbd8ac4f73e5c0c54a408e8bb1d37952 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:51:12 +0900 Subject: [PATCH 046/432] faet: objectbox --- modules/data/objectbox-models/default.json | 49 ++++++++++++++++++- .../data/objectbox-models/default.json.bak | 48 ++++++++++++++++-- 2 files changed, 92 insertions(+), 5 deletions(-) diff --git a/modules/data/objectbox-models/default.json b/modules/data/objectbox-models/default.json index ff836a5648..e9d3071665 100644 --- a/modules/data/objectbox-models/default.json +++ b/modules/data/objectbox-models/default.json @@ -316,10 +316,55 @@ "targetId": "1:4355718382021751829" } ] + }, + { + "id": "3:1672123969377209864", + "lastPropertyId": "6:7634221281760726222", + "name": "ReactionUsersRecord", + "properties": [ + { + "id": "1:676934433640553584", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:8038701529227730420", + "name": "accountId", + "indexId": "6:6489830746707304657", + "type": 6, + "flags": 8 + }, + { + "id": "3:2163943456333549192", + "name": "noteId", + "indexId": "7:7445768282858324655", + "type": 9, + "flags": 2048 + }, + { + "id": "4:2143810341282238420", + "name": "accountIdAndNoteIdAndReaction", + "indexId": "8:2235427975397612884", + "type": 9, + "flags": 2080 + }, + { + "id": "5:1292188942965914799", + "name": "reaction", + "type": 9 + }, + { + "id": "6:7634221281760726222", + "name": "accountIds", + "type": 30 + } + ], + "relations": [] } ], - "lastEntityId": "2:2221534449032746185", - "lastIndexId": "5:7310482946990627063", + "lastEntityId": "3:1672123969377209864", + "lastIndexId": "8:2235427975397612884", "lastRelationId": "2:5971800600708341253", "lastSequenceId": "0:0", "modelVersion": 5, diff --git a/modules/data/objectbox-models/default.json.bak b/modules/data/objectbox-models/default.json.bak index e3b0ef778e..ff836a5648 100644 --- a/modules/data/objectbox-models/default.json.bak +++ b/modules/data/objectbox-models/default.json.bak @@ -274,11 +274,53 @@ } ], "relations": [] + }, + { + "id": "2:2221534449032746185", + "lastPropertyId": "4:3861751215772392457", + "name": "ThreadRecord", + "properties": [ + { + "id": "1:6898596592219565017", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:2473069054411856439", + "name": "targetNoteId", + "type": 9 + }, + { + "id": "3:2251620119095233921", + "name": "accountId", + "type": 6 + }, + { + "id": "4:3861751215772392457", + "name": "targetNoteIdAndAccountId", + "indexId": "5:7310482946990627063", + "type": 9, + "flags": 2080 + } + ], + "relations": [ + { + "id": "1:5368363319508289421", + "name": "ancestors", + "targetId": "1:4355718382021751829" + }, + { + "id": "2:5971800600708341253", + "name": "descendants", + "targetId": "1:4355718382021751829" + } + ] } ], - "lastEntityId": "1:4355718382021751829", - "lastIndexId": "4:873220635513493863", - "lastRelationId": "0:0", + "lastEntityId": "2:2221534449032746185", + "lastIndexId": "5:7310482946990627063", + "lastRelationId": "2:5971800600708341253", "lastSequenceId": "0:0", "modelVersion": 5, "modelVersionParserMinimum": 5, From 169cee1b1867332a6ec5361a43f942254edaf216 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:56:11 +0900 Subject: [PATCH 047/432] =?UTF-8?q?fix:=20=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?=E9=80=86=E3=81=AB=E3=81=97=E3=81=A6=E3=81=84=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/reaction/impl/ReactionUserRepositoryImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt index d0c7a60523..dea63dbc4b 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt @@ -57,11 +57,11 @@ class ReactionUserRepositoryImpl @Inject constructor( it.reaction == reaction } val accountIds = if (reaction == null) { - emojiReaction?.accountIds - } else { resBody.emojiReactions?.map { it.accountIds }?.flatten() + } else { + emojiReaction?.accountIds } ?: emptyList() userRepository.syncIn(accountIds.map { From 626d4af0b50ba65d522becc1a342c2d317e0f4fc Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 14:56:22 +0900 Subject: [PATCH 048/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E4=B8=80=E8=A6=A7=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../history/ReactionHistoryViewModel.kt | 54 +++++++------------ 1 file changed, 19 insertions(+), 35 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryViewModel.kt index 92ef4f1b3c..dd5373e9dd 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryViewModel.kt @@ -9,7 +9,6 @@ import dagger.assisted.AssistedInject import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import net.pantasystem.milktea.common.Logger -import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common_android.emoji.V13EmojiUrlResolver import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository @@ -17,10 +16,7 @@ import net.pantasystem.milktea.model.emoji.Emoji import net.pantasystem.milktea.model.instance.MetaRepository import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.notes.NoteRepository -import net.pantasystem.milktea.model.notes.reaction.ReactionHistory -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryDataSource -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryPaginator -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryRequest +import net.pantasystem.milktea.model.notes.reaction.* import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserRepository import net.pantasystem.milktea.note.EmojiType @@ -28,13 +24,12 @@ import net.pantasystem.milktea.note.from class ReactionHistoryViewModel @AssistedInject constructor( - reactionHistoryDataSource: ReactionHistoryDataSource, - paginatorFactory: ReactionHistoryPaginator.Factory, - val loggerFactory: Logger.Factory, - val metaRepository: MetaRepository, - val accountRepository: AccountRepository, - val noteRepository: NoteRepository, - val userRepository: UserRepository, + loggerFactory: Logger.Factory, + private val metaRepository: MetaRepository, + private val accountRepository: AccountRepository, + noteRepository: NoteRepository, + private val userRepository: UserRepository, + private val reactionUserRepository: ReactionUserRepository, @Assisted val noteId: Note.Id, @Assisted val type: String? ) : ViewModel() { @@ -49,7 +44,9 @@ class ReactionHistoryViewModel @AssistedInject constructor( val logger = loggerFactory.create("ReactionHistoryVM") private val isLoading = MutableStateFlow(false) - private val histories = MutableStateFlow>(emptyList()) + + private val users = reactionUserRepository.observeBy(noteId, type) + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) @OptIn(ExperimentalCoroutinesApi::class) private val emojis = flowOf(noteId).mapNotNull { @@ -62,7 +59,6 @@ class ReactionHistoryViewModel @AssistedInject constructor( private val note = noteRepository.observeOne(noteId) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) - private val paginator = paginatorFactory.create(ReactionHistoryRequest(noteId, type)) @OptIn(FlowPreview::class) private val account = suspend { @@ -85,9 +81,9 @@ class ReactionHistoryViewModel @AssistedInject constructor( noteInfo, emojis, isLoading, - histories, + users, account, - ) { noteInfo, emojis, loading, histories, a -> + ) { noteInfo, emojis, loading, users, a -> ReactionHistoryUiState( items = listOfNotNull( type?.let { type -> @@ -111,8 +107,8 @@ class ReactionHistoryViewModel @AssistedInject constructor( ReactionHistoryListType.Header(it) } } - ) + histories.map { - ReactionHistoryListType.ItemUser(it.user, account = a) + ) + users.map { + ReactionHistoryListType.ItemUser(it, account = a) } + listOfNotNull( if (loading) { ReactionHistoryListType.Loading @@ -128,27 +124,15 @@ class ReactionHistoryViewModel @AssistedInject constructor( ) init { - reactionHistoryDataSource.filter(noteId, type).onEach { - histories.value = it - }.catch { - - }.launchIn(viewModelScope + Dispatchers.IO) + viewModelScope.launch { + isLoading.value = true + reactionUserRepository.syncBy(noteId, type) + isLoading.value = false + } } fun next() { - if (isLoading.value) { - return - } - isLoading.value = true - viewModelScope.launch { - runCancellableCatching { - paginator.next() - }.onFailure { - logger.error("リアクションの履歴の取得に失敗しました", e = it) - } - isLoading.value = false - } } } From da0d6e7df7a7143469487adffc8dfe8da172e190 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 15:00:49 +0900 Subject: [PATCH 049/432] =?UTF-8?q?rm:=20=E4=B8=8D=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9F=E5=87=A6=E7=90=86=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/di/module/ReactionModule.kt | 14 +-- .../impl/InMemoryReactionHistoryDataSource.kt | 62 ------------- .../impl/ReactionHistoryPaginatorImpl.kt | 90 ------------------- .../history/ReactionHistoryPagerDialog.kt | 10 --- .../reaction/ReactionHistoryDataSource.kt | 20 ----- .../reaction/ReactionHistoryPaginator.kt | 19 ---- 6 files changed, 1 insertion(+), 214 deletions(-) delete mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/InMemoryReactionHistoryDataSource.kt delete mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionHistoryPaginatorImpl.kt delete mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionHistoryDataSource.kt delete mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionHistoryPaginator.kt diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/ReactionModule.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/ReactionModule.kt index b2c7315b25..59c219d280 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/ReactionModule.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/ReactionModule.kt @@ -5,13 +5,9 @@ import dagger.Module import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import net.pantasystem.milktea.data.infrastructure.emoji.UserEmojiConfigRepositoryImpl -import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.InMemoryReactionHistoryDataSource -import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.ReactionHistoryPaginatorImpl import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.ReactionUserRepositoryImpl import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.history.ReactionHistoryRepositoryImpl import net.pantasystem.milktea.model.emoji.UserEmojiConfigRepository -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryDataSource -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryPaginator import net.pantasystem.milktea.model.notes.reaction.ReactionUserRepository import net.pantasystem.milktea.model.notes.reaction.history.ReactionHistoryRepository import javax.inject.Singleton @@ -19,15 +15,7 @@ import javax.inject.Singleton @Module @InstallIn(SingletonComponent::class) abstract class ReactionModule { - - @Binds - @Singleton - abstract fun bindReactionHistoryDataSource(ds: InMemoryReactionHistoryDataSource): ReactionHistoryDataSource - - @Binds - @Singleton - abstract fun bindReactionHistoryPaging(impl: ReactionHistoryPaginatorImpl.Factory): ReactionHistoryPaginator.Factory - + @Binds @Singleton abstract fun bindReactionHistoryRepository(impl: ReactionHistoryRepositoryImpl): ReactionHistoryRepository diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/InMemoryReactionHistoryDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/InMemoryReactionHistoryDataSource.kt deleted file mode 100644 index fff868c614..0000000000 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/InMemoryReactionHistoryDataSource.kt +++ /dev/null @@ -1,62 +0,0 @@ -package net.pantasystem.milktea.data.infrastructure.notes.reaction.impl - -import net.pantasystem.milktea.model.notes.Note -import net.pantasystem.milktea.model.notes.reaction.ReactionHistory -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryDataSource -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import javax.inject.Inject - -class InMemoryReactionHistoryDataSource @Inject constructor(): ReactionHistoryDataSource { - - private val lock = Mutex() - private val stateFlow = MutableStateFlow(mapOf()) - - override suspend fun add(reactionHistory: ReactionHistory) { - lock.withLock { - stateFlow.value = stateFlow.value.toMutableMap().also { - it[reactionHistory.id] = reactionHistory - } - } - } - - override suspend fun addAll(reactionHistories: List) { - lock.withLock { - stateFlow.value = stateFlow.value.toMutableMap().also { - it.putAll(reactionHistories.map { r -> - r.id to r - }) - } - } - } - - - override fun filter(noteId: Note.Id, type: String?): Flow> { - return stateFlow.map { - it.values.filter { history -> - history.noteId == noteId - && history.id.accountId == noteId.accountId - && (type == null || type == history.type) - }.sortedBy { history -> - history.id.reactionId - }.asReversed() - } - } - - override fun findAll(): Flow> { - return stateFlow.map { - it.values.toList() - } - } - - override suspend fun clear(noteId: Note.Id) { - lock.withLock { - stateFlow.value = stateFlow.value.filterNot { - it.value.noteId == noteId - } - } - } -} \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionHistoryPaginatorImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionHistoryPaginatorImpl.kt deleted file mode 100644 index 42396fc061..0000000000 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionHistoryPaginatorImpl.kt +++ /dev/null @@ -1,90 +0,0 @@ -package net.pantasystem.milktea.data.infrastructure.notes.reaction.impl - -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext -import net.pantasystem.milktea.api.misskey.notes.reaction.RequestReactionHistoryDTO -import net.pantasystem.milktea.common.throwIfHasError -import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider -import net.pantasystem.milktea.data.converters.UserDTOEntityConverter -import net.pantasystem.milktea.model.account.AccountRepository -import net.pantasystem.milktea.model.notes.reaction.ReactionHistory -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryDataSource -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryPaginator -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryRequest -import net.pantasystem.milktea.model.user.UserDataSource -import java.util.* -import javax.inject.Inject - -class ReactionHistoryPaginatorImpl( - override val reactionHistoryRequest: ReactionHistoryRequest, - private val reactionHistoryDataSource: ReactionHistoryDataSource, - private val misskeyAPIProvider: MisskeyAPIProvider, - private val accountRepository: AccountRepository, - private val userDataSource: UserDataSource, - private val userDTOEntityConverter: UserDTOEntityConverter, -) : ReactionHistoryPaginator { - - class Factory @Inject constructor( - private val reactionHistoryDataSource: ReactionHistoryDataSource, - private val misskeyAPIProvider: MisskeyAPIProvider, - private val accountRepository: AccountRepository, - private val userDataSource: UserDataSource, - private val userDTOEntityConverter: UserDTOEntityConverter, - ) : ReactionHistoryPaginator.Factory { - override fun create(reactionHistoryRequest: ReactionHistoryRequest) : ReactionHistoryPaginator { - return ReactionHistoryPaginatorImpl( - reactionHistoryRequest, - reactionHistoryDataSource, - misskeyAPIProvider, - accountRepository, - userDataSource, - userDTOEntityConverter, - ) - } - } - - val limit: Int = 20 - - val lock = Mutex() - - private var offset: Int = 0 - - override suspend fun next(): Boolean { - return withContext(Dispatchers.IO) { - lock.withLock { - - val account = accountRepository.get(reactionHistoryRequest.noteId.accountId).getOrThrow() - val misskeyAPI = misskeyAPIProvider.get(account.normalizedInstanceUri) - val res = misskeyAPI.reactions( - RequestReactionHistoryDTO( - i = account.token, - offset = offset, - limit = limit, - noteId = reactionHistoryRequest.noteId.noteId, - type = reactionHistoryRequest.type - ) - ).throwIfHasError().body()?: emptyList() - - if(res.isNotEmpty()) { - offset += res.size - } - val reactionHistories = res.map { - val user = userDTOEntityConverter.convert(account, it.user) - userDataSource.add(user) - ReactionHistory( - ReactionHistory.Id(it.id, account.accountId), - reactionHistoryRequest.noteId, - Date(it.createdAt.toEpochMilliseconds()), - user, - it.type - ) - } - reactionHistoryDataSource.addAll(reactionHistories) - reactionHistories.isNotEmpty() - } - } - - } -} \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryPagerDialog.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryPagerDialog.kt index 9b1dbd3d1d..68182d2e02 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryPagerDialog.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryPagerDialog.kt @@ -20,13 +20,11 @@ import kotlinx.coroutines.withContext import net.pantasystem.milktea.common_android.ui.text.CustomEmojiDecorator import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.notes.reaction.Reaction -import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryDataSource import net.pantasystem.milktea.model.notes.reaction.ReactionHistoryRequest import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.DialogReactionHistoryPagerBinding import net.pantasystem.milktea.note.reaction.viewmodel.ReactionHistoryPagerUiState import net.pantasystem.milktea.note.reaction.viewmodel.ReactionHistoryPagerViewModel -import javax.inject.Inject @AndroidEntryPoint class ReactionHistoryPagerDialog : BottomSheetDialogFragment() { @@ -55,8 +53,6 @@ class ReactionHistoryPagerDialog : BottomSheetDialogFragment() { private val pagerViewModel by viewModels() - @Inject - internal lateinit var reactionHistoryDataSource: ReactionHistoryDataSource private val aId: Long by lazy(LazyThreadSafetyMode.NONE) { requireArguments().getLong(EXTRA_ACCOUNT_ID, -1).apply { @@ -179,10 +175,4 @@ class ReactionHistoryPagerDialog : BottomSheetDialogFragment() { dismissAllowingStateLoss() } - override fun onDestroy() { - super.onDestroy() - requireActivity().lifecycleScope.launch(Dispatchers.IO) { - reactionHistoryDataSource.clear(noteId) - } - } } \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionHistoryDataSource.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionHistoryDataSource.kt deleted file mode 100644 index 423e551f38..0000000000 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionHistoryDataSource.kt +++ /dev/null @@ -1,20 +0,0 @@ -package net.pantasystem.milktea.model.notes.reaction - -import kotlinx.coroutines.flow.Flow -import net.pantasystem.milktea.model.notes.Note - - -interface ReactionHistoryDataSource { - - fun findAll(): Flow> - - fun filter(noteId: Note.Id, type: String? = null): Flow> - - - - suspend fun add(reactionHistory: ReactionHistory) - - suspend fun addAll(reactionHistories: List) - - suspend fun clear(noteId: Note.Id) -} \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionHistoryPaginator.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionHistoryPaginator.kt deleted file mode 100644 index d625f3c06c..0000000000 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ReactionHistoryPaginator.kt +++ /dev/null @@ -1,19 +0,0 @@ -package net.pantasystem.milktea.model.notes.reaction - -/** - * リアクションの履歴をページネーションするためのインターフェース - * 読み込んだデータはReactionHistoryDataSourceへ注入する - */ -interface ReactionHistoryPaginator { - - interface Factory { - fun create(reactionHistoryRequest: ReactionHistoryRequest): ReactionHistoryPaginator - } - - val reactionHistoryRequest: ReactionHistoryRequest - - /** - * 次のリアクションの履歴を取得します - */ - suspend fun next(): Boolean -} \ No newline at end of file From e9c1606c6de59b9bac9789c7e1837df61628b45c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 15:22:50 +0900 Subject: [PATCH 050/432] =?UTF-8?q?fix:=20=E6=9D=A1=E4=BB=B6=E5=BC=8F?= =?UTF-8?q?=E3=81=AE=E8=AA=A4=E3=82=8A=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/ReactionUserRepositoryImpl.kt | 100 ++++++++++-------- 1 file changed, 57 insertions(+), 43 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt index dea63dbc4b..5166d6d186 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/ReactionUserRepositoryImpl.kt @@ -8,6 +8,7 @@ import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common.throwIfHasError import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider +import net.pantasystem.milktea.data.converters.UserDTOEntityConverter import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.notes.Note @@ -23,55 +24,67 @@ class ReactionUserRepositoryImpl @Inject constructor( private val mastodonAPIProvider: MastodonAPIProvider, private val userRepository: UserRepository, private val userDataSource: UserDataSource, + private val userDTOEntityConverter: UserDTOEntityConverter, private val dao: ReactionUserDAO, ) : ReactionUserRepository { - override suspend fun syncBy(noteId: Note.Id, reaction: String?): Result = runCancellableCatching { - val account = accountRepository.get(noteId.accountId).getOrThrow() - dao.remove(noteId, reaction) - dao.createEmptyIfNotExists(noteId, reaction) - when(account.instanceType) { - Account.InstanceType.MISSKEY -> { - var reactions: List - do { - reactions = requireNotNull( - misskeyAPIProvider.get(account).reactions( - RequestReactionHistoryDTO( - i = account.token, - noteId = noteId.noteId, - type = reaction, + override suspend fun syncBy(noteId: Note.Id, reaction: String?): Result = + runCancellableCatching { + val account = accountRepository.get(noteId.accountId).getOrThrow() + dao.remove(noteId, reaction) + dao.createEmptyIfNotExists(noteId, reaction) + when (account.instanceType) { + Account.InstanceType.MISSKEY -> { + var reactions: List + var offset = 0 + do { + reactions = requireNotNull( + misskeyAPIProvider.get(account).reactions( + RequestReactionHistoryDTO( + i = account.token, + noteId = noteId.noteId, + type = reaction, + offset = offset, + limit = 10 + ) + ).throwIfHasError().body() + ) + offset += reactions.size + dao.appendAccountIds(noteId, reaction, reactions.map { it.user.id }) + userDataSource.addAll(reactions.map { + userDTOEntityConverter.convert( + account, + it.user ) - ).throwIfHasError().body() + }) + } while (reactions.size >= 10) + + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + val resBody = requireNotNull( + mastodonAPIProvider.get(account).getStatus(noteId.noteId) + .throwIfHasError() + .body() ) - dao.appendAccountIds(noteId, reaction, reactions.map { it.user.id }) - } while (reactions.isEmpty()) + val emojiReaction = resBody.emojiReactions?.firstOrNull { + it.reaction == reaction + } + val accountIds = if (reaction == null) { + resBody.emojiReactions?.map { + it.accountIds + }?.flatten() + } else { + emojiReaction?.accountIds + } ?: emptyList() - } - Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { - val resBody = requireNotNull( - mastodonAPIProvider.get(account).getStatus(noteId.noteId) - .throwIfHasError() - .body() - ) - val emojiReaction = resBody.emojiReactions?.firstOrNull { - it.reaction == reaction + userRepository.syncIn(accountIds.map { + User.Id(account.accountId, it) + }) + dao.update(noteId, reaction, accountIds) } - val accountIds = if (reaction == null) { - resBody.emojiReactions?.map { - it.accountIds - }?.flatten() - } else { - emojiReaction?.accountIds - } ?: emptyList() - userRepository.syncIn(accountIds.map { - User.Id(account.accountId, it) - }) - dao.update(noteId, reaction, accountIds) } - } - } @OptIn(ExperimentalCoroutinesApi::class) override fun observeBy(noteId: Note.Id, reaction: String?): Flow> { @@ -85,9 +98,10 @@ class ReactionUserRepositoryImpl @Inject constructor( } } - override suspend fun findBy(noteId: Note.Id, reaction: String?): Result> = runCancellableCatching { - val accountIds = dao.findBy(noteId, reaction)?.accountIds ?: emptyList() - userDataSource.getIn(noteId.accountId, accountIds).getOrThrow() - } + override suspend fun findBy(noteId: Note.Id, reaction: String?): Result> = + runCancellableCatching { + val accountIds = dao.findBy(noteId, reaction)?.accountIds ?: emptyList() + userDataSource.getIn(noteId.accountId, accountIds).getOrThrow() + } } \ No newline at end of file From e2aa48efc39e59b4edffdebb8fa13e447442ae7b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 24 Apr 2023 15:23:01 +0900 Subject: [PATCH 051/432] =?UTF-8?q?feat:=20=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AA=E3=83=B3=E3=82=B0=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/reaction/history/ReactionHistoryViewModel.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryViewModel.kt index dd5373e9dd..dc62cb7e51 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/history/ReactionHistoryViewModel.kt @@ -126,7 +126,9 @@ class ReactionHistoryViewModel @AssistedInject constructor( init { viewModelScope.launch { isLoading.value = true - reactionUserRepository.syncBy(noteId, type) + reactionUserRepository.syncBy(noteId, type).onFailure { + logger.error("リアクション履歴の同期に失敗", it) + } isLoading.value = false } } From 25af95b11236d0dcb6280766618f1b8a0bbbba2b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 30 Apr 2023 13:01:32 +0900 Subject: [PATCH 052/432] =?UTF-8?q?feat:=20reblog(=E3=83=96=E3=83=BC?= =?UTF-8?q?=E3=82=B9=E3=83=88=EF=BC=89=E3=81=AE=E6=A7=8B=E9=80=A0=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=8F=BE=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/model/notes/renote/Renotes.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt index 9f4942d1c5..e9de2d1569 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt @@ -1,14 +1,18 @@ package net.pantasystem.milktea.model.notes.renote import net.pantasystem.milktea.model.notes.Note +import net.pantasystem.milktea.model.user.User sealed interface Renote { - val noteId: Note.Id data class Quote( - override val noteId: Note.Id + val noteId: Note.Id ) : Renote data class Normal( - override val noteId: Note.Id + val noteId: Note.Id + ) : Renote + + data class Reblog( + val userId: User.Id ) : Renote } From 232a453badf0ec128f64b5b9743809be4c43eb1a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 30 Apr 2023 13:01:36 +0900 Subject: [PATCH 053/432] =?UTF-8?q?feat:=20reblog(=E3=83=96=E3=83=BC?= =?UTF-8?q?=E3=82=B9=E3=83=88=EF=BC=89=E3=81=AE=E6=A7=8B=E9=80=A0=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=8F=BE=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/renote/item_renote_user.kt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/item_renote_user.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/item_renote_user.kt index 6412eecd3b..bcf6c35792 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/item_renote_user.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/item_renote_user.kt @@ -23,21 +23,22 @@ import coil.compose.rememberAsyncImagePainter import kotlinx.coroutines.ExperimentalCoroutinesApi import net.pantasystem.milktea.common_compose.CustomEmojiText import net.pantasystem.milktea.common_compose.getSimpleElapsedTime -import net.pantasystem.milktea.model.notes.NoteRelation import net.pantasystem.milktea.model.user.User @ExperimentalCoroutinesApi @Composable @Stable fun ItemRenoteUser( - note: NoteRelation, + note: RenoteItemType, myId: User.Id?, accountHost: String?, onAction: (ItemRenoteAction) -> Unit, isUserNameDefault: Boolean = false ) { - val createdAt = getSimpleElapsedTime(time = note.note.createdAt) + val createdAt = (note as? RenoteItemType.Renote)?.let { + getSimpleElapsedTime(time = it.note.note.createdAt) + } Card( shape = RoundedCornerShape(0.dp), @@ -96,7 +97,9 @@ fun ItemRenoteUser( } } Column { - Text(createdAt) + if (createdAt != null) { + Text(createdAt) + } if (note.user.id == myId) { IconButton(onClick = { onAction(ItemRenoteAction.OnDeleteButtonClicked(note)) @@ -113,7 +116,7 @@ fun ItemRenoteUser( sealed interface ItemRenoteAction { - data class OnClick(val note: NoteRelation) : ItemRenoteAction - data class OnDeleteButtonClicked(val note: NoteRelation): ItemRenoteAction + data class OnClick(val note: RenoteItemType) : ItemRenoteAction + data class OnDeleteButtonClicked(val note: RenoteItemType): ItemRenoteAction } From 571ec2ceccb73caa31c2a6373c8972f7205aed91 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 30 Apr 2023 13:13:25 +0900 Subject: [PATCH 054/432] =?UTF-8?q?feat:=20RenoteItemType=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/renote/RenotesViewModel.kt | 67 +++++++++++++------ .../milktea/note/renote/renote_users.kt | 13 ++-- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt index 2eab8645cf..1f531d0832 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt @@ -10,19 +10,20 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.Logger -import net.pantasystem.milktea.common.PageableState import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.model.notes.* import net.pantasystem.milktea.model.notes.renote.Renote import net.pantasystem.milktea.model.notes.renote.RenotesPagingService import net.pantasystem.milktea.model.user.User +import net.pantasystem.milktea.model.user.UserRepository class RenotesViewModel @AssistedInject constructor( private val renotesPagingServiceFactory: RenotesPagingService.Factory, private val noteGetter: NoteRelationGetter, private val noteRepository: NoteRepository, private val noteCaptureAPIAdapter: NoteCaptureAPIAdapter, + private val userRepository: UserRepository, accountStore: AccountStore, loggerFactory: Logger.Factory, @Assisted val noteId: Note.Id, @@ -42,11 +43,19 @@ class RenotesViewModel @AssistedInject constructor( private val logger = loggerFactory.create("RenotesVM") - val renotes = renotesPagingService.state.map { - it.convert { list -> - list.filterIsInstance() + val renotes = renotesPagingService.state.map { state -> + state.suspendConvert { renotes -> + renotes.mapNotNull {renote -> + when(renote) { + is Renote.Normal -> noteGetter.get(renote.noteId).getOrNull()?.let { + RenoteItemType.Renote(it) + } + is Renote.Quote -> null + is Renote.Reblog -> RenoteItemType.Reblog(userRepository.find(renote.userId)) + } + } } - }.asNoteRelation() + } val myId = accountStore.observeCurrentAccount.map { it?.let { @@ -63,8 +72,10 @@ class RenotesViewModel @AssistedInject constructor( renotesPagingService.state.mapNotNull { (it.content as? StateContent.Exist)?.rawContent }.map { renotes -> - renotes.map { - noteCaptureAPIAdapter.capture(it.noteId) + renotes.mapNotNull { renote -> + (renote as? Renote.Normal)?.let { + noteCaptureAPIAdapter.capture(it.noteId) + } } }.map { flows -> combine(flows) { @@ -96,25 +107,28 @@ class RenotesViewModel @AssistedInject constructor( } } - fun delete(noteId: Note.Id) { + fun delete(item: RenoteItemType) { viewModelScope.launch { - noteRepository.delete(noteId).onFailure { - _errors.value = it - }.onSuccess { - refresh() - } - } - } - - private fun Flow>>.asNoteRelation(): Flow>> { - return this.map { pageable -> - pageable.suspendConvert { list -> - list.mapNotNull { - noteGetter.get(it.noteId).getOrNull() + when(item) { + is RenoteItemType.Reblog -> { + noteRepository.unrenote(noteId).onFailure { + _errors.value = it + }.onSuccess { + refresh() + } + } + is RenoteItemType.Renote -> { + noteRepository.delete(item.note.note.id).onFailure { + _errors.value = it + }.onSuccess { + refresh() + } } } + } } + } fun RenotesViewModel.Companion.provideViewModel( @@ -125,4 +139,15 @@ fun RenotesViewModel.Companion.provideViewModel( override fun create(modelClass: Class): T { return factory.create(noteId) as T } +} + +sealed interface RenoteItemType { + + val user: User + data class Renote(val note: NoteRelation) : RenoteItemType { + override val user: User + get() = note.user + } + + data class Reblog(override val user: User) : RenoteItemType } \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/renote_users.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/renote_users.kt index 773605013d..e58d1c15f7 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/renote_users.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/renote_users.kt @@ -22,10 +22,10 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapNotNull import net.pantasystem.milktea.common.PageableState import net.pantasystem.milktea.common.StateContent -import net.pantasystem.milktea.model.notes.NoteRelation import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.note.renote.ItemRenoteAction import net.pantasystem.milktea.note.renote.ItemRenoteUser +import net.pantasystem.milktea.note.renote.RenoteItemType import net.pantasystem.milktea.note.renote.RenotesViewModel @@ -33,14 +33,14 @@ import net.pantasystem.milktea.note.renote.RenotesViewModel @Composable fun RenoteUsersScreen( renotesViewModel: RenotesViewModel, - onSelected: (NoteRelation) -> Unit, + onSelected: (RenoteItemType) -> Unit, onScrollState: (Boolean) -> Unit, ) { val myId by renotesViewModel.myId.collectAsState() val account by renotesViewModel.account.collectAsState() - val renotes: PageableState> by renotesViewModel.renotes.asLiveData() + val renotes: PageableState> by renotesViewModel.renotes.asLiveData() .observeAsState( initial = PageableState.Fixed( StateContent.NotExist() @@ -61,7 +61,7 @@ fun RenoteUsersScreen( onSelected(it.note) } is ItemRenoteAction.OnDeleteButtonClicked -> { - renotesViewModel.delete(it.note.note.id) + renotesViewModel.delete(it.note) } } }, @@ -100,7 +100,7 @@ fun RenoteUsersScreen( @ExperimentalCoroutinesApi @Composable fun RenoteUserList( - notes: List, + notes: List, myId: User.Id?, accountHost: String?, onAction: (ItemRenoteAction) -> Unit, @@ -135,9 +135,6 @@ fun RenoteUserList( rememberNestedScrollInteropConnection())) { this.items( notes.size, - key = { - notes[it].note.id - } ) { pos -> ItemRenoteUser( note = notes[pos], From a33da83dd8f8afc255b1cb95a64718acc8e7341c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 30 Apr 2023 13:41:41 +0900 Subject: [PATCH 055/432] =?UTF-8?q?feat:=20reblog=E3=81=97=E3=81=9F?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=82=92=E5=8F=96=E5=BE=97=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api/mastodon/MastodonAPI.kt | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt index d6b88b461a..5cd8a0c653 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt @@ -78,7 +78,7 @@ interface MastodonAPI { suspend fun getHomeTimeline( @Query("min_id") minId: String? = null, @Query("max_id") maxId: String? = null, - @Query("visibilities[]", encoded = true) visibilities: List? = null + @Query("visibilities[]", encoded = true) visibilities: List? = null, ): Response> @GET("api/v1/timelines/list/{listId}") @@ -100,7 +100,7 @@ interface MastodonAPI { @Query("min_id") minId: String? = null, @Query("max_id") maxId: String? = null, @Query("since_id") sinceId: String? = null, - @Query("limit") limit: Int = 40 + @Query("limit") limit: Int = 40, ): Response> @GET("api/v1/accounts/{accountId}/following") @@ -109,7 +109,7 @@ interface MastodonAPI { @Query("min_id") minId: String? = null, @Query("max_id") maxId: String? = null, @Query("since_id") sinceId: String? = null, - @Query("limit") limit: Int = 40 + @Query("limit") limit: Int = 40, ): Response> @GET("api/v1/accounts/{accountId}") @@ -120,7 +120,7 @@ interface MastodonAPI { @Query( "id[]", encoded = true - ) ids: List + ) ids: List, ): Response> @POST("api/v1/accounts/{accountId}/follow") @@ -132,13 +132,13 @@ interface MastodonAPI { @PUT("api/v1/statuses/{statusId}/emoji_reactions/{emoji}") suspend fun reaction( @Path("statusId") statusId: String, - @Path("emoji") emoji: String + @Path("emoji") emoji: String, ): Response @DELETE("api/v1/statuses/{statusId}/emoji_reactions/{emoji}") suspend fun deleteReaction( @Path("statusId") statusId: String, - @Path("emoji") emoji: String + @Path("emoji") emoji: String, ): Response @POST("api/v1/statuses/{statusId}/emoji_unreaction") @@ -159,13 +159,13 @@ interface MastodonAPI { @GET("api/v1/favourites") suspend fun getFavouriteStatuses( @Query("min_id") minId: String? = null, - @Query("max_id") maxId: String? = null + @Query("max_id") maxId: String? = null, ): Response> @POST("api/v1/accounts/{accountId}/mute") suspend fun muteAccount( @Path("accountId") accountId: String, - @Body body: MuteAccountRequest + @Body body: MuteAccountRequest, ): Response @POST("api/v1/accounts/{accountId}/unmute") @@ -191,7 +191,7 @@ interface MastodonAPI { @POST("api/v1/statuses") suspend fun createStatus( - @Body body: CreateStatus + @Body body: CreateStatus, ): Response @POST("api/v1/status") @@ -209,7 +209,7 @@ interface MastodonAPI { @POST("api/v1/polls/{pollId}/votes") suspend fun voteOnPoll( @Path("pollId") pollId: String, - @Field("choices[]", encoded = true) choices: List + @Field("choices[]", encoded = true) choices: List, ): Response @POST("api/v1/statuses/{statusId}/mute") @@ -245,23 +245,32 @@ interface MastodonAPI { suspend fun getList(@Path("listId") listId: String): Response @POST("api/v1/lists/{listId}/accounts") - suspend fun addAccountsToList(@Path("listId") listId: String, @Body body: AddAccountsToList): Response + suspend fun addAccountsToList( + @Path("listId") listId: String, + @Body body: AddAccountsToList, + ): Response @DELETE("api/v1/lists/{listId}/accounts") - suspend fun removeAccountsFromList(@Path("listId") listId: String, @Body body: RemoveAccountsFromList): Response + suspend fun removeAccountsFromList( + @Path("listId") listId: String, + @Body body: RemoveAccountsFromList, + ): Response @GET("api/v1/lists/{listId}") suspend fun getAccountsInList( @Path("listId") listId: String, @Query("max_id") maxId: String? = null, - @Query("min_id") minId: String? = null + @Query("min_id") minId: String? = null, ): Response> @GET("api/v1/statuses/{statusId}/context") suspend fun getStatusesContext(@Path("statusId") statusId: String): Response @PUT("api/v1/media/{mediaId}") - suspend fun updateMediaAttachment(@Path("mediaId") mediaId: String, @Body body: UpdateMediaAttachment): Response + suspend fun updateMediaAttachment( + @Path("mediaId") mediaId: String, + @Body body: UpdateMediaAttachment, + ): Response @GET("api/v2/search") suspend fun search( @@ -273,7 +282,7 @@ interface MastodonAPI { @Query("max_id") maxId: String? = null, @Query("min_id") minId: String? = null, @Query("limit") limit: Int? = null, - @Query("offset") offset: Int? = null + @Query("offset") offset: Int? = null, ): Response @POST("api/v1/follow_requests/{accountId}/authorize") @@ -283,16 +292,19 @@ interface MastodonAPI { suspend fun rejectFollowRequest(@Path("accountId") accountId: String): Response @GET("api/v1/follow_requests") - suspend fun getFollowRequests(@Query("max_id") maxId: String? = null, @Query("min_id") minId: String? = null): Response> + suspend fun getFollowRequests( + @Query("max_id") maxId: String? = null, + @Query("min_id") minId: String? = null, + ): Response> @GET("api/v1/markers") suspend fun getMarkers( - @Query("timeline[]", encoded = true) timeline: List + @Query("timeline[]", encoded = true) timeline: List, ): Response @POST("api/v1/markers") suspend fun saveMarkers( - @Body markers: SaveMarkersRequest + @Body markers: SaveMarkersRequest, ): Response @GET("api/v1/filters") @@ -303,4 +315,12 @@ interface MastodonAPI { @GET("api/v1/instance/rules") suspend fun getRules(): Response> + + @GET("api/v1/statuses/{id}/reblogged_by") + suspend fun getRebloggedBy( + @Path("id") id: String, + @Query("max_id") maxId: String? = null, + @Query("since_id") sinceId: String? = null, + @Query("min_id") minId: String? = null, + ): Response> } \ No newline at end of file From 6f5d1a1f2861241925110b32891ed3a44196f895 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 30 Apr 2023 13:41:56 +0900 Subject: [PATCH 056/432] =?UTF-8?q?feat:=20=E5=BC=95=E7=94=A8=E3=81=AE?= =?UTF-8?q?=E6=A7=8B=E9=80=A0=E3=82=92=E5=89=8A=E9=99=A4=E3=81=97=E3=83=95?= =?UTF-8?q?=E3=83=A9=E3=82=B0=E3=81=A8=E3=81=97=E3=81=A6=E8=A1=A8=E7=8F=BE?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/model/notes/renote/Renotes.kt | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt index e9de2d1569..beada9b57f 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt @@ -3,16 +3,13 @@ package net.pantasystem.milktea.model.notes.renote import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.user.User -sealed interface Renote { - data class Quote( - val noteId: Note.Id - ) : Renote - - data class Normal( - val noteId: Note.Id - ) : Renote +sealed interface RenoteType { + data class Renote( + val noteId: Note.Id, + val isQuote: Boolean + ) : RenoteType data class Reblog( val userId: User.Id - ) : Renote + ) : RenoteType } From 071cbce60ed137f7d73adfe8be398965d8cead90 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 30 Apr 2023 13:42:19 +0900 Subject: [PATCH 057/432] =?UTF-8?q?feat:=20mastodon=E3=81=AE=E3=83=AA?= =?UTF-8?q?=E3=83=96=E3=83=AD=E3=82=B0=E3=81=97=E3=81=9F=E3=83=A6=E3=83=BC?= =?UTF-8?q?=E3=82=B6=E3=82=92=E6=89=B1=E3=81=88=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/renote/RenotesPagingService.kt | 109 +++++++++++++----- .../notes/renote/RenotesPagingService.kt | 2 +- 2 files changed, 83 insertions(+), 28 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/renote/RenotesPagingService.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/renote/RenotesPagingService.kt index a67014be62..8e632e9b8b 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/renote/RenotesPagingService.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/renote/RenotesPagingService.kt @@ -4,54 +4,62 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import net.pantasystem.milktea.api.mastodon.accounts.MastodonAccountDTO import net.pantasystem.milktea.api.misskey.notes.FindRenotes import net.pantasystem.milktea.api.misskey.notes.NoteDTO -import net.pantasystem.milktea.common.PageableState -import net.pantasystem.milktea.common.StateContent +import net.pantasystem.milktea.common.* import net.pantasystem.milktea.common.paginator.* -import net.pantasystem.milktea.common.runCancellableCatching -import net.pantasystem.milktea.common.throwIfHasError +import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.infrastructure.notes.NoteDataSourceAdder +import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.notes.Note -import net.pantasystem.milktea.model.notes.renote.Renote +import net.pantasystem.milktea.model.notes.renote.RenoteType import net.pantasystem.milktea.model.notes.renote.RenotesPagingService +import net.pantasystem.milktea.model.user.UserDataSource import javax.inject.Inject class RenotesPagingServiceImpl( targetNoteId: Note.Id, val misskeyAPIProvider: MisskeyAPIProvider, + val mastodonAPIProvider: MastodonAPIProvider, val accountRepository: AccountRepository, val noteDataSourceAdder: NoteDataSourceAdder, - + val userDataSource: UserDataSource, ) : RenotesPagingService { class Factory @Inject constructor( val misskeyAPIProvider: MisskeyAPIProvider, val accountRepository: AccountRepository, val noteDataSourceAdder: NoteDataSourceAdder, + val mastodonAPIProvider: MastodonAPIProvider, + val userDataSource: UserDataSource, ) : RenotesPagingService.Factory { override fun create(noteId: Note.Id): RenotesPagingService { return RenotesPagingServiceImpl( noteId, misskeyAPIProvider, + mastodonAPIProvider, accountRepository, noteDataSourceAdder, + userDataSource, ) } } private val pagingImpl = RenotesPagingImpl( targetNoteId, misskeyAPIProvider, + mastodonAPIProvider, accountRepository, noteDataSourceAdder, + userDataSource ) private val controller = PreviousPagingController(pagingImpl, pagingImpl, pagingImpl, pagingImpl) - override val state: Flow>> + override val state: Flow>> get() = pagingImpl.state override suspend fun clear() { @@ -73,61 +81,108 @@ class RenotesPagingServiceImpl( class RenotesPagingImpl( private val targetNoteId: Note.Id, val misskeyAPIProvider: MisskeyAPIProvider, + val mastodonAPIProvider: MastodonAPIProvider, val accountRepository: AccountRepository, val noteDataSourceAdder: NoteDataSourceAdder, -) : PreviousLoader, - EntityConverter, + val userDataSource: UserDataSource, +) : PreviousLoader, + EntityConverter, StateLocker, - PaginationState, + PaginationState, IdGetter { - private val _state: MutableStateFlow>> = + private val _state: MutableStateFlow>> = MutableStateFlow(PageableState.Fixed(StateContent.NotExist())) - override val state: Flow>> + override val state: Flow>> get() = _state override val mutex: Mutex = Mutex() - override suspend fun loadPrevious(): Result> { + private var maxId: String? = null + private var minId: String? = null + + override suspend fun loadPrevious(): Result> { return runCancellableCatching { val account = accountRepository.get(targetNoteId.accountId).getOrThrow() val i = account.token + when(account.instanceType) { + Account.InstanceType.MISSKEY -> { + misskeyAPIProvider.get(account.normalizedInstanceUri) + .renotes(FindRenotes(i = i, noteId = targetNoteId.noteId, untilId = getUntilId())) + .throwIfHasError().body()!!.let { list -> + list.map { + RenoteNetworkDTO.Renote(it) + } + } + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + val untilId = getUntilId() + val empty = (getState().content as? StateContent.Exist)?.rawContent.isNullOrEmpty() + if (untilId == null && !empty) { + return@runCancellableCatching emptyList() + } + val res = mastodonAPIProvider.get(account) + .getRebloggedBy(targetNoteId.noteId, maxId = getUntilId()) + .throwIfHasError() + MastodonLinkHeaderDecoder(res.headers()["Link"]).let { + maxId = it.getMaxId() + } + res.body()!!.let { list -> + list.map { + RenoteNetworkDTO.Reblog(it) + } + } + } + } - misskeyAPIProvider.get(account.normalizedInstanceUri) - .renotes(FindRenotes(i = i, noteId = targetNoteId.noteId, untilId = getUntilId())) - .throwIfHasError().body()!! } } - override suspend fun convertAll(list: List): List { + override suspend fun convertAll(list: List): List { val account = accountRepository.get(targetNoteId.accountId).getOrThrow() return list.map { - noteDataSourceAdder.addNoteDtoToDataSource(account, it) - }.map { - if (it.isQuote()) { - Renote.Quote(it.id) - } else { - Renote.Normal(it.id) + when(it) { + is RenoteNetworkDTO.Reblog -> { + val model = it.accountDTO.toModel(account = account) + userDataSource.add(model).getOrThrow() + RenoteType.Reblog(model.id) + } + is RenoteNetworkDTO.Renote -> { + val note = noteDataSourceAdder.addNoteDtoToDataSource(account, it.note) + RenoteType.Renote(note.id, isQuote = note.isQuote()) + } } } } override suspend fun getSinceId(): String? { - return (getState().content as? StateContent.Exist)?.rawContent?.firstOrNull()?.noteId?.noteId + if (minId != null) { + return maxId + } + return ((getState().content as? StateContent.Exist)?.rawContent?.firstOrNull() as? RenoteType.Renote)?.noteId?.noteId } override suspend fun getUntilId(): String? { - return (getState().content as? StateContent.Exist)?.rawContent?.lastOrNull()?.noteId?.noteId + if (maxId != null) { + return maxId + } + return ((getState().content as? StateContent.Exist)?.rawContent?.lastOrNull() as? RenoteType.Renote)?.noteId?.noteId } - override fun getState(): PageableState> { + override fun getState(): PageableState> { return _state.value } - override fun setState(state: PageableState>) { + override fun setState(state: PageableState>) { _state.value = state } } + +sealed interface RenoteNetworkDTO { + data class Renote(val note: NoteDTO) : RenoteNetworkDTO + + data class Reblog(val accountDTO: MastodonAccountDTO) : RenoteNetworkDTO +} \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/RenotesPagingService.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/RenotesPagingService.kt index f5c9ab5f70..8f0437d753 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/RenotesPagingService.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/RenotesPagingService.kt @@ -10,7 +10,7 @@ interface RenotesPagingService { fun create(noteId: Note.Id): RenotesPagingService } - val state: Flow>> + val state: Flow>> suspend fun next() suspend fun refresh() suspend fun clear() From 5f5a2f29ccbcd6783c7c9fd48ea6de3f5c9f4789 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 30 Apr 2023 13:42:37 +0900 Subject: [PATCH 058/432] =?UTF-8?q?feat:=20=E5=90=8D=E5=89=8D=E3=81=AE?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=82=92=E5=8F=8D=E6=98=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/renote/RenotesViewModel.kt | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt index 1f531d0832..3c5b666623 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt @@ -13,7 +13,7 @@ import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.model.notes.* -import net.pantasystem.milktea.model.notes.renote.Renote +import net.pantasystem.milktea.model.notes.renote.RenoteType import net.pantasystem.milktea.model.notes.renote.RenotesPagingService import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserRepository @@ -47,11 +47,14 @@ class RenotesViewModel @AssistedInject constructor( state.suspendConvert { renotes -> renotes.mapNotNull {renote -> when(renote) { - is Renote.Normal -> noteGetter.get(renote.noteId).getOrNull()?.let { - RenoteItemType.Renote(it) + is RenoteType.Renote -> if (renote.isQuote) { + null + } else { + noteGetter.get(renote.noteId).getOrNull()?.let { + RenoteItemType.Renote(it) + } } - is Renote.Quote -> null - is Renote.Reblog -> RenoteItemType.Reblog(userRepository.find(renote.userId)) + is RenoteType.Reblog -> RenoteItemType.Reblog(userRepository.find(renote.userId)) } } } @@ -73,7 +76,7 @@ class RenotesViewModel @AssistedInject constructor( (it.content as? StateContent.Exist)?.rawContent }.map { renotes -> renotes.mapNotNull { renote -> - (renote as? Renote.Normal)?.let { + (renote as? RenoteType.Renote)?.let { noteCaptureAPIAdapter.capture(it.noteId) } } From dee029628d2b1500a3c8d298b8591e0066df5ac1 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 1 May 2023 12:23:40 +0900 Subject: [PATCH 059/432] =?UTF-8?q?feat:=20Query=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt index 5cd8a0c653..969d0da5e7 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt @@ -275,6 +275,7 @@ interface MastodonAPI { @GET("api/v2/search") suspend fun search( @Query("q") q: String, + @Query("type") type: String? = null, @Query("resolve") resolve: Boolean = true, @Query("following") following: Boolean = false, @Query("account_id") accountId: String? = null, From c3a680fe5f705c7283f7f3cc189698511a95998f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 1 May 2023 12:52:30 +0900 Subject: [PATCH 060/432] =?UTF-8?q?feat:=20=E6=A4=9C=E7=B4=A2=E7=B5=90?= =?UTF-8?q?=E6=9E=9C=E7=94=BB=E9=9D=A2=E3=81=AE=E7=8A=B6=E6=85=8B=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E3=81=99=E3=81=9F=E3=82=81=E3=81=AEViewModel=E3=82=92?= =?UTF-8?q?=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/SearchResultViewModel.kt | 112 ++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt new file mode 100644 index 0000000000..4222b4c15b --- /dev/null +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt @@ -0,0 +1,112 @@ +package net.pantasystem.milktea.search + +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.ViewModel +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.combine +import net.pantasystem.milktea.app_store.account.AccountStore +import net.pantasystem.milktea.common_android.resource.StringSource +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.account.AccountRepository +import javax.inject.Inject + +@HiltViewModel +class SearchResultViewModel @Inject constructor( + private val accountRepository: AccountRepository, + private val accountStore: AccountStore, + private val savedStateHandle: SavedStateHandle, +) : ViewModel() { + companion object { + const val EXTRA_KEYWORD = + "net.pantasystem.milktea.search.SearchResultViewModel.EXTRA_KEYWORD" + } + + private val keyword = savedStateHandle.getStateFlow(EXTRA_KEYWORD, "") + + val uiState = combine( + accountStore.observeCurrentAccount, + keyword + ) { currentAccount, keyword -> + SearchResultUiState( + currentAccount = currentAccount, + keyword = keyword + ) + } + + fun setKeyword(q: String) { + savedStateHandle[EXTRA_KEYWORD] = q + } +} + +data class SearchResultUiState( + val currentAccount: Account?, + val keyword: String, +) { + val isTag: Boolean = keyword.startsWith("#") + + val tabItems: List = when (currentAccount?.instanceType) { + Account.InstanceType.MISSKEY -> listOfNotNull( + if (isTag) { + SearchResultTabItem( + title = StringSource(R.string.timeline), + type = SearchResultTabItem.Type.SearchMisskeyPostsByTag, + query = keyword.replace("#", "") + ) + } else { + SearchResultTabItem( + title = StringSource(R.string.timeline), + type = SearchResultTabItem.Type.SearchMisskeyPosts, + query = keyword, + ) + }, + if (isTag) SearchResultTabItem( + title = StringSource(R.string.media), + type = SearchResultTabItem.Type.SearchMisskeyPostsWithFilesByTag, + query = keyword.replace("#", "") + ) else null, + SearchResultTabItem( + title = StringSource(R.string.user), + type = SearchResultTabItem.Type.SearchMisskeyUsers, + query = keyword, + ) + ) + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> listOfNotNull( + if (isTag) { + SearchResultTabItem( + title = StringSource(R.string.timeline), + type = SearchResultTabItem.Type.SearchMastodonPostsByTag, + query = keyword.replace("#", ""), + ) + } else { + SearchResultTabItem( + title = StringSource(R.string.timeline), + type = SearchResultTabItem.Type.SearchMastodonPosts, + query = keyword, + ) + }, + SearchResultTabItem( + title = StringSource(R.string.user), + type = SearchResultTabItem.Type.SearchMastodonUsers, + query = keyword, + ) + + ) + null -> emptyList() + } +} + +data class SearchResultTabItem( + val title: StringSource, + val type: Type, + val query: String, +) { + enum class Type { + SearchMisskeyPosts, + SearchMisskeyPostsByTag, + SearchMisskeyPostsWithFilesByTag, + SearchMisskeyUsers, + SearchMastodonPosts, + SearchMastodonPostsByTag, + SearchMastodonUsers, + } +} \ No newline at end of file From 341215636c0875f499a7ee6b9b9401ede3ef382c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 1 May 2023 13:51:36 +0900 Subject: [PATCH 061/432] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E3=81=AB?= =?UTF-8?q?=E5=AF=BE=E3=81=97=E3=81=A6=E6=A4=9C=E7=B4=A2=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/page/PageTypeHelper.kt | 1 + .../MastodonTimelineStorePagingStoreImpl.kt | 59 +++++++++++++------ .../milktea/model/account/page/PageParams.kt | 5 ++ .../milktea/model/account/page/PageType.kt | 1 + .../milktea/model/account/page/Pageable.kt | 11 ++++ 5 files changed, 59 insertions(+), 18 deletions(-) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt index 5f378889de..3c0ced3b6b 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt @@ -42,6 +42,7 @@ object PageTypeHelper{ CALCKEY_RECOMMENDED_TIMELINE -> context.getString(R.string.calckey_recomended_timeline) CLIP_NOTES -> context.getString(R.string.clip) MASTODON_BOOKMARK_TIMELINE -> context.getString(R.string.bookmark) + MASTODON_SEARCH_TIMELINE -> context.getString(R.string.search) } } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt index 9647239d0b..e6db062bc7 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt @@ -53,26 +53,26 @@ internal class MastodonTimelineStorePagingStoreImpl( is Pageable.Mastodon.HashTagTimeline -> api.getHashtagTimeline( pageableTimeline.hashtag, minId = getSinceId(), - ) + ).getBodyOrFail() Pageable.Mastodon.HomeTimeline -> api.getHomeTimeline( minId = getSinceId(), visibilities = getVisibilitiesParameter(getAccount()), - ) + ).getBodyOrFail() is Pageable.Mastodon.ListTimeline -> api.getListTimeline( minId = getSinceId(), listId = pageableTimeline.listId, - ) + ).getBodyOrFail() is Pageable.Mastodon.LocalTimeline -> api.getPublicTimeline( local = true, minId = getSinceId(), visibilities = getVisibilitiesParameter(getAccount()), onlyMedia = pageableTimeline.getOnlyMedia() - ) + ).getBodyOrFail() is Pageable.Mastodon.PublicTimeline -> api.getPublicTimeline( minId = getSinceId(), visibilities = getVisibilitiesParameter(getAccount()), onlyMedia = pageableTimeline.getOnlyMedia() - ) + ).getBodyOrFail() is Pageable.Mastodon.UserTimeline -> { api.getAccountTimeline( accountId = pageableTimeline.userId, @@ -82,16 +82,25 @@ internal class MastodonTimelineStorePagingStoreImpl( minId = getSinceId() ).throwIfHasError().also { updateMinIdFrom(it) - } + }.getBodyOrFail() } Pageable.Mastodon.BookmarkTimeline -> { api.getBookmarks( minId = minId ).throwIfHasError().also { updateMinIdFrom(it) - } + }.getBodyOrFail() } - }.throwIfHasError().body()!!.let { list -> + is Pageable.Mastodon.SearchTimeline -> { + api.search( + q = pageableTimeline.query, + type = "statuses", + minId = minId, + ).throwIfHasError().also { + updateMinIdFrom(it) + }.body()?.statuses!! + } + }.let { list -> if (isShouldUseLinkHeader()) { filterNotExistsStatuses(list) } else { @@ -146,26 +155,26 @@ internal class MastodonTimelineStorePagingStoreImpl( pageableTimeline.hashtag, maxId = maxId, onlyMedia = pageableTimeline.getOnlyMedia() - ) + ).getBodyOrFail() Pageable.Mastodon.HomeTimeline -> api.getHomeTimeline( maxId = maxId, visibilities = getVisibilitiesParameter(getAccount()) - ) + ).getBodyOrFail() is Pageable.Mastodon.ListTimeline -> api.getListTimeline( maxId = maxId, listId = pageableTimeline.listId, - ) + ).getBodyOrFail() is Pageable.Mastodon.LocalTimeline -> api.getPublicTimeline( local = true, maxId = maxId, visibilities = getVisibilitiesParameter(getAccount()), onlyMedia = pageableTimeline.getOnlyMedia() - ) + ).getBodyOrFail() is Pageable.Mastodon.PublicTimeline -> api.getPublicTimeline( maxId = maxId, visibilities = getVisibilitiesParameter(getAccount()), onlyMedia = pageableTimeline.getOnlyMedia() - ) + ).getBodyOrFail() is Pageable.Mastodon.UserTimeline -> { api.getAccountTimeline( accountId = pageableTimeline.userId, @@ -175,16 +184,25 @@ internal class MastodonTimelineStorePagingStoreImpl( maxId = maxId, ).throwIfHasError().also { updateMaxIdFrom(it) - } + }.body() } Pageable.Mastodon.BookmarkTimeline -> { api.getBookmarks( maxId = maxId, ).throwIfHasError().also { updateMaxIdFrom(it) - } + }.body() } - }.throwIfHasError().body()!!.let { list -> + is Pageable.Mastodon.SearchTimeline -> { + api.search( + q = pageableTimeline.query, + type = "statuses", + maxId = maxId, + ).throwIfHasError().also { + updateMaxIdFrom(it) + }.body()?.statuses + } + }!!.let { list -> if (isShouldUseLinkHeader()) { filterNotExistsStatuses(list) } else { @@ -212,6 +230,7 @@ internal class MastodonTimelineStorePagingStoreImpl( private fun isShouldUseLinkHeader(): Boolean { return pageableTimeline is Pageable.Mastodon.BookmarkTimeline || pageableTimeline is Pageable.Mastodon.UserTimeline + || pageableTimeline is Pageable.Mastodon.SearchTimeline } /** @@ -234,7 +253,7 @@ internal class MastodonTimelineStorePagingStoreImpl( * responseのmaxIdがnullの場合は更新がキャンセルされる * minIdがnullの場合はresponseのminIdが指定される */ - private fun updateMaxIdFrom(response: Response>) { + private fun updateMaxIdFrom(response: Response<*>) { val decoder = MastodonLinkHeaderDecoder(response.headers()["link"]) // NOTE: 次のページネーションのIdが取得できない場合は次のIdが取得できるまで同じIdを使い回し続ける @@ -254,7 +273,7 @@ internal class MastodonTimelineStorePagingStoreImpl( * responseのminIdがnullの場合は更新がキャンセルされる * maxIdがnullの場合はresponseのmaxIdが指定される */ - private fun updateMinIdFrom(response: Response>) { + private fun updateMinIdFrom(response: Response<*>) { val decoder = MastodonLinkHeaderDecoder(response.headers()["link"]) // NOTE: 次のページネーションのIdが取得できない場合は次のIdが取得できるまで同じIdを使い回し続ける @@ -268,4 +287,8 @@ internal class MastodonTimelineStorePagingStoreImpl( maxId = decoder.getMaxId() } } + + private fun Response>.getBodyOrFail(): List { + return requireNotNull(throwIfHasError().body()) + } } \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt index f9ec9dd904..83f34551c3 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt @@ -192,6 +192,11 @@ data class PageParams( MASTODON_BOOKMARK_TIMELINE -> { Pageable.Mastodon.BookmarkTimeline } + MASTODON_SEARCH_TIMELINE -> { + Pageable.Mastodon.SearchTimeline( + requireNotNull(query) + ) + } } } catch (e: NullPointerException) { throw IllegalStateException("パラメーターに問題があります: $this") diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt index 0d0019875b..0927bcba2a 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt @@ -30,6 +30,7 @@ enum class PageType(val defaultName: String, val label: String){ MASTODON_LIST_TIMELINE("MastodonListTimeline", "MASTODON_LIST_TIMELINE"), MASTODON_USER_TIMELINE("MastodonUserTimeline", "MASTODON_USER_TIMELINE"), MASTODON_BOOKMARK_TIMELINE("MastodonBookmarkTimeline", "MASTODON_BOOKMARK_TIMELINE"), + MASTODON_SEARCH_TIMELINE("MastodonSearchTimeline", "MASTODON_SEARCH_TIMELINE"), CALCKEY_RECOMMENDED_TIMELINE("CalckeyRecommendedTimeline", "CALCKEY_RECOMMENDED_TIMELINE"), CLIP_NOTES("ClipNotes", "CLIP_NOTES"), diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt index c1f2261f08..1bd1151531 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt @@ -480,6 +480,17 @@ sealed class Pageable : Serializable { } } + data class SearchTimeline( + val query: String + ) : Mastodon() { + override fun toParams(): PageParams { + return PageParams( + type = PageType.MASTODON_SEARCH_TIMELINE, + query = query + ) + } + } + } object CalckeyRecommendedTimeline : Pageable(), UntilPaginate, SincePaginate { From eb49c958fcec238221622a794fc7002dae788a6d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 1 May 2023 14:09:56 +0900 Subject: [PATCH 062/432] =?UTF-8?q?feat:=20mastodon,=20misskey=E3=81=A7?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E5=86=85=E5=AE=B9=E3=82=92?= =?UTF-8?q?=E5=88=87=E3=82=8A=E5=88=86=E3=81=91=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/SearchResultActivity.kt | 131 ++++++++++-------- .../res/layout/activity_search_result.xml | 2 +- .../milktea/user/search/SearchUserFragment.kt | 4 - 3 files changed, 73 insertions(+), 64 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt index 89086cb74d..d0f38d1b11 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt @@ -3,7 +3,6 @@ package net.pantasystem.milktea.search import android.app.Activity -import android.content.Context import android.content.Intent import android.os.Bundle import android.view.Menu @@ -11,10 +10,14 @@ import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentStatePagerAdapter +import androidx.fragment.app.FragmentActivity +import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.DiffUtil +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.google.android.material.tabs.TabLayoutMediator import com.wada811.databinding.dataBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -26,10 +29,10 @@ import net.pantasystem.milktea.app_store.setting.SettingStore import net.pantasystem.milktea.common.ui.ApplyMenuTint import net.pantasystem.milktea.common.ui.ApplyTheme import net.pantasystem.milktea.common_android_ui.PageableFragmentFactory +import net.pantasystem.milktea.common_android_ui.account.viewmodel.AccountViewModel import net.pantasystem.milktea.common_navigation.SearchNavType import net.pantasystem.milktea.common_navigation.SearchNavigation import net.pantasystem.milktea.common_viewmodel.confirm.ConfirmViewModel -import net.pantasystem.milktea.common_android_ui.account.viewmodel.AccountViewModel import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.page.Page import net.pantasystem.milktea.model.account.page.Pageable @@ -43,10 +46,6 @@ class SearchResultActivity : AppCompatActivity() { companion object { const val EXTRA_SEARCH_WORLD = "net.pantasystem.milktea.search.SearchResultActivity.EXTRA_SEARCH_WORLD" - - private const val SEARCH_NOTES = 0 - private const val SEARCH_USERS = 1 - private const val SEARCH_NOTES_WITH_FILES = 2 } private var mSearchWord: String? = null @@ -72,6 +71,8 @@ class SearchResultActivity : AppCompatActivity() { @Inject internal lateinit var applyMenuTint: ApplyMenuTint + private val searchResultViewModel: SearchResultViewModel by viewModels() + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) applyTheme() @@ -82,6 +83,8 @@ class SearchResultActivity : AppCompatActivity() { val keyword: String? = intent.getStringExtra(EXTRA_SEARCH_WORLD) ?: intent.data?.getQueryParameter("keyword") + searchResultViewModel.setKeyword(keyword ?: "") + mSearchWord = keyword if (keyword == null) { @@ -93,9 +96,16 @@ class SearchResultActivity : AppCompatActivity() { val isTag = keyword.startsWith("#") mIsTag = isTag - val pager = PagerAdapter(this, supportFragmentManager, pageableFragmentFactory, keyword) + val pager = SearchResultViewPagerAdapter(this, pageableFragmentFactory) binding.searchResultPager.adapter = pager - binding.searchResultTab.setupWithViewPager(binding.searchResultPager) + TabLayoutMediator( + binding.searchResultTab, + binding.searchResultPager, + ) { tab, position -> + tab.text = pager.items[position].title.getString(this) + }.attach() + + net.pantasystem.milktea.note.view.ActionNoteHandler( this, @@ -105,6 +115,10 @@ class SearchResultActivity : AppCompatActivity() { ).initViewModelListener() invalidateOptionsMenu() + searchResultViewModel.uiState.onEach { + pager.submitList(it.tabItems) + }.flowWithLifecycle(lifecycle, Lifecycle.State.RESUMED).launchIn(lifecycleScope) + accountStore.observeCurrentAccount.onEach { ar -> mAccountRelation = ar }.launchIn(lifecycleScope) @@ -193,65 +207,64 @@ class SearchResultActivity : AppCompatActivity() { } } - class PagerAdapter( - private val context: Context, - fragmentManager: FragmentManager, - private val pageableFragmentFactory: PageableFragmentFactory, - private val keyword: String, - ) : FragmentStatePagerAdapter(fragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - private val isTag = keyword.startsWith("#") +} - val pages = ArrayList(listOf(SEARCH_NOTES, SEARCH_USERS)).apply { - if (isTag) { - add(SEARCH_NOTES_WITH_FILES) - } - } +class SearchResultViewPagerAdapter( + activity: FragmentActivity, + private val pageableFragmentFactory: PageableFragmentFactory, +) : FragmentStateAdapter(activity) { + + var items: List = emptyList() + private set - override fun getCount(): Int { - return pages.size + + override fun createFragment(position: Int): Fragment { + val item = items[position] + return when(item.type) { + SearchResultTabItem.Type.SearchMisskeyPosts -> pageableFragmentFactory.create( + Pageable.Search(query = item.query) + ) + SearchResultTabItem.Type.SearchMisskeyPostsByTag -> pageableFragmentFactory.create( + Pageable.SearchByTag(tag = item.query) + ) + SearchResultTabItem.Type.SearchMisskeyPostsWithFilesByTag -> pageableFragmentFactory.create( + Pageable.SearchByTag(tag = item.query, withFiles = true) + ) + SearchResultTabItem.Type.SearchMisskeyUsers -> SearchUserFragment.newInstance(item.query) + SearchResultTabItem.Type.SearchMastodonPosts -> pageableFragmentFactory.create(Pageable.Mastodon.SearchTimeline(item.query)) + SearchResultTabItem.Type.SearchMastodonPostsByTag -> pageableFragmentFactory.create(Pageable.Mastodon.SearchTimeline(item.query)) + SearchResultTabItem.Type.SearchMastodonUsers -> SearchUserFragment.newInstance(item.query) } + } - @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) - override fun getItem(position: Int): Fragment { - val isTag = keyword.startsWith("#") - - return when (pages[position]) { - SEARCH_NOTES, SEARCH_NOTES_WITH_FILES -> { - val request: Pageable = if (isTag) { - if (pages[position] == SEARCH_NOTES) { - Pageable.SearchByTag(tag = keyword.replace("#", ""), withFiles = false) - } else { - Pageable.SearchByTag(tag = keyword.replace("#", ""), withFiles = true) - } - - } else { - Pageable.Search(query = keyword) - } - pageableFragmentFactory.create(request) - } - SEARCH_USERS -> { - SearchUserFragment.newInstance(keyword) - } - else -> { - pageableFragmentFactory.create( - Pageable.Search(query = keyword) - ) - } + override fun getItemCount(): Int { + return items.size + } + + fun submitList(list: List) { + val old = items + items = list + val callback = object : DiffUtil.Callback() { + override fun getNewListSize(): Int { + return list.size } - } - override fun getPageTitle(position: Int): CharSequence? { - return when (pages[position]) { - SEARCH_NOTES -> context.getString(R.string.timeline) - SEARCH_NOTES_WITH_FILES -> context.getString(R.string.media) - SEARCH_USERS -> context.getString(R.string.user) - else -> null + override fun getOldListSize(): Int { + return old.size } - } - } + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == list[newItemPosition] + } + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == list[newItemPosition] + } + } + val result = DiffUtil.calculateDiff(callback) + result.dispatchUpdatesTo(this) + } } class SearchNavigationImpl @Inject constructor( diff --git a/modules/features/search/src/main/res/layout/activity_search_result.xml b/modules/features/search/src/main/res/layout/activity_search_result.xml index 8797cd185c..b861cb5e32 100644 --- a/modules/features/search/src/main/res/layout/activity_search_result.xml +++ b/modules/features/search/src/main/res/layout/activity_search_result.xml @@ -33,7 +33,7 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintEnd_toEndOf="parent" /> - Date: Mon, 1 May 2023 14:11:18 +0900 Subject: [PATCH 063/432] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=82=92=E5=88=87=E3=82=8A=E5=88=86=E3=81=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/SearchResultActivity.kt | 65 ------------------ .../search/SearchResultViewPagerAdapter.kt | 68 +++++++++++++++++++ 2 files changed, 68 insertions(+), 65 deletions(-) create mode 100644 modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt index d0f38d1b11..1fc0814342 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt @@ -9,19 +9,13 @@ import android.view.Menu import android.view.MenuItem import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.flowWithLifecycle import androidx.lifecycle.lifecycleScope -import androidx.recyclerview.widget.DiffUtil -import androidx.viewpager2.adapter.FragmentStateAdapter import com.google.android.material.tabs.TabLayoutMediator import com.wada811.databinding.dataBinding import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import net.pantasystem.milktea.app_store.account.AccountStore @@ -38,7 +32,6 @@ import net.pantasystem.milktea.model.account.page.Page import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.note.viewmodel.NotesViewModel import net.pantasystem.milktea.search.databinding.ActivitySearchResultBinding -import net.pantasystem.milktea.user.search.SearchUserFragment import javax.inject.Inject @AndroidEntryPoint @@ -138,7 +131,6 @@ class SearchResultActivity : AppCompatActivity() { return super.onCreateOptionsMenu(menu) } - @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) override fun onOptionsItemSelected(item: MenuItem): Boolean { when (item.itemId) { android.R.id.home -> finish() @@ -210,63 +202,6 @@ class SearchResultActivity : AppCompatActivity() { } -class SearchResultViewPagerAdapter( - activity: FragmentActivity, - private val pageableFragmentFactory: PageableFragmentFactory, -) : FragmentStateAdapter(activity) { - - var items: List = emptyList() - private set - - - override fun createFragment(position: Int): Fragment { - val item = items[position] - return when(item.type) { - SearchResultTabItem.Type.SearchMisskeyPosts -> pageableFragmentFactory.create( - Pageable.Search(query = item.query) - ) - SearchResultTabItem.Type.SearchMisskeyPostsByTag -> pageableFragmentFactory.create( - Pageable.SearchByTag(tag = item.query) - ) - SearchResultTabItem.Type.SearchMisskeyPostsWithFilesByTag -> pageableFragmentFactory.create( - Pageable.SearchByTag(tag = item.query, withFiles = true) - ) - SearchResultTabItem.Type.SearchMisskeyUsers -> SearchUserFragment.newInstance(item.query) - SearchResultTabItem.Type.SearchMastodonPosts -> pageableFragmentFactory.create(Pageable.Mastodon.SearchTimeline(item.query)) - SearchResultTabItem.Type.SearchMastodonPostsByTag -> pageableFragmentFactory.create(Pageable.Mastodon.SearchTimeline(item.query)) - SearchResultTabItem.Type.SearchMastodonUsers -> SearchUserFragment.newInstance(item.query) - } - } - - override fun getItemCount(): Int { - return items.size - } - - fun submitList(list: List) { - val old = items - items = list - val callback = object : DiffUtil.Callback() { - override fun getNewListSize(): Int { - return list.size - } - - override fun getOldListSize(): Int { - return old.size - } - - override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - return old[oldItemPosition] == list[newItemPosition] - } - - override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { - return old[oldItemPosition] == list[newItemPosition] - } - } - val result = DiffUtil.calculateDiff(callback) - result.dispatchUpdatesTo(this) - } -} - class SearchNavigationImpl @Inject constructor( val activity: Activity ): SearchNavigation { diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt new file mode 100644 index 0000000000..c300684735 --- /dev/null +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt @@ -0,0 +1,68 @@ +package net.pantasystem.milktea.search + +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentActivity +import androidx.recyclerview.widget.DiffUtil +import androidx.viewpager2.adapter.FragmentStateAdapter +import net.pantasystem.milktea.common_android_ui.PageableFragmentFactory +import net.pantasystem.milktea.model.account.page.Pageable +import net.pantasystem.milktea.user.search.SearchUserFragment + + +class SearchResultViewPagerAdapter( + activity: FragmentActivity, + private val pageableFragmentFactory: PageableFragmentFactory, +) : FragmentStateAdapter(activity) { + + var items: List = emptyList() + private set + + + override fun createFragment(position: Int): Fragment { + val item = items[position] + return when(item.type) { + SearchResultTabItem.Type.SearchMisskeyPosts -> pageableFragmentFactory.create( + Pageable.Search(query = item.query) + ) + SearchResultTabItem.Type.SearchMisskeyPostsByTag -> pageableFragmentFactory.create( + Pageable.SearchByTag(tag = item.query) + ) + SearchResultTabItem.Type.SearchMisskeyPostsWithFilesByTag -> pageableFragmentFactory.create( + Pageable.SearchByTag(tag = item.query, withFiles = true) + ) + SearchResultTabItem.Type.SearchMisskeyUsers -> SearchUserFragment.newInstance(item.query) + SearchResultTabItem.Type.SearchMastodonPosts -> pageableFragmentFactory.create(Pageable.Mastodon.SearchTimeline(item.query)) + SearchResultTabItem.Type.SearchMastodonPostsByTag -> pageableFragmentFactory.create( + Pageable.Mastodon.SearchTimeline(item.query)) + SearchResultTabItem.Type.SearchMastodonUsers -> SearchUserFragment.newInstance(item.query) + } + } + + override fun getItemCount(): Int { + return items.size + } + + fun submitList(list: List) { + val old = items + items = list + val callback = object : DiffUtil.Callback() { + override fun getNewListSize(): Int { + return list.size + } + + override fun getOldListSize(): Int { + return old.size + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == list[newItemPosition] + } + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == list[newItemPosition] + } + } + val result = DiffUtil.calculateDiff(callback) + result.dispatchUpdatesTo(this) + } +} \ No newline at end of file From 29888fd38eed201bc0ffd8c6f626d479cd7d7e0d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 1 May 2023 14:26:04 +0900 Subject: [PATCH 064/432] refactor --- .../java/net/pantasystem/milktea/search/SearchResultActivity.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt index 1fc0814342..9aad223c10 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt @@ -1,5 +1,3 @@ -@file:Suppress("DEPRECATION") - package net.pantasystem.milktea.search import android.app.Activity From 0fb55f32adaddd023068eb2ebdff9f7cdafdb6a4 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 1 May 2023 14:34:19 +0900 Subject: [PATCH 065/432] =?UTF-8?q?feat:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E3=83=8D=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=8C=E6=A9=9F?= =?UTF-8?q?=E8=83=BD=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/MastodonTimelineStorePagingStoreImpl.kt | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt index e6db062bc7..e4348bc4ed 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt @@ -92,13 +92,7 @@ internal class MastodonTimelineStorePagingStoreImpl( }.getBodyOrFail() } is Pageable.Mastodon.SearchTimeline -> { - api.search( - q = pageableTimeline.query, - type = "statuses", - minId = minId, - ).throwIfHasError().also { - updateMinIdFrom(it) - }.body()?.statuses!! + return@runCancellableCatching emptyList() } }.let { list -> if (isShouldUseLinkHeader()) { @@ -198,6 +192,7 @@ internal class MastodonTimelineStorePagingStoreImpl( q = pageableTimeline.query, type = "statuses", maxId = maxId, + offset = (getState().content as? StateContent.Exist)?.rawContent?.size ?: 0 ).throwIfHasError().also { updateMaxIdFrom(it) }.body()?.statuses @@ -230,7 +225,6 @@ internal class MastodonTimelineStorePagingStoreImpl( private fun isShouldUseLinkHeader(): Boolean { return pageableTimeline is Pageable.Mastodon.BookmarkTimeline || pageableTimeline is Pageable.Mastodon.UserTimeline - || pageableTimeline is Pageable.Mastodon.SearchTimeline } /** From 4085fd8f4c9424e71af00dcc1715566150cf9b88 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 1 May 2023 15:01:45 +0900 Subject: [PATCH 066/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=82=B0=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=82=92=E5=8F=96?= =?UTF-8?q?=E5=BE=97=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/page/PageTypeHelper.kt | 1 + .../MastodonTimelineStorePagingStoreImpl.kt | 12 +++++++ .../milktea/search/SearchResultActivity.kt | 35 ++----------------- .../milktea/search/SearchResultViewModel.kt | 28 ++++++++++++++- .../search/SearchResultViewPagerAdapter.kt | 2 +- .../milktea/model/account/page/PageParams.kt | 5 +++ .../milktea/model/account/page/PageType.kt | 1 + .../milktea/model/account/page/Pageable.kt | 11 ++++++ .../model/account/page/PageableTemplate.kt | 10 ++++-- 9 files changed, 68 insertions(+), 37 deletions(-) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt index 3c0ced3b6b..0a63d58f9f 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt @@ -43,6 +43,7 @@ object PageTypeHelper{ CLIP_NOTES -> context.getString(R.string.clip) MASTODON_BOOKMARK_TIMELINE -> context.getString(R.string.bookmark) MASTODON_SEARCH_TIMELINE -> context.getString(R.string.search) + MASTODON_TAG_TIMELINE -> context.getString(R.string.tag) } } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt index e4348bc4ed..eab84f3b93 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt @@ -94,6 +94,12 @@ internal class MastodonTimelineStorePagingStoreImpl( is Pageable.Mastodon.SearchTimeline -> { return@runCancellableCatching emptyList() } + is Pageable.Mastodon.TagTimeline -> { + api.getHashtagTimeline( + tag = pageableTimeline.tag, + minId = minId, + ).throwIfHasError().body()!! + } }.let { list -> if (isShouldUseLinkHeader()) { filterNotExistsStatuses(list) @@ -197,6 +203,12 @@ internal class MastodonTimelineStorePagingStoreImpl( updateMaxIdFrom(it) }.body()?.statuses } + is Pageable.Mastodon.TagTimeline -> { + api.getHashtagTimeline( + tag = pageableTimeline.tag, + maxId = maxId, + ).getBodyOrFail() + } }!!.let { list -> if (isShouldUseLinkHeader()) { filterNotExistsStatuses(list) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt index 9aad223c10..1f0c48802e 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt @@ -21,7 +21,6 @@ import net.pantasystem.milktea.app_store.setting.SettingStore import net.pantasystem.milktea.common.ui.ApplyMenuTint import net.pantasystem.milktea.common.ui.ApplyTheme import net.pantasystem.milktea.common_android_ui.PageableFragmentFactory -import net.pantasystem.milktea.common_android_ui.account.viewmodel.AccountViewModel import net.pantasystem.milktea.common_navigation.SearchNavType import net.pantasystem.milktea.common_navigation.SearchNavigation import net.pantasystem.milktea.common_viewmodel.confirm.ConfirmViewModel @@ -44,8 +43,7 @@ class SearchResultActivity : AppCompatActivity() { private var mAccountRelation: Account? = null private val binding: ActivitySearchResultBinding by dataBinding() - val notesViewModel by viewModels() - private val accountViewModel: AccountViewModel by viewModels() + private val notesViewModel by viewModels() @Inject lateinit var settingStore: SettingStore @@ -145,36 +143,7 @@ class SearchResultActivity : AppCompatActivity() { } private fun searchAddToTab() { - val word = mSearchWord ?: return - - val samePage = getSamePage() - if (samePage == null) { - val page = if (mIsTag == true) { - Page( - mAccountRelation?.accountId ?: -1, - word, - 0, - pageable = Pageable.SearchByTag( - tag = word.replace( - "#", - "" - ) - ) - ) - } else { - Page( - mAccountRelation?.accountId ?: -1, - mSearchWord ?: "", - -1, - pageable = Pageable.Search(word) - ) - } - accountViewModel.addPage( - page - ) - } else { - accountViewModel.removePage(samePage) - } + searchResultViewModel.toggleAddToTab() } private fun isAddedPage(): Boolean { diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt index 4222b4c15b..faeb62b824 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt @@ -2,18 +2,22 @@ package net.pantasystem.milktea.search import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore +import net.pantasystem.milktea.common.mapCancellableCatching import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.account.page.PageableTemplate import javax.inject.Inject @HiltViewModel class SearchResultViewModel @Inject constructor( - private val accountRepository: AccountRepository, private val accountStore: AccountStore, + private val accountRepository: AccountRepository, private val savedStateHandle: SavedStateHandle, ) : ViewModel() { companion object { @@ -36,6 +40,28 @@ class SearchResultViewModel @Inject constructor( fun setKeyword(q: String) { savedStateHandle[EXTRA_KEYWORD] = q } + + fun toggleAddToTab() { + viewModelScope.launch { + val keyword = savedStateHandle.get(EXTRA_KEYWORD) ?: return@launch + accountRepository.getCurrentAccount().mapCancellableCatching { account -> + val exists = account.pages.firstOrNull { + (it.pageParams.tag == keyword + || it.pageParams.query == keyword) + } + if (exists == null) { + val page = if (keyword.startsWith("#")) { + PageableTemplate(account).tag(keyword) + } else { + PageableTemplate(account).search(keyword) + } + accountStore.addPage(page) + } else { + accountStore.removePage(exists) + } + } + } + } } data class SearchResultUiState( diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt index c300684735..87943e8836 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt @@ -33,7 +33,7 @@ class SearchResultViewPagerAdapter( SearchResultTabItem.Type.SearchMisskeyUsers -> SearchUserFragment.newInstance(item.query) SearchResultTabItem.Type.SearchMastodonPosts -> pageableFragmentFactory.create(Pageable.Mastodon.SearchTimeline(item.query)) SearchResultTabItem.Type.SearchMastodonPostsByTag -> pageableFragmentFactory.create( - Pageable.Mastodon.SearchTimeline(item.query)) + Pageable.Mastodon.TagTimeline(item.query)) SearchResultTabItem.Type.SearchMastodonUsers -> SearchUserFragment.newInstance(item.query) } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt index 83f34551c3..cf1a6a692a 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt @@ -197,6 +197,11 @@ data class PageParams( requireNotNull(query) ) } + MASTODON_TAG_TIMELINE -> { + Pageable.Mastodon.TagTimeline( + requireNotNull(tag) + ) + } } } catch (e: NullPointerException) { throw IllegalStateException("パラメーターに問題があります: $this") diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt index 0927bcba2a..e7e594f2e1 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt @@ -31,6 +31,7 @@ enum class PageType(val defaultName: String, val label: String){ MASTODON_USER_TIMELINE("MastodonUserTimeline", "MASTODON_USER_TIMELINE"), MASTODON_BOOKMARK_TIMELINE("MastodonBookmarkTimeline", "MASTODON_BOOKMARK_TIMELINE"), MASTODON_SEARCH_TIMELINE("MastodonSearchTimeline", "MASTODON_SEARCH_TIMELINE"), + MASTODON_TAG_TIMELINE("MastodonTagTimeline", "MASTODON_TAG_TIMELINE"), CALCKEY_RECOMMENDED_TIMELINE("CalckeyRecommendedTimeline", "CALCKEY_RECOMMENDED_TIMELINE"), CLIP_NOTES("ClipNotes", "CLIP_NOTES"), diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt index 1bd1151531..347a2feaca 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt @@ -491,6 +491,17 @@ sealed class Pageable : Serializable { } } + data class TagTimeline( + val tag: String, + ) : Mastodon() { + override fun toParams(): PageParams { + return PageParams( + type = PageType.MASTODON_TAG_TIMELINE, + tag = tag, + ) + } + } + } object CalckeyRecommendedTimeline : Pageable(), UntilPaginate, SincePaginate { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt index 3f74bd785e..3168e31250 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt @@ -30,10 +30,16 @@ class PageableTemplate(val account: Account?) { return Page(account?.accountId?: - 1, title, 0, Pageable.Show(noteId)) } fun tag(tag: String): Page { - return Page(account?.accountId?: - 1, tag, 0, Pageable.SearchByTag(tag.replace("#", ""))) + return when(account?.instanceType) { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> Page(account.accountId, tag, 0, Pageable.Mastodon.TagTimeline(tag.replace("#", ""))) + else -> Page(account?.accountId?: - 1, tag, 0, Pageable.SearchByTag(tag.replace("#", ""))) + } } fun search(query: String): Page { - return Page(account?.accountId?: - 1, query, 0, Pageable.Search(query)) + return when(account?.instanceType) { + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> Page(account.accountId, query, 0, Pageable.Mastodon.SearchTimeline(query)) + else -> Page(account?.accountId?: - 1, query, 0, Pageable.Search(query)) + } } fun featured(title: String) = Page(account?.accountId?: - 1, title, 0, Pageable.Featured(null)) fun notification(title: String) = Page(account?.accountId?: - 1, title, 0, Pageable.Notification()) From 8298bc936e14c54e2d61f63d1b88eeb35662a476 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 1 May 2023 16:33:34 +0900 Subject: [PATCH 067/432] =?UTF-8?q?feat:=20type=E3=81=A7accounts=E3=81=AE?= =?UTF-8?q?=E3=81=BF=E3=81=AB=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/user/UserApiAdapter.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserApiAdapter.kt index 13d8fa4d2f..3d49c8a9e6 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserApiAdapter.kt @@ -96,7 +96,8 @@ internal class UserApiAdapterImpl @Inject constructor( Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { val body = requireNotNull( mastodonAPIProvider.get(account).search( - if (host == null) userName else "$userName@$host" + if (host == null) userName else "$userName@$host", + type = "accounts" ).throwIfHasError().body() ).accounts SearchResult.Mastodon(body) From 64c429ab7508e99e992865873719e5fc6b749bc1 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 2 May 2023 16:25:16 +0900 Subject: [PATCH 068/432] =?UTF-8?q?fix:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=82=B5=E3=82=A4=E3=82=BA=E3=81=8C=E6=96=87=E5=AD=97=E3=82=B5?= =?UTF-8?q?=E3=82=A4=E3=82=BA=E3=82=88=E3=82=8A=E5=B0=8F=E3=81=95=E3=81=84?= =?UTF-8?q?=E5=A0=B4=E5=90=88=E6=AD=A3=E3=81=97=E3=81=8F=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=81=93=E3=81=A8=E3=81=8C=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=81=AA=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_android/ui/text/EmojiSpan.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt index 7051004efd..a2b274d3e6 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt @@ -77,12 +77,7 @@ abstract class EmojiSpan(val key: T) : ReplacementSpan(){ val imageHeight = size.intrinsicHeight // 画像がテキストの高さよりも大きい場合、画像をテキストと同じ高さに縮小する - val scale = if (imageHeight > textHeight) { - textHeight / imageHeight.toFloat() - } else { - 1.0f - } - + val scale = textHeight / imageHeight.toFloat() // テキストの高さに合わせた画像の幅 return (imageWidth * scale).toInt() } From c517ae1b1b73b0761683290b6d9c9688e5f10c8b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 2 May 2023 16:26:18 +0900 Subject: [PATCH 069/432] =?UTF-8?q?feat:=20=E3=82=AB=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92=E3=82=88=E3=82=8A?= =?UTF-8?q?=E5=B0=8F=E3=81=95=E3=81=AA=E3=82=B5=E3=82=A4=E3=82=BA=E3=81=A8?= =?UTF-8?q?=E3=81=97=E3=81=A6=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=82=80=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_android/ui/text/CustomEmojiDecorator.kt | 6 +++--- .../pantasystem/milktea/common_android_ui/MFMDecorator.kt | 5 +++-- .../milktea/note/reaction/choices/EmojiChoicesAdapter.kt | 1 + .../net/pantasystem/milktea/note/view/InstanceInfoHelper.kt | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt index 8d11cc06e7..528d6d124f 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt @@ -37,7 +37,7 @@ class CustomEmojiDecorator { GlideApp.with(view) .asDrawable() .load(it.result.getUrl(accountHost)) - .override(min(view.textSize.toInt(), 640)) + .override(min(view.textSize.toInt(), 20)) .into(span.target) builder.setSpan(span, it.start, it.end, 0) } @@ -57,7 +57,7 @@ class CustomEmojiDecorator { val span = DrawableEmojiSpan(emojiAdapter, it.result.getUrl(accountHost)) GlideApp.with(view) .asDrawable() - .override(min(view.textSize.toInt(), 640)) + .override(min(view.textSize.toInt(), 20)) .load(it.result.getUrl(accountHost)) .into(span.target) builder.setSpan(span, it.start, it.end, 0) @@ -79,7 +79,7 @@ class CustomEmojiDecorator { GlideApp.with(view) .asDrawable() .load(it.result.getUrl(accountHost)) - .override(min(view.textSize.toInt(), 640)) + .override(min(view.textSize.toInt(), 20)) .into(span.target) builder.setSpan(span, it.start, it.end, 0) } diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt index 4998031cc7..ccbdc43b39 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt @@ -30,6 +30,7 @@ import net.pantasystem.milktea.common_navigation.UserDetailNavigationArgs import net.pantasystem.milktea.model.emoji.Emoji import java.lang.ref.WeakReference import kotlin.math.max +import kotlin.math.min object MFMDecorator { @@ -312,7 +313,7 @@ object MFMDecorator { spannableString.setSpan(emojiSpan, skippedEmoji.start, skippedEmoji.end, 0) GlideApp.with(textView) .load(emojiElement.emoji.url) - .override(max(textView.textSize.toInt(), 10)) + .override(min(max(textView.textSize.toInt(), 10), 20)) .addListener(object : RequestListener { override fun onLoadFailed( e: GlideException?, @@ -322,7 +323,7 @@ object MFMDecorator { ): Boolean { val t = this@LazyEmojiDecorator.textView.get() if (t != null && !skipEmojis.contains(emojiElement.emoji) && t.getTag(R.id.TEXT_VIEW_MFM_TAG_ID) == lazyDecorateResult.sourceText) { - if (retryCounter < 100) { + if (retryCounter < 10) { t.text = decorate( t, diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiChoicesAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiChoicesAdapter.kt index 0bc6e26e99..df60144491 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiChoicesAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiChoicesAdapter.kt @@ -52,6 +52,7 @@ class EmojiChoicesAdapter( .load(item.emoji.url ?: item.emoji.uri) // FIXME: webpの場合うまく表示できなくなる // .centerCrop() + .override(60) .into(holder.binding.reactionImagePreview) holder.binding.reactionStringPreview.setMemoVisibility(View.GONE) holder.binding.reactionImagePreview.setMemoVisibility(View.VISIBLE) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/InstanceInfoHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/InstanceInfoHelper.kt index 2807361b06..f4682cfd9b 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/InstanceInfoHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/InstanceInfoHelper.kt @@ -30,7 +30,7 @@ object InstanceInfoHelper { val iconDrawable = DrawableEmojiSpan(emojiAdapter, info?.faviconUrl) Glide.with(this) .load(info!!.faviconUrl) - .override(min(this.textSize.toInt(), 640)) + .override(min(this.textSize.toInt(), 20)) .into(iconDrawable.target) text = SpannableStringBuilder(":${info.faviconUrl}:${info.name}").apply { setSpan(iconDrawable, 0, ":${info.faviconUrl}:".length, 0) From a600c075c558b8520f82a36d423e80bbab2ff2f3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 4 May 2023 17:17:46 +0900 Subject: [PATCH 070/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=82=B0=E3=82=92?= =?UTF-8?q?=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=E3=81=97=E3=81=9F=E6=99=82?= =?UTF-8?q?=E3=81=AB=E3=80=81=E6=A4=9C=E7=B4=A2=E7=B5=90=E6=9E=9C=E7=94=BB?= =?UTF-8?q?=E9=9D=A2=E3=81=AB=E9=81=B7=E7=A7=BB=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_android_ui/DecorateTextHelper.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt index f10b9d915f..2fd0ef713e 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt @@ -20,6 +20,7 @@ import net.pantasystem.milktea.common_android.mfm.MFMParser import net.pantasystem.milktea.common_android.mfm.Root import net.pantasystem.milktea.common_android.ui.text.CustomEmojiDecorator import net.pantasystem.milktea.common_android.ui.text.DrawableEmojiSpan +import net.pantasystem.milktea.common_navigation.SearchNavType import net.pantasystem.milktea.common_navigation.UserDetailNavigationArgs import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.emoji.Emoji @@ -100,7 +101,11 @@ object DecorateTextHelper { tag != null -> { // FIXME: タグの場合うまく動作しないケースがある // 原因としてTagオブジェクトに入っているURLとHTML上に表示されているURLが異なるから - false + val intent = navigationEntryPoint.searchNavigation().newIntent(SearchNavType.ResultScreen( + searchWord = "#${tag.name}" + )) + context.startActivity(intent) + true } mention != null -> { val intent = navigationEntryPoint From 6176fd9f780147ae55997414919739cf348ecb10 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 4 May 2023 18:00:20 +0900 Subject: [PATCH 071/432] =?UTF-8?q?feat:=20textSpan=E3=82=92=E5=8F=96?= =?UTF-8?q?=E5=BE=97=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_android_ui/DecorateTextHelper.kt | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt index 2fd0ef713e..bbe9a14ef7 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt @@ -78,13 +78,28 @@ object DecorateTextHelper { stopDrawableAnimations(this) when (textType) { is TextType.Mastodon -> { - this.text = CustomEmojiDecorator().decorate( + val decoratedText = CustomEmojiDecorator().decorate( textType.html.spanned, textType.html.accountHost, textType.html.parserResult, this ) + this.text = decoratedText this.movementMethod = ClickListenableLinkMovementMethod { url -> + + // NOTE: クリックしたURLを探している + val urlSpans = decoratedText.getSpans(0, decoratedText.length, URLSpan::class.java) + var textHashTag: CharSequence? = null + for (urlSpan in urlSpans) { + val start = decoratedText.getSpanStart(urlSpan) + val end = decoratedText.getSpanEnd(urlSpan) + val spannedText = decoratedText.subSequence(start, end) + if (spannedText.isNotEmpty() && spannedText[0] == '#') { + if (urlSpan.url == url) { + textHashTag = spannedText + } + } + } val tag = textType.tags.firstOrNull { it.url == url || it.url == url.lowercase() } @@ -99,14 +114,19 @@ object DecorateTextHelper { ) when { tag != null -> { - // FIXME: タグの場合うまく動作しないケースがある - // 原因としてTagオブジェクトに入っているURLとHTML上に表示されているURLが異なるから val intent = navigationEntryPoint.searchNavigation().newIntent(SearchNavType.ResultScreen( searchWord = "#${tag.name}" )) context.startActivity(intent) true } + textHashTag != null -> { + val intent = navigationEntryPoint.searchNavigation().newIntent(SearchNavType.ResultScreen( + searchWord = textHashTag.toString() + )) + context.startActivity(intent) + true + } mention != null -> { val intent = navigationEntryPoint .userDetailNavigation() From 63018d1ab80fdf14fbcfbe925e5775aa819f1d2f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:17:10 +0900 Subject: [PATCH 072/432] =?UTF-8?q?feat:=20=E3=83=97=E3=83=AD=E3=82=B0?= =?UTF-8?q?=E3=83=A9=E3=83=A0=E3=83=AC=E3=83=99=E3=83=AB=E3=81=A7=E3=83=8E?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=AE=E3=83=95=E3=82=A9=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=B5=E3=82=A4=E3=82=BA=E3=82=92=E8=AA=BF=E7=AF=80=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=81=9F=E3=82=81=E3=81=AE=E5=88=B6=E5=BE=A1=E3=82=AF=E3=83=A9?= =?UTF-8?q?=E3=82=B9=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/timeline/NoteFontSizeBinder.kt | 91 +++++++++++++++++++ .../note/timeline/TimelineListAdapter.kt | 8 ++ 2 files changed, 99 insertions(+) create mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/NoteFontSizeBinder.kt diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/NoteFontSizeBinder.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/NoteFontSizeBinder.kt new file mode 100644 index 0000000000..2216398ee8 --- /dev/null +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/NoteFontSizeBinder.kt @@ -0,0 +1,91 @@ +package net.pantasystem.milktea.note.timeline + +import android.widget.TextView +import net.pantasystem.milktea.note.databinding.ItemSimpleNoteBinding + +class NoteFontSizeBinder( + val userInfoViews: HeaderViews, + val contentViews: ContentViews, + val quoteToUserInfoViews: HeaderViews, + val quoteToContentViews: ContentViews, + val replyToHeaderViews: HeaderViews? = null, + val replyToContentViews: ContentViews? = null, +) { + + class HeaderViews( + val nameView: TextView, + val userNameView: TextView, + val elapsedTimeView: TextView?, + ) + + class ContentViews( + val cwView: TextView, + val textView: TextView, + ) + + companion object { + fun from(binding: ItemSimpleNoteBinding): NoteFontSizeBinder { + return NoteFontSizeBinder( + userInfoViews = HeaderViews( + nameView = binding.mainName, + userNameView = binding.subName, + elapsedTimeView = binding.elapsedTime + ), + contentViews = ContentViews( + cwView = binding.cw, + textView = binding.text + ), + quoteToUserInfoViews = HeaderViews( + nameView = binding.subNoteMainName, + userNameView = binding.subNoteSubName, + elapsedTimeView = null, + ), + quoteToContentViews = ContentViews( + cwView = binding.subCw, + textView = binding.subNoteText + ) + ) + } + } + + fun bind( + headerFontSize: Float, + contentFontSize: Float, + ) { + bind( + header = userInfoViews, + content = contentViews, + headerFontSize = headerFontSize, + contentFontSize = contentFontSize + ) + bind( + header = quoteToUserInfoViews, + content = quoteToContentViews, + headerFontSize = headerFontSize, + contentFontSize = contentFontSize, + ) + + + if (replyToContentViews != null && replyToHeaderViews != null) { + bind( + header = replyToHeaderViews, + content = replyToContentViews, + headerFontSize = headerFontSize, + contentFontSize = contentFontSize, + ) + } + } + + private fun bind( + header: HeaderViews, + content: ContentViews, + headerFontSize: Float, + contentFontSize: Float, + ) { + header.elapsedTimeView?.textSize = headerFontSize + header.userNameView.textSize = headerFontSize + header.nameView.textSize = headerFontSize + content.cwView.textSize = contentFontSize + content.textView.textSize = contentFontSize + } +} \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt index 46d112b8bc..f5adc8017a 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt @@ -260,6 +260,10 @@ class TimelineListAdapter( binding.simpleNote.reactionView.setRecycledViewPool(reactionCounterRecyclerViewPool) binding.simpleNote.urlPreviewList.setRecycledViewPool(urlPreviewListRecyclerViewPool) binding.simpleNote.manyFilePreviewListView.setRecycledViewPool(manyFilePreviewListViewRecyclerViewPool) + NoteFontSizeBinder.from(binding.simpleNote).bind( + headerFontSize = 15f, + contentFontSize = 15f, + ) NoteViewHolder(binding) } ViewHolderType.HasReplyToNote -> { @@ -267,6 +271,10 @@ class TimelineListAdapter( binding.simpleNote.reactionView.setRecycledViewPool(reactionCounterRecyclerViewPool) binding.simpleNote.urlPreviewList.setRecycledViewPool(urlPreviewListRecyclerViewPool) binding.simpleNote.manyFilePreviewListView.setRecycledViewPool(manyFilePreviewListViewRecyclerViewPool) + NoteFontSizeBinder.from(binding.simpleNote).bind( + headerFontSize = 15f, + contentFontSize = 15f, + ) HasReplyToNoteViewHolder(binding) } ViewHolderType.Loading -> { From 84c2e9db6338ae847334881b6a8d8e3e3f924492 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:27:23 +0900 Subject: [PATCH 073/432] =?UTF-8?q?feat:=20config=E3=81=AB=E3=83=86?= =?UTF-8?q?=E3=82=AD=E3=82=B9=E3=83=88=E3=82=B5=E3=82=A4=E3=82=BA=E3=82=92?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/settings/Config.kt | 14 +++++++++++++- .../settings/LocalConfigRepository.kt | 1 + .../data/infrastructure/settings/ConfigKtTest.kt | 8 ++++++++ .../data/infrastructure/settings/KeysKtTest.kt | 12 ++++++++++-- .../pantasystem/milktea/model/setting/Config.kt | 8 +++++++- .../net/pantasystem/milktea/model/setting/Keys.kt | 8 ++++++++ .../pantasystem/milktea/model/setting/PrefType.kt | 4 ++++ 7 files changed, 51 insertions(+), 4 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt index 80f2720cd0..4e1662ff05 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt @@ -117,7 +117,13 @@ fun Config.Companion.from(map: Map): Config { )?.value ?: DefaultConfig.config.isVisibleInstanceUrlInToolbar, isHideMediaWhenMobileNetwork = map.getValue( Keys.IsHideMediaWhenMobileNetwork - )?.value ?: DefaultConfig.config.isHideMediaWhenMobileNetwork + )?.value ?: DefaultConfig.config.isHideMediaWhenMobileNetwork, + noteHeaderFontSize = map.getValue( + Keys.NoteHeaderFontSize + )?.value ?: DefaultConfig.config.noteHeaderFontSize, + noteContentFontSize = map.getValue( + Keys.NoteContentFontSize + )?.value ?: DefaultConfig.config.noteContentFontSize ) } @@ -214,6 +220,12 @@ fun Config.pref(key: Keys): PrefType { Keys.IsHideMediaWhenMobileNetwork -> { PrefType.BoolPref(isHideMediaWhenMobileNetwork) } + Keys.NoteContentFontSize -> { + PrefType.FloatPref(noteContentFontSize) + } + Keys.NoteHeaderFontSize -> { + PrefType.FloatPref(noteHeaderFontSize) + } } } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/LocalConfigRepository.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/LocalConfigRepository.kt index 3ccdaf8c69..0378c35d01 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/LocalConfigRepository.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/LocalConfigRepository.kt @@ -48,6 +48,7 @@ class LocalConfigRepositoryImpl( is PrefType.BoolPref -> putBoolean(it.key.str(), entry.value) is PrefType.IntPref -> putInt(it.key.str(), entry.value) is PrefType.StrPref -> putString(it.key.str(), entry.value) + is PrefType.FloatPref -> putFloat(it.key.str(), entry.value) } } } diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt index 39c81fd7dd..93ff06ecf9 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt @@ -139,6 +139,14 @@ class ConfigKtTest { config.isHideMediaWhenMobileNetwork, (u as PrefType.BoolPref).value ) + Keys.NoteContentFontSize -> Assertions.assertEquals( + config.noteContentFontSize, + (u as PrefType.FloatPref).value + ) + Keys.NoteHeaderFontSize -> Assertions.assertEquals( + config.noteHeaderFontSize, + (u as PrefType.FloatPref).value + ) } } } diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt index b1b751bca7..5b4d52d74e 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt @@ -100,6 +100,14 @@ class KeysKtTest { "IsHideMediaWhenMobileNetwork", key.str() ) + Keys.NoteContentFontSize -> Assertions.assertEquals( + "NoteContentFontSize", + key.str() + ) + Keys.NoteHeaderFontSize -> Assertions.assertEquals( + "NoteHeaderFontSize", + key.str() + ) } } } @@ -107,8 +115,8 @@ class KeysKtTest { @Test fun checkAllKeysCount() { - Assertions.assertEquals(27, Keys.allKeys.size) - Assertions.assertEquals(27, Keys.allKeys.map { it.str() }.toSet().size) + Assertions.assertEquals(29, Keys.allKeys.size) + Assertions.assertEquals(29, Keys.allKeys.map { it.str() }.toSet().size) } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt index 2b8466edaa..83b10e5fc8 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt @@ -50,6 +50,8 @@ data class IsAnalyticsCollectionEnabled( * @Param isEnableNoteDivider ノートの区切り線の有無 trueで有り falseで無し * @param isVisibleInstanceUrlInToolbar toolbar内にインスタンス情報を表示するか? * @param isHideMediaWhenMobileNetwork モバイルネットワークの時はメディアを表示しない + * @param noteHeaderFontSize ノートのヘッダー部分のテキストサイズ + * @param noteContentFontSize ノートのコンテンツ部分のテキストサイズ */ data class Config( val isSimpleEditorEnabled: Boolean, @@ -77,6 +79,8 @@ data class Config( val isEnableNoteDivider: Boolean, val isVisibleInstanceUrlInToolbar: Boolean, val isHideMediaWhenMobileNetwork: Boolean, + val noteHeaderFontSize: Float, + val noteContentFontSize: Float, ) { companion object @@ -131,7 +135,9 @@ object DefaultConfig { isEnableStreamingAPIAndNoteCapture = true, isEnableNoteDivider = true, isVisibleInstanceUrlInToolbar = true, - isHideMediaWhenMobileNetwork = false + isHideMediaWhenMobileNetwork = false, + noteContentFontSize = 15f, + noteHeaderFontSize = 15f, ) fun getRememberVisibilityConfig(accountId: Long): RememberVisibility.Remember { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt index 32489b992b..2ccc97147d 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt @@ -29,6 +29,8 @@ val Keys.Companion.allKeys by lazy { Keys.IsEnableNoteDivider, Keys.IsVisibleInstanceUrlInToolbar, Keys.IsHideMediaWhenMobileNetwork, + Keys.NoteHeaderFontSize, + Keys.NoteContentFontSize, ) } @@ -82,6 +84,10 @@ sealed interface Keys { object IsHideMediaWhenMobileNetwork : Keys + object NoteHeaderFontSize: Keys + + object NoteContentFontSize: Keys + companion object } @@ -114,5 +120,7 @@ fun Keys.str(): String { is Keys.IsEnableNoteDivider -> "IsEnableNoteDivider" is Keys.IsVisibleInstanceUrlInToolbar -> "IsVisibleInstanceUrlInToolbar" is Keys.IsHideMediaWhenMobileNetwork -> "IsHideMediaWhenMobileNetwork" + is Keys.NoteContentFontSize -> "NoteContentFontSize" + is Keys.NoteHeaderFontSize -> "NoteContentFontSize" } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/PrefType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/PrefType.kt index 097345b9e4..64c50021e4 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/PrefType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/PrefType.kt @@ -13,4 +13,8 @@ sealed interface PrefType { val value: Int ) : PrefType + data class FloatPref( + val value: Float + ) : PrefType + } From 324a08ad1f659bfef12a6ecb33e628589729c8bb Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:30:15 +0900 Subject: [PATCH 074/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E7=8A=B6?= =?UTF-8?q?=E6=85=8B=E3=82=92=E8=AA=AD=E3=81=BF=E5=8F=96=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/pinned/PinnedNoteFragment.kt | 5 +++++ .../milktea/note/timeline/TimelineFragment.kt | 5 +++++ .../milktea/note/timeline/TimelineListAdapter.kt | 11 +++++++---- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/pinned/PinnedNoteFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/pinned/PinnedNoteFragment.kt index ef48fd5cfe..bdd5e5fc48 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/pinned/PinnedNoteFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/pinned/PinnedNoteFragment.kt @@ -20,6 +20,7 @@ import net.pantasystem.milktea.common_navigation.AuthorizationArgs import net.pantasystem.milktea.common_navigation.AuthorizationNavigation import net.pantasystem.milktea.common_navigation.ChannelDetailNavigation import net.pantasystem.milktea.common_navigation.UserDetailNavigation +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentPinnedNotesBinding @@ -57,6 +58,9 @@ class PinnedNoteFragment : Fragment(R.layout.fragment_pinned_notes) { @Inject lateinit var channelDetailNavigation: ChannelDetailNavigation + @Inject + lateinit var configRepository: LocalConfigRepository + val notesViewModel: NotesViewModel by activityViewModels() val pinnedNotesViewModel: PinnedNotesViewModel by viewModels() @@ -66,6 +70,7 @@ class PinnedNoteFragment : Fragment(R.layout.fragment_pinned_notes) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val adapter = TimelineListAdapter( + configRepository = configRepository, viewLifecycleOwner, onRefreshAction = { diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt index 24c281021a..1b943d0d3e 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt @@ -31,6 +31,7 @@ import net.pantasystem.milktea.common_viewmodel.CurrentPageableTimelineViewModel import net.pantasystem.milktea.common_viewmodel.ScrollToTopViewModel import net.pantasystem.milktea.model.account.page.Page import net.pantasystem.milktea.model.account.page.Pageable +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentSwipeRefreshRecyclerViewBinding import net.pantasystem.milktea.note.timeline.viewmodel.TimeMachineEventViewModel @@ -108,6 +109,9 @@ class TimelineFragment : Fragment(R.layout.fragment_swipe_refresh_recycler_view) @Inject lateinit var channelDetailNavigation: ChannelDetailNavigation + @Inject + lateinit var configRepository: LocalConfigRepository + private val mBinding: FragmentSwipeRefreshRecyclerViewBinding by dataBinding() @@ -147,6 +151,7 @@ class TimelineFragment : Fragment(R.layout.fragment_swipe_refresh_recycler_view) val lm = LinearLayoutManager(this.requireContext()) _linearLayoutManager = lm val adapter = TimelineListAdapter( + configRepository = configRepository, viewLifecycleOwner, onRefreshAction = { mViewModel.loadInit() diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt index f5adc8017a..463f585b11 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt @@ -19,6 +19,7 @@ import com.google.android.flexbox.FlexboxLayoutManager import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.ItemHasReplyToNoteBinding import net.pantasystem.milktea.note.databinding.ItemNoteBinding @@ -33,6 +34,7 @@ import net.pantasystem.milktea.note.viewmodel.HasReplyToNoteViewData import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData class TimelineListAdapter( + private val configRepository: LocalConfigRepository, private val lifecycleOwner: LifecycleOwner, val onRefreshAction: () -> Unit, val onReauthenticateAction: () -> Unit, @@ -254,6 +256,7 @@ class TimelineListAdapter( } override fun onCreateViewHolder(p0: ViewGroup, p1: Int): TimelineListItemViewHolderBase { + val config = configRepository.get().getOrNull() return when(ViewHolderType.values()[p1]) { ViewHolderType.NormalNote -> { val binding = DataBindingUtil.inflate(LayoutInflater.from(p0.context), R.layout.item_note, p0, false) @@ -261,8 +264,8 @@ class TimelineListAdapter( binding.simpleNote.urlPreviewList.setRecycledViewPool(urlPreviewListRecyclerViewPool) binding.simpleNote.manyFilePreviewListView.setRecycledViewPool(manyFilePreviewListViewRecyclerViewPool) NoteFontSizeBinder.from(binding.simpleNote).bind( - headerFontSize = 15f, - contentFontSize = 15f, + headerFontSize = config?.noteHeaderFontSize ?: 15f, + contentFontSize = config?.noteContentFontSize ?: 15f, ) NoteViewHolder(binding) } @@ -272,8 +275,8 @@ class TimelineListAdapter( binding.simpleNote.urlPreviewList.setRecycledViewPool(urlPreviewListRecyclerViewPool) binding.simpleNote.manyFilePreviewListView.setRecycledViewPool(manyFilePreviewListViewRecyclerViewPool) NoteFontSizeBinder.from(binding.simpleNote).bind( - headerFontSize = 15f, - contentFontSize = 15f, + headerFontSize = config?.noteHeaderFontSize ?: 15f, + contentFontSize = config?.noteContentFontSize ?: 15f, ) HasReplyToNoteViewHolder(binding) } From e05eac81894533aa8f83fe527bf04247c4faeb59 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:40:44 +0900 Subject: [PATCH 075/432] fix --- .../src/main/java/net/pantasystem/milktea/model/setting/Keys.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt index 2ccc97147d..fc25b4fbbd 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt @@ -121,6 +121,6 @@ fun Keys.str(): String { is Keys.IsVisibleInstanceUrlInToolbar -> "IsVisibleInstanceUrlInToolbar" is Keys.IsHideMediaWhenMobileNetwork -> "IsHideMediaWhenMobileNetwork" is Keys.NoteContentFontSize -> "NoteContentFontSize" - is Keys.NoteHeaderFontSize -> "NoteContentFontSize" + is Keys.NoteHeaderFontSize -> "NoteHeaderFontSize" } } From 1beec2049c2830faa78f6f8f7d2f982d1143ff5e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:42:08 +0900 Subject: [PATCH 076/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E7=94=BB?= =?UTF-8?q?=E9=9D=A2=E3=81=AB=E9=A0=85=E7=9B=AE=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activities/SettingAppearanceActivity.kt | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index 8a06060d9a..92b27fd550 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -279,6 +279,35 @@ class SettingAppearanceActivity : AppCompatActivity() { modifier = Modifier.padding(horizontal = 16.dp) ) + Text( + "Note header font size(${currentConfigState.noteHeaderFontSize}sp)", + modifier = Modifier.padding(horizontal = 16.dp) + ) + Slider( + value = currentConfigState.noteHeaderFontSize, + valueRange = 10f..24f, + onValueChange = { + currentConfigState = + currentConfigState.copy(noteHeaderFontSize = it) + }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + + Text( + "Note content font size(${currentConfigState.noteContentFontSize}sp)", + modifier = Modifier.padding(horizontal = 16.dp) + ) + Slider( + value = currentConfigState.noteContentFontSize, + valueRange = 10f..24f, + onValueChange = { + currentConfigState = + currentConfigState.copy(noteContentFontSize = it) + }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + + SettingSwitchTile( checked = currentConfigState.isEnableNoteDivider, onChanged = { From fc574626cc6323c15f343647a83dd6056125a20e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:49:11 +0900 Subject: [PATCH 077/432] =?UTF-8?q?feat:=20=E9=80=9A=E7=9F=A5=E3=81=AB?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=82=8B=E3=83=8E=E3=83=BC?= =?UTF-8?q?=E3=83=88=E3=81=AE=E3=83=95=E3=82=A9=E3=83=B3=E3=83=88=E3=82=B5?= =?UTF-8?q?=E3=82=A4=E3=82=BA=E3=81=8C=E8=A8=AD=E5=AE=9A=E3=81=AE=E7=8A=B6?= =?UTF-8?q?=E6=85=8B=E3=81=AB=E5=BF=9C=E3=81=98=E3=81=A6=E5=A4=89=E3=82=8F?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/notification/NotificationFragment.kt | 5 +++++ .../milktea/notification/NotificationListAdapter.kt | 11 ++++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt index b4651868c4..17b8dca741 100644 --- a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt +++ b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt @@ -34,6 +34,7 @@ import net.pantasystem.milktea.common_navigation.UserDetailNavigation import net.pantasystem.milktea.common_viewmodel.CurrentPageableTimelineViewModel import net.pantasystem.milktea.common_viewmodel.ScrollToTopViewModel import net.pantasystem.milktea.model.account.page.Pageable +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.view.NoteCardActionHandler import net.pantasystem.milktea.note.viewmodel.NotesViewModel import net.pantasystem.milktea.notification.databinding.FragmentNotificationBinding @@ -72,6 +73,9 @@ class NotificationFragment : Fragment(R.layout.fragment_notification) { @Inject lateinit var applyMenuTint: ApplyMenuTint + @Inject + lateinit var configRepository: LocalConfigRepository + private val mBinding: FragmentNotificationBinding by dataBinding() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -80,6 +84,7 @@ class NotificationFragment : Fragment(R.layout.fragment_notification) { mLinearLayoutManager = LinearLayoutManager(requireContext()) val adapter = NotificationListAdapter( + configRepository, diffUtilItemCallBack, mViewModel, viewLifecycleOwner, diff --git a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationListAdapter.kt b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationListAdapter.kt index 05535e8f00..9b93af5c96 100644 --- a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationListAdapter.kt +++ b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationListAdapter.kt @@ -14,7 +14,9 @@ import com.google.android.flexbox.* import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.reaction.ReactionCountAdapter +import net.pantasystem.milktea.note.timeline.NoteFontSizeBinder import net.pantasystem.milktea.note.view.NoteCardAction import net.pantasystem.milktea.note.view.NoteCardActionListenerAdapter import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData @@ -27,6 +29,7 @@ import net.pantasystem.milktea.notification.viewmodel.NotificationViewModel class NotificationListAdapter constructor( + private val configRepository: LocalConfigRepository, diffUtilCallBack: DiffUtil.ItemCallback, val notificationViewModel: NotificationViewModel, private val lifecycleOwner: LifecycleOwner, @@ -112,7 +115,13 @@ class NotificationListAdapter constructor( lifecycleOwner, notificationViewModel, noteCardActionListenerAdapter - ) + ).also { + val config = configRepository.get().getOrNull() + NoteFontSizeBinder.from(it.binding.simpleNote).bind( + contentFontSize = config?.noteContentFontSize ?: 15f, + headerFontSize = config?.noteHeaderFontSize ?: 15f, + ) + } } } } From ba8b428b7b762dd8ca903e79b98856074cb03e7c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:53:45 +0900 Subject: [PATCH 078/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E5=B1=A5=E6=AD=B4=E3=81=AB=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=95=E3=82=8C=E3=82=8B=E3=83=8E=E3=83=BC=E3=83=88?= =?UTF-8?q?=E3=81=AE=E3=83=95=E3=82=A9=E3=83=B3=E3=83=88=E3=82=B5=E3=82=A4?= =?UTF-8?q?=E3=82=BA=E3=81=8C=E8=A8=AD=E5=AE=9A=E3=81=AE=E7=8A=B6=E6=85=8B?= =?UTF-8?q?=E3=81=AB=E5=BF=9C=E3=81=98=E3=81=A6=E5=A4=89=E3=82=8F=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/reaction/UserReactionsFragment.kt | 5 +++++ .../user/reaction/UserReactionsListAdapter.kt | 21 +++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsFragment.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsFragment.kt index 4e1a7793ff..19dc6e422b 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsFragment.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsFragment.kt @@ -19,6 +19,7 @@ import net.pantasystem.milktea.common.PageableState import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common_navigation.ChannelDetailNavigation import net.pantasystem.milktea.common_navigation.UserDetailNavigation +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.note.view.NoteCardActionHandler import net.pantasystem.milktea.note.view.NoteCardActionListenerAdapter @@ -50,6 +51,9 @@ class UserReactionsFragment : Fragment(R.layout.fragment_user_reactions) { @Inject lateinit var channelDetailNavigation: ChannelDetailNavigation + @Inject + lateinit var configRepository: LocalConfigRepository + private val binding: FragmentUserReactionsBinding by dataBinding() private val viewModel by viewModels() private val notesViewModel by activityViewModels() @@ -61,6 +65,7 @@ class UserReactionsFragment : Fragment(R.layout.fragment_user_reactions) { super.onViewCreated(view, savedInstanceState) val adapter = UserReactionsListAdapter( + configRepository, lifecycleOwner = viewLifecycleOwner, noteCardActionHandler = NoteCardActionListenerAdapter { NoteCardActionHandler( diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsListAdapter.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsListAdapter.kt index 3a4f857325..be0abe8c96 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsListAdapter.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsListAdapter.kt @@ -13,26 +13,29 @@ import com.google.android.flexbox.FlexboxLayoutManager import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.reaction.ReactionCountAdapter +import net.pantasystem.milktea.note.timeline.NoteFontSizeBinder import net.pantasystem.milktea.note.view.NoteCardActionListenerAdapter import net.pantasystem.milktea.user.R import net.pantasystem.milktea.user.databinding.ItemUserReactionBinding class UserReactionsListAdapter( + private val configRepository: LocalConfigRepository, private val lifecycleOwner: LifecycleOwner, - private val noteCardActionHandler: NoteCardActionListenerAdapter + private val noteCardActionHandler: NoteCardActionListenerAdapter, ) : ListAdapter( object : DiffUtil.ItemCallback() { override fun areContentsTheSame( oldItem: UserReactionBindingModel, - newItem: UserReactionBindingModel + newItem: UserReactionBindingModel, ): Boolean { return oldItem.reaction == newItem.reaction } override fun areItemsTheSame( oldItem: UserReactionBindingModel, - newItem: UserReactionBindingModel + newItem: UserReactionBindingModel, ): Boolean { return oldItem.reaction.id == newItem.reaction.id } @@ -43,7 +46,17 @@ class UserReactionsListAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserReactionViewHolder { - val binding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_user_reaction, parent, false) + val config = configRepository.get().getOrNull() + val binding = DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.item_user_reaction, + parent, + false + ) + NoteFontSizeBinder.from(binding.simpleNote).bind( + headerFontSize = config?.noteHeaderFontSize ?: 15f, + contentFontSize = config?.noteContentFontSize ?: 15f + ) return UserReactionViewHolder(lifecycleOwner, binding, noteCardActionHandler) } } From 18005599206eab2a91bbac207f2ddd6274d8f3da Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:56:43 +0900 Subject: [PATCH 079/432] =?UTF-8?q?fix:=20=E6=AD=A3=E3=81=97=E3=81=8F?= =?UTF-8?q?=E5=80=A4=E3=81=8C=E5=8F=96=E5=BE=97=E3=81=95=E3=82=8C=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/data/infrastructure/settings/Config.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt index 4e1662ff05..fd1c5872d6 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt @@ -30,6 +30,9 @@ fun SharedPreferences.getPrefTypes(keys: Set = Keys.allKeys): Map { PrefType.IntPref(value as Int) } + Float::class -> { + PrefType.FloatPref(value as Float) + } else -> null } } From 0a6dd704f160c671d91c6048805fd915e41f87d0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 6 May 2023 23:59:11 +0900 Subject: [PATCH 080/432] =?UTF-8?q?feat:=20=E3=82=A2=E3=83=97=E3=83=AA?= =?UTF-8?q?=E3=81=AE=E5=86=8D=E8=B5=B7=E5=8B=95=E3=81=8C=E5=BF=85=E8=A6=81?= =?UTF-8?q?=E3=81=A7=E3=81=82=E3=82=8B=E3=81=93=E3=81=A8=E3=82=92=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/activities/SettingAppearanceActivity.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index 92b27fd550..78ea022f55 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -283,6 +283,12 @@ class SettingAppearanceActivity : AppCompatActivity() { "Note header font size(${currentConfigState.noteHeaderFontSize}sp)", modifier = Modifier.padding(horizontal = 16.dp) ) + Text( + stringResource(id = R.string.settings_app_restart_required), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colors.error, + fontSize = 14.sp + ) Slider( value = currentConfigState.noteHeaderFontSize, valueRange = 10f..24f, @@ -297,6 +303,12 @@ class SettingAppearanceActivity : AppCompatActivity() { "Note content font size(${currentConfigState.noteContentFontSize}sp)", modifier = Modifier.padding(horizontal = 16.dp) ) + Text( + stringResource(id = R.string.settings_app_restart_required), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colors.error, + fontSize = 14.sp + ) Slider( value = currentConfigState.noteContentFontSize, valueRange = 10f..24f, From 6c43fd7e9a1b1197b176703e48f94f757ea2cf6f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 00:03:46 +0900 Subject: [PATCH 081/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=81=A8=E3=81=97=E3=81=A6?= =?UTF-8?q?=E5=AE=9A=E7=BE=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_resource/src/main/res/values-ja/strings.xml | 2 ++ .../common_resource/src/main/res/values-zh/strings.xml | 2 ++ .../common_resource/src/main/res/values/strings.xml | 3 +++ .../setting/activities/SettingAppearanceActivity.kt | 10 ++++++++-- 4 files changed, 15 insertions(+), 2 deletions(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index a0149413c9..65b655dc97 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -590,6 +590,8 @@ 無期限 リモートで表示 フォローされています + ノートヘッダー文字サイズ(%fsp) + ノートコンテンツ文字サイズ(%fsp) diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 2fd8e86681..0a112fec12 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -584,6 +584,8 @@ 无限期 远程查看 正在关注你 + Note content font size(%fsp) + Note header font size(%fsp) \ No newline at end of file diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 40d0f1f92d..bf6698eee6 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -567,6 +567,9 @@ Only Media About Milktea Source code(GitHub) + Note content font size(%fsp) + Note header font size(%fsp) + diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index 78ea022f55..5ebbfda8e8 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -280,7 +280,10 @@ class SettingAppearanceActivity : AppCompatActivity() { ) Text( - "Note header font size(${currentConfigState.noteHeaderFontSize}sp)", + stringResource( + id = R.string.settings_note_header_font_size, + currentConfigState.noteHeaderFontSize + ), modifier = Modifier.padding(horizontal = 16.dp) ) Text( @@ -300,7 +303,10 @@ class SettingAppearanceActivity : AppCompatActivity() { ) Text( - "Note content font size(${currentConfigState.noteContentFontSize}sp)", + stringResource( + id = R.string.settings_note_content_font_size, + currentConfigState.noteContentFontSize + ), modifier = Modifier.padding(horizontal = 16.dp) ) Text( From bab7351b7f92fb27e3f0fabe66312674978d08ca Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 00:11:38 +0900 Subject: [PATCH 082/432] =?UTF-8?q?feat:=20=E8=A9=B3=E7=B4=B0=E7=94=BB?= =?UTF-8?q?=E9=9D=A2=E3=81=A7=E3=82=82=E7=8A=B6=E6=85=8B=E3=81=8C=E5=8F=8D?= =?UTF-8?q?=E6=98=A0=E3=81=95=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../detail/NoteChildConversationAdapter.kt | 9 ++ .../milktea/note/detail/NoteDetailAdapter.kt | 103 ++++++++++++++---- .../milktea/note/detail/NoteDetailFragment.kt | 5 + 3 files changed, 94 insertions(+), 23 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteChildConversationAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteChildConversationAdapter.kt index 2a49010cfd..b53073a387 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteChildConversationAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteChildConversationAdapter.kt @@ -14,14 +14,18 @@ import com.google.android.flexbox.* import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import net.pantasystem.milktea.model.setting.DefaultConfig +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.ItemSimpleNoteBinding import net.pantasystem.milktea.note.reaction.ReactionCountAdapter +import net.pantasystem.milktea.note.timeline.NoteFontSizeBinder import net.pantasystem.milktea.note.view.NoteCardAction import net.pantasystem.milktea.note.view.NoteCardActionListenerAdapter import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData class NoteChildConversationAdapter( + val configRepository: LocalConfigRepository, val lifecycleOwner: LifecycleOwner, val onAction: (NoteCardAction) -> Unit, ) : ListAdapter(object : DiffUtil.ItemCallback(){ @@ -50,6 +54,11 @@ class NoteChildConversationAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SimpleNoteHolder { val binding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_simple_note, parent, false) + val config = configRepository.get().getOrNull() ?: DefaultConfig.config + NoteFontSizeBinder.from(binding).bind( + headerFontSize = config.noteHeaderFontSize, + contentFontSize = config.noteContentFontSize, + ) return SimpleNoteHolder(binding) } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailAdapter.kt index 2f2da2c961..bdd341f8dd 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailAdapter.kt @@ -16,6 +16,8 @@ import com.google.android.flexbox.* import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import net.pantasystem.milktea.model.setting.DefaultConfig +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.ItemConversationBinding import net.pantasystem.milktea.note.databinding.ItemDetailNoteBinding @@ -24,29 +26,35 @@ import net.pantasystem.milktea.note.detail.viewmodel.NoteConversationViewData import net.pantasystem.milktea.note.detail.viewmodel.NoteDetailViewData import net.pantasystem.milktea.note.detail.viewmodel.NoteDetailViewModel import net.pantasystem.milktea.note.reaction.ReactionCountAdapter +import net.pantasystem.milktea.note.timeline.NoteFontSizeBinder import net.pantasystem.milktea.note.view.NoteCardAction import net.pantasystem.milktea.note.view.NoteCardActionListenerAdapter import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData class NoteDetailAdapter( + private val configRepository: LocalConfigRepository, private val noteDetailViewModel: NoteDetailViewModel, private val viewLifecycleOwner: LifecycleOwner, - diffUtil: DiffUtil.ItemCallback = object : DiffUtil.ItemCallback(){ + diffUtil: DiffUtil.ItemCallback = object : + DiffUtil.ItemCallback() { override fun areContentsTheSame( oldItem: PlaneNoteViewData, - newItem: PlaneNoteViewData + newItem: PlaneNoteViewData, ): Boolean { return oldItem.id == newItem.id } - override fun areItemsTheSame(oldItem: PlaneNoteViewData, newItem: PlaneNoteViewData): Boolean { + override fun areItemsTheSame( + oldItem: PlaneNoteViewData, + newItem: PlaneNoteViewData, + ): Boolean { return oldItem.id == newItem.id } }, val onAction: (NoteCardAction) -> Unit, -) : ListAdapter(diffUtil){ +) : ListAdapter(diffUtil) { - companion object{ + companion object { const val NOTE = 0 const val DETAIL = 1 const val CONVERSATION = 2 @@ -68,29 +76,73 @@ class NoteDetailAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - return when(viewType){ - NOTE ->{ - val binding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_note, parent, false) + val config = configRepository.get().getOrNull() ?: DefaultConfig.config + return when (viewType) { + NOTE -> { + val binding = DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.item_note, + parent, + false + ) + NoteFontSizeBinder.from(binding.simpleNote).bind( + headerFontSize = config.noteHeaderFontSize, + contentFontSize = config.noteContentFontSize, + ) NoteHolder(binding) } - DETAIL ->{ - val binding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_detail_note, parent, false) + DETAIL -> { + val binding = DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.item_detail_note, + parent, + false + ) + NoteFontSizeBinder( + contentViews = NoteFontSizeBinder.ContentViews( + cwView = binding.cw, + textView = binding.text, + ), + userInfoViews = NoteFontSizeBinder.HeaderViews( + nameView = binding.mainName, + userNameView = binding.subName, + elapsedTimeView = null, + ), + quoteToContentViews = NoteFontSizeBinder.ContentViews( + cwView = binding.subCw, + textView = binding.subNoteText, + ), + quoteToUserInfoViews = NoteFontSizeBinder.HeaderViews( + nameView = binding.subNoteMainName, + userNameView = binding.subNoteSubName, + elapsedTimeView = null, + ) + ).bind( + headerFontSize = config.noteHeaderFontSize, + contentFontSize = config.noteContentFontSize + ) DetailNoteHolder(binding) } - CONVERSATION ->{ - val binding = DataBindingUtil.inflate(LayoutInflater.from(parent.context), R.layout.item_conversation, parent, false) + CONVERSATION -> { + val binding = DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.item_conversation, + parent, + false + ) ConversationHolder(binding) } else -> throw IllegalArgumentException("NOTE, DETAIL, CONVERSATIONしか許可されていません") } } + override fun onBindViewHolder(holder: ViewHolder, position: Int) { val note = getItem(position) //val reactionAdapter = createReactionAdapter(note) //val layoutManager = LinearLayoutManager(holder.itemView.context) - when(holder){ - is NoteHolder ->{ + when (holder) { + is NoteHolder -> { holder.binding.note = note setReactionCounter(note, holder.binding.simpleNote.reactionView) @@ -98,22 +150,26 @@ class NoteDetailAdapter( holder.binding.noteCardActionListener = noteCardActionListenerAdapter holder.binding.executePendingBindings() } - is DetailNoteHolder ->{ + is DetailNoteHolder -> { holder.binding.note = note as NoteDetailViewData holder.binding.noteCardActionListener = noteCardActionListenerAdapter setReactionCounter(note, holder.binding.reactionView) holder.binding.lifecycleOwner = viewLifecycleOwner holder.binding.executePendingBindings() } - is ConversationHolder ->{ - Log.d("NoteDetailAdapter", "conversation: ${(note as NoteConversationViewData).conversation.value?.size}") + is ConversationHolder -> { + Log.d( + "NoteDetailAdapter", + "conversation: ${(note as NoteConversationViewData).conversation.value?.size}" + ) holder.binding.childrenViewData = note setReactionCounter(note, holder.binding.childNote.reactionView) holder.binding.noteDetailViewModel = noteDetailViewModel - val adapter = NoteChildConversationAdapter(viewLifecycleOwner, onAction) + val adapter = NoteChildConversationAdapter(configRepository, viewLifecycleOwner, onAction) holder.binding.conversationView.adapter = adapter - holder.binding.conversationView.layoutManager = LinearLayoutManager(holder.itemView.context) + holder.binding.conversationView.layoutManager = + LinearLayoutManager(holder.itemView.context) holder.binding.noteCardActionListener = noteCardActionListenerAdapter note.conversation.observe(viewLifecycleOwner) { adapter.submitList(it) @@ -129,7 +185,7 @@ class NoteDetailAdapter( private var job: Job? = null - private fun setReactionCounter(note: PlaneNoteViewData, reactionView: RecyclerView){ + private fun setReactionCounter(note: PlaneNoteViewData, reactionView: RecyclerView) { val reactionList = note.reactionCountsViewData.value val adapter = ReactionCountAdapter { @@ -143,10 +199,11 @@ class NoteDetailAdapter( job?.cancel() job = note.reactionCountsViewData.onEach { adapter.submitList(it.toList()) - }.flowWithLifecycle(viewLifecycleOwner.lifecycle).launchIn(viewLifecycleOwner.lifecycleScope) + }.flowWithLifecycle(viewLifecycleOwner.lifecycle) + .launchIn(viewLifecycleOwner.lifecycleScope) val exLayoutManager = reactionView.layoutManager - if(exLayoutManager !is FlexboxLayoutManager){ + if (exLayoutManager !is FlexboxLayoutManager) { val flexBoxLayoutManager = FlexboxLayoutManager(reactionView.context) flexBoxLayoutManager.flexDirection = FlexDirection.ROW flexBoxLayoutManager.flexWrap = FlexWrap.WRAP @@ -155,7 +212,7 @@ class NoteDetailAdapter( reactionView.layoutManager = flexBoxLayoutManager } - if(reactionList.isNotEmpty()){ + if (reactionList.isNotEmpty()) { reactionView.visibility = View.VISIBLE } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailFragment.kt index 8ae3f76cd4..d648d38fd9 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailFragment.kt @@ -23,6 +23,7 @@ import net.pantasystem.milktea.common_viewmodel.CurrentPageableTimelineViewModel import net.pantasystem.milktea.model.account.page.Page import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.notes.Note +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentNoteDetailBinding import net.pantasystem.milktea.note.detail.viewmodel.NoteDetailViewModel @@ -85,6 +86,9 @@ class NoteDetailFragment : Fragment(R.layout.fragment_note_detail) { @Inject lateinit var channelDetailNavigation: ChannelDetailNavigation + @Inject + lateinit var configRepository: LocalConfigRepository + @Suppress("DEPRECATION") val page: Pageable.Show by lazy { (arguments?.getSerializable(EXTRA_PAGE) as? Page)?.pageable() as? Pageable.Show @@ -108,6 +112,7 @@ class NoteDetailFragment : Fragment(R.layout.fragment_note_detail) { super.onViewCreated(view, savedInstanceState) val adapter = NoteDetailAdapter( + configRepository = configRepository, noteDetailViewModel = noteDetailViewModel, viewLifecycleOwner = viewLifecycleOwner ) { From fcb092ffd232cad37940d9c2673fb922e91cd0be Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 00:21:25 +0900 Subject: [PATCH 083/432] =?UTF-8?q?feat:=20misskey=20dev=E8=87=AA=E4=BD=93?= =?UTF-8?q?=E3=81=8C=E4=B8=8D=E5=AE=89=E5=AE=9A=E3=81=AA=E3=81=AE=E3=81=A7?= =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=81=8B=E3=82=89=E9=99=A4=E5=A4=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../nodeinfo/NodeInfoFetcherImplTest.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/nodeinfo/NodeInfoFetcherImplTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/nodeinfo/NodeInfoFetcherImplTest.kt index a6668452d6..3e4dc8407d 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/nodeinfo/NodeInfoFetcherImplTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/nodeinfo/NodeInfoFetcherImplTest.kt @@ -32,12 +32,12 @@ internal class NodeInfoFetcherImplTest { assertNotNull(impl.fetch("misskey.io")) } - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun fetch_GiveMisskeyDev() = runTest { - val impl = NodeInfoFetcherImpl(NodeInfoAPIBuilderImpl(DefaultOkHttpClientProvider()), loggerFactory) - assertNotNull(impl.fetch("misskey.dev")) - } +// @OptIn(ExperimentalCoroutinesApi::class) +// @Test +// fun fetch_GiveMisskeyDev() = runTest { +// val impl = NodeInfoFetcherImpl(NodeInfoAPIBuilderImpl(DefaultOkHttpClientProvider()), loggerFactory) +// assertNotNull(impl.fetch("misskey.dev")) +// } @OptIn(ExperimentalCoroutinesApi::class) @Test From 065d3611c6e440cc5656c03bf7c46c5b64e93892 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 00:28:16 +0900 Subject: [PATCH 084/432] =?UTF-8?q?feat:=20=E3=83=87=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=AB=E3=83=88=E5=80=A4=E3=82=92=E5=85=B1=E9=80=9A=E5=8C=96?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/timeline/TimelineListAdapter.kt | 13 ++++++++----- .../milktea/notification/NotificationListAdapter.kt | 9 ++++++--- .../user/reaction/UserReactionsListAdapter.kt | 9 ++++++--- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt index 463f585b11..de397a56b7 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineListAdapter.kt @@ -19,6 +19,7 @@ import com.google.android.flexbox.FlexboxLayoutManager import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import net.pantasystem.milktea.model.setting.DefaultConfig import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.ItemHasReplyToNoteBinding @@ -256,7 +257,9 @@ class TimelineListAdapter( } override fun onCreateViewHolder(p0: ViewGroup, p1: Int): TimelineListItemViewHolderBase { - val config = configRepository.get().getOrNull() + val config = configRepository.get().getOrElse { + DefaultConfig.config + } return when(ViewHolderType.values()[p1]) { ViewHolderType.NormalNote -> { val binding = DataBindingUtil.inflate(LayoutInflater.from(p0.context), R.layout.item_note, p0, false) @@ -264,8 +267,8 @@ class TimelineListAdapter( binding.simpleNote.urlPreviewList.setRecycledViewPool(urlPreviewListRecyclerViewPool) binding.simpleNote.manyFilePreviewListView.setRecycledViewPool(manyFilePreviewListViewRecyclerViewPool) NoteFontSizeBinder.from(binding.simpleNote).bind( - headerFontSize = config?.noteHeaderFontSize ?: 15f, - contentFontSize = config?.noteContentFontSize ?: 15f, + headerFontSize = config.noteHeaderFontSize, + contentFontSize = config.noteContentFontSize, ) NoteViewHolder(binding) } @@ -275,8 +278,8 @@ class TimelineListAdapter( binding.simpleNote.urlPreviewList.setRecycledViewPool(urlPreviewListRecyclerViewPool) binding.simpleNote.manyFilePreviewListView.setRecycledViewPool(manyFilePreviewListViewRecyclerViewPool) NoteFontSizeBinder.from(binding.simpleNote).bind( - headerFontSize = config?.noteHeaderFontSize ?: 15f, - contentFontSize = config?.noteContentFontSize ?: 15f, + headerFontSize = config.noteHeaderFontSize, + contentFontSize = config.noteContentFontSize, ) HasReplyToNoteViewHolder(binding) } diff --git a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationListAdapter.kt b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationListAdapter.kt index 9b93af5c96..f4f5911161 100644 --- a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationListAdapter.kt +++ b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationListAdapter.kt @@ -14,6 +14,7 @@ import com.google.android.flexbox.* import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import net.pantasystem.milktea.model.setting.DefaultConfig import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.reaction.ReactionCountAdapter import net.pantasystem.milktea.note.timeline.NoteFontSizeBinder @@ -116,10 +117,12 @@ class NotificationListAdapter constructor( notificationViewModel, noteCardActionListenerAdapter ).also { - val config = configRepository.get().getOrNull() + val config = configRepository.get().getOrElse { + DefaultConfig.config + } NoteFontSizeBinder.from(it.binding.simpleNote).bind( - contentFontSize = config?.noteContentFontSize ?: 15f, - headerFontSize = config?.noteHeaderFontSize ?: 15f, + contentFontSize = config.noteContentFontSize, + headerFontSize = config.noteHeaderFontSize, ) } } diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsListAdapter.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsListAdapter.kt index be0abe8c96..de2d1efda6 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsListAdapter.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsListAdapter.kt @@ -13,6 +13,7 @@ import com.google.android.flexbox.FlexboxLayoutManager import kotlinx.coroutines.Job import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import net.pantasystem.milktea.model.setting.DefaultConfig import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.reaction.ReactionCountAdapter import net.pantasystem.milktea.note.timeline.NoteFontSizeBinder @@ -46,7 +47,9 @@ class UserReactionsListAdapter( } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserReactionViewHolder { - val config = configRepository.get().getOrNull() + val config = configRepository.get().getOrElse { + DefaultConfig.config + } val binding = DataBindingUtil.inflate( LayoutInflater.from(parent.context), R.layout.item_user_reaction, @@ -54,8 +57,8 @@ class UserReactionsListAdapter( false ) NoteFontSizeBinder.from(binding.simpleNote).bind( - headerFontSize = config?.noteHeaderFontSize ?: 15f, - contentFontSize = config?.noteContentFontSize ?: 15f + headerFontSize = config.noteHeaderFontSize, + contentFontSize = config.noteContentFontSize ) return UserReactionViewHolder(lifecycleOwner, binding, noteCardActionHandler) } From 706c121543724d568eea1775b41285bd1dfc5722 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 02:54:14 +0900 Subject: [PATCH 085/432] fix --- modules/common_resource/src/main/res/values-ja/strings.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 65b655dc97..dde3f200da 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -590,8 +590,8 @@ 無期限 リモートで表示 フォローされています - ノートヘッダー文字サイズ(%fsp) - ノートコンテンツ文字サイズ(%fsp) + ノートコンテンツ文字サイズ(%fsp) + ノートヘッダー文字サイズ(%fsp) From fdbdcae22774409a19883a0acec32fcc7f5b2ae1 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 14:14:47 +0900 Subject: [PATCH 086/432] =?UTF-8?q?feat:=20=E7=94=BB=E9=9D=A2=E3=82=92?= =?UTF-8?q?=E5=88=B6=E5=BE=A1=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AE?= =?UTF-8?q?ViewModel=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/SearchTopViewModel.kt | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt new file mode 100644 index 0000000000..88b2fc270f --- /dev/null +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt @@ -0,0 +1,74 @@ +package net.pantasystem.milktea.search + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import net.pantasystem.milktea.app_store.account.AccountStore +import net.pantasystem.milktea.common_android.resource.StringSource +import net.pantasystem.milktea.model.account.Account +import javax.inject.Inject + +@HiltViewModel +class SearchTopViewModel @Inject constructor( + val accountStore: AccountStore, +) : ViewModel() { + private val currentAccount = accountStore.observeCurrentAccount.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + null + ) + + val uiState = currentAccount.map { + SearchTopUiState( + currentAccount = it, + tabItems = when(it?.instanceType) { + Account.InstanceType.MISSKEY -> { + listOf( + SearchTopTabItem( + StringSource(R.string.title_featured), + SearchTopTabItem.TabType.MisskeyFeatured, + ), + SearchTopTabItem( + StringSource(R.string.explore), + SearchTopTabItem.TabType.MisskeyExploreUsers, + ), + SearchTopTabItem( + StringSource(R.string.explore_fediverse), + SearchTopTabItem.TabType.MisskeyExploreFediverseUsers, + ) + ) + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + listOf( + SearchTopTabItem( + StringSource(R.string.title_featured), + SearchTopTabItem.TabType.MastodonTrends, + ), + ) + } + null -> emptyList() + } + ) + } + +} + +data class SearchTopUiState( + val currentAccount: Account?, + val tabItems: List, +) + +data class SearchTopTabItem( + val title: StringSource, + val type: TabType, +) { + enum class TabType { + MisskeyFeatured, + MastodonTrends, + MisskeyExploreUsers, + MisskeyExploreFediverseUsers + } +} \ No newline at end of file From 7f1acbc43efbd43cbd2a56e3d019486d3b53e185 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 14:24:54 +0900 Subject: [PATCH 087/432] =?UTF-8?q?feat:=20ViewPager2=E3=82=92=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/SearchTopFragment.kt | 95 ++++++++++++------- .../main/res/layout/fragment_search_top.xml | 2 +- 2 files changed, 64 insertions(+), 33 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt index 531044f974..02fd2480d6 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt @@ -1,8 +1,5 @@ -@file:Suppress("DEPRECATION") - package net.pantasystem.milktea.search -import android.content.Context import android.content.Intent import android.os.Bundle import android.view.Menu @@ -12,13 +9,19 @@ import android.view.View import androidx.core.view.MenuHost import androidx.core.view.MenuProvider import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentManager -import androidx.fragment.app.FragmentPagerAdapter +import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.DiffUtil +import androidx.viewpager2.adapter.FragmentStateAdapter +import com.google.android.material.tabs.TabLayoutMediator import com.wada811.databinding.dataBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import net.pantasystem.milktea.common.ui.ApplyMenuTint import net.pantasystem.milktea.common.ui.ToolbarSetter import net.pantasystem.milktea.common_android_ui.PageableFragmentFactory @@ -42,12 +45,19 @@ class SearchTopFragment : Fragment(R.layout.fragment_search_top) { @Inject internal lateinit var pageableFragmentFactory: PageableFragmentFactory + val viewModel: SearchTopViewModel by viewModels() + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - - mBinding.searchViewPager.adapter = - SearchPagerAdapter(this.childFragmentManager, requireContext(), pageableFragmentFactory) - mBinding.searchTabLayout.setupWithViewPager(mBinding.searchViewPager) + val adapter = SearchPagerAdapterV2(pageableFragmentFactory, this) + + mBinding.searchViewPager.adapter = adapter + TabLayoutMediator( + mBinding.searchTabLayout, + mBinding.searchViewPager + ) { tab, position -> + tab.text = adapter.tabs[position].title.getString(requireContext()) + }.attach() (requireActivity() as MenuHost).addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menuInflater.inflate(R.menu.search_top_menu, menu) @@ -71,6 +81,10 @@ class SearchTopFragment : Fragment(R.layout.fragment_search_top) { return false } }, viewLifecycleOwner, Lifecycle.State.RESUMED) + + viewModel.uiState.onEach { + adapter.submitList(it.tabItems) + }.flowWithLifecycle(viewLifecycleOwner.lifecycle).launchIn(viewLifecycleOwner.lifecycleScope) } @@ -83,34 +97,51 @@ class SearchTopFragment : Fragment(R.layout.fragment_search_top) { } } +} + +class SearchPagerAdapterV2( + private val pageableFragmentFactory: PageableFragmentFactory, + fragment: Fragment +) : FragmentStateAdapter(fragment) { + + var tabs: List = emptyList() + private set + + override fun getItemCount(): Int { + return tabs.size + } - class SearchPagerAdapter( - supportFragmentManager: FragmentManager, - context: Context, - private val pageableFragmentFactory: PageableFragmentFactory, - ) : FragmentPagerAdapter(supportFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) { - private val tabList = - listOf( - context.getString(R.string.title_featured), - context.getString(R.string.explore), - context.getString(R.string.explore_fediverse) - ) - - override fun getCount(): Int { - return tabList.size + override fun createFragment(position: Int): Fragment { + val item = tabs[position] + return when(item.type) { + SearchTopTabItem.TabType.MisskeyFeatured -> pageableFragmentFactory.create(Pageable.Featured(null)) + SearchTopTabItem.TabType.MastodonTrends -> pageableFragmentFactory.create(Pageable.Featured(null)) + SearchTopTabItem.TabType.MisskeyExploreUsers -> ExploreFragment.newInstance(ExploreType.Local) + SearchTopTabItem.TabType.MisskeyExploreFediverseUsers -> ExploreFragment.newInstance(ExploreType.Fediverse) } + } - override fun getItem(position: Int): Fragment { - return when (position) { - 0 -> pageableFragmentFactory.create(Pageable.Featured(null)) - 1 -> ExploreFragment.newInstance(ExploreType.Local) - 2 -> ExploreFragment.newInstance(ExploreType.Fediverse) - else -> throw IllegalArgumentException("range 0..1, list:$tabList") + fun submitList(list: List) { + val old = tabs + tabs = list + val callback = object : DiffUtil.Callback() { + override fun getOldListSize(): Int { + return old.size + } + + override fun getNewListSize(): Int { + return list.size } - } - override fun getPageTitle(position: Int): CharSequence { - return tabList[position] + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == list[newItemPosition] + } + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + return old[oldItemPosition] == list[newItemPosition] + } } + val result = DiffUtil.calculateDiff(callback) + result.dispatchUpdatesTo(this) } } \ No newline at end of file diff --git a/modules/features/search/src/main/res/layout/fragment_search_top.xml b/modules/features/search/src/main/res/layout/fragment_search_top.xml index 94587b3117..416571cea8 100644 --- a/modules/features/search/src/main/res/layout/fragment_search_top.xml +++ b/modules/features/search/src/main/res/layout/fragment_search_top.xml @@ -40,7 +40,7 @@ - Date: Sun, 7 May 2023 14:26:27 +0900 Subject: [PATCH 088/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=83=96=E3=81=AE?= =?UTF-8?q?=E7=A8=AE=E5=88=A5=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/model/account/page/PageType.kt | 1 + .../pantasystem/milktea/model/account/page/Pageable.kt | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt index e7e594f2e1..a589b98963 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt @@ -32,6 +32,7 @@ enum class PageType(val defaultName: String, val label: String){ MASTODON_BOOKMARK_TIMELINE("MastodonBookmarkTimeline", "MASTODON_BOOKMARK_TIMELINE"), MASTODON_SEARCH_TIMELINE("MastodonSearchTimeline", "MASTODON_SEARCH_TIMELINE"), MASTODON_TAG_TIMELINE("MastodonTagTimeline", "MASTODON_TAG_TIMELINE"), + MASTODON_TREND_TIMELINE("MastodonTrendTimeline", "MASTODON_TREND_TIMELINE"), CALCKEY_RECOMMENDED_TIMELINE("CalckeyRecommendedTimeline", "CALCKEY_RECOMMENDED_TIMELINE"), CLIP_NOTES("ClipNotes", "CLIP_NOTES"), diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt index 347a2feaca..7bc25b10f7 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt @@ -502,6 +502,14 @@ sealed class Pageable : Serializable { } } + object TrendTimeline : Mastodon() { + override fun toParams(): PageParams { + return PageParams( + + ) + } + } + } object CalckeyRecommendedTimeline : Pageable(), UntilPaginate, SincePaginate { From e8e8f49c60fb5de097990bb4e76ccdcd624bcb3a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 14:39:31 +0900 Subject: [PATCH 089/432] =?UTF-8?q?feat:=20mastodon=E3=81=AE=E3=83=88?= =?UTF-8?q?=E3=83=AC=E3=83=B3=E3=83=89=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/api/mastodon/MastodonAPI.kt | 6 ++++++ .../common_android_ui/account/page/PageTypeHelper.kt | 1 + .../notes/MastodonTimelineStorePagingStoreImpl.kt | 8 ++++++++ .../net/pantasystem/milktea/search/SearchTopFragment.kt | 2 +- .../pantasystem/milktea/model/account/page/PageParams.kt | 3 +++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt index 969d0da5e7..c8ac5f68d2 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt @@ -324,4 +324,10 @@ interface MastodonAPI { @Query("since_id") sinceId: String? = null, @Query("min_id") minId: String? = null, ): Response> + + @GET("api/v1/trends/statuses") + suspend fun getTrendStatuses( + @Query("limit") limit: Int? = null, + @Query("offset") offset: Int? = null, + ): Response> } \ No newline at end of file diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt index 0a63d58f9f..46fc2ff48f 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt @@ -44,6 +44,7 @@ object PageTypeHelper{ MASTODON_BOOKMARK_TIMELINE -> context.getString(R.string.bookmark) MASTODON_SEARCH_TIMELINE -> context.getString(R.string.search) MASTODON_TAG_TIMELINE -> context.getString(R.string.tag) + MASTODON_TREND_TIMELINE -> context.getString(R.string.featured) } } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt index eab84f3b93..ea11cf678d 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt @@ -94,6 +94,9 @@ internal class MastodonTimelineStorePagingStoreImpl( is Pageable.Mastodon.SearchTimeline -> { return@runCancellableCatching emptyList() } + is Pageable.Mastodon.TrendTimeline -> { + return@runCancellableCatching emptyList() + } is Pageable.Mastodon.TagTimeline -> { api.getHashtagTimeline( tag = pageableTimeline.tag, @@ -203,6 +206,11 @@ internal class MastodonTimelineStorePagingStoreImpl( updateMaxIdFrom(it) }.body()?.statuses } + is Pageable.Mastodon.TrendTimeline -> { + api.getTrendStatuses( + offset = (getState().content as? StateContent.Exist)?.rawContent?.size ?: 0 + ).getBodyOrFail() + } is Pageable.Mastodon.TagTimeline -> { api.getHashtagTimeline( tag = pageableTimeline.tag, diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt index 02fd2480d6..d5613490b1 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt @@ -115,7 +115,7 @@ class SearchPagerAdapterV2( val item = tabs[position] return when(item.type) { SearchTopTabItem.TabType.MisskeyFeatured -> pageableFragmentFactory.create(Pageable.Featured(null)) - SearchTopTabItem.TabType.MastodonTrends -> pageableFragmentFactory.create(Pageable.Featured(null)) + SearchTopTabItem.TabType.MastodonTrends -> pageableFragmentFactory.create(Pageable.Mastodon.TrendTimeline) SearchTopTabItem.TabType.MisskeyExploreUsers -> ExploreFragment.newInstance(ExploreType.Local) SearchTopTabItem.TabType.MisskeyExploreFediverseUsers -> ExploreFragment.newInstance(ExploreType.Fediverse) } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt index cf1a6a692a..2e00e51d00 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt @@ -202,6 +202,9 @@ data class PageParams( requireNotNull(tag) ) } + MASTODON_TREND_TIMELINE -> { + Pageable.Mastodon.TrendTimeline + } } } catch (e: NullPointerException) { throw IllegalStateException("パラメーターに問題があります: $this") From 1fdf9b9f0b6f1c8249f96079f749c3d5c1186b37 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 14:59:22 +0900 Subject: [PATCH 090/432] =?UTF-8?q?feat:=20=E3=83=88=E3=83=AC=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=81=AE=E3=83=8F=E3=83=83=E3=82=B7=E3=83=A5=E3=82=BF?= =?UTF-8?q?=E3=82=B0=E3=82=92=E5=8F=96=E5=BE=97=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api/mastodon/MastodonAPI.kt | 7 +++++++ .../milktea/api/mastodon/tag/MastodonTagDTO.kt | 17 +++++++++++++++++ .../milktea/api/misskey/MisskeyAPI.kt | 4 ++++ .../milktea/api/misskey/trend/HashtagTrend.kt | 10 ++++++++++ 4 files changed, 38 insertions(+) create mode 100644 modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt create mode 100644 modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt index c8ac5f68d2..51fdff280b 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt @@ -27,6 +27,7 @@ import net.pantasystem.milktea.api.mastodon.search.SearchResponse import net.pantasystem.milktea.api.mastodon.status.CreateStatus import net.pantasystem.milktea.api.mastodon.status.ScheduledStatus import net.pantasystem.milktea.api.mastodon.status.TootStatusDTO +import net.pantasystem.milktea.api.mastodon.tag.MastodonTagDTO import retrofit2.Response import retrofit2.http.* @@ -330,4 +331,10 @@ interface MastodonAPI { @Query("limit") limit: Int? = null, @Query("offset") offset: Int? = null, ): Response> + + @GET("api/v1/trends/tags") + suspend fun getTagTrends( + @Query("limit") limit: Int? = null, + @Query("offset") offset: Int? = null, + ): Response> } \ No newline at end of file diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt new file mode 100644 index 0000000000..7b014c49ff --- /dev/null +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt @@ -0,0 +1,17 @@ +package net.pantasystem.milktea.api.mastodon.tag + +import kotlinx.serialization.SerialName + +@kotlinx.serialization.Serializable +data class MastodonTagDTO( + @SerialName("name") val name: String, + @SerialName("url") val url: String, + @SerialName("history") val history: List, +) { + @kotlinx.serialization.Serializable + data class History( + @SerialName("day") val day: Long, + @SerialName("uses") val uses: Int, + @SerialName("accounts") val accounts: Int, + ) +} \ No newline at end of file diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt index 89b85d1a6d..36ba57c4f6 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt @@ -27,6 +27,7 @@ import net.pantasystem.milktea.api.misskey.register.Subscription import net.pantasystem.milktea.api.misskey.register.UnSubscription import net.pantasystem.milktea.api.misskey.register.WebClientBaseRequest import net.pantasystem.milktea.api.misskey.register.WebClientRegistries +import net.pantasystem.milktea.api.misskey.trend.HashtagTrend import net.pantasystem.milktea.api.misskey.users.* import net.pantasystem.milktea.api.misskey.users.renote.mute.CreateRenoteMuteRequest import net.pantasystem.milktea.api.misskey.users.renote.mute.DeleteRenoteMuteRequest @@ -318,4 +319,7 @@ interface MisskeyAPI { @POST("api/renote-mute/delete") suspend fun deleteRenoteMute(@Body req: DeleteRenoteMuteRequest): Response + + @POST("api/hashtags/trend") + suspend fun getTrendingHashtags(@Body body: EmptyRequest): Response> } \ No newline at end of file diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt new file mode 100644 index 0000000000..adf3e23f43 --- /dev/null +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt @@ -0,0 +1,10 @@ +package net.pantasystem.milktea.api.misskey.trend + +import kotlinx.serialization.SerialName + +@kotlinx.serialization.Serializable +data class HashtagTrend( + @SerialName("tag") val tag: String, + @SerialName("chart") val chart: List, + @SerialName("usesCount") val usesCount: Int, +) \ No newline at end of file From 257b5e97038e4b7c894935f76c8e11329ee8f48c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 7 May 2023 15:14:15 +0900 Subject: [PATCH 091/432] =?UTF-8?q?feat:=20mastodon=E3=81=A7=E3=82=82?= =?UTF-8?q?=E3=82=BF=E3=82=B0=E6=A4=9C=E7=B4=A2=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/mastodon/search/SearchResponse.kt | 4 ++ .../hashtag/HashtagRepositoryImpl.kt | 43 +++++++++++++++---- .../milktea/search/SearchViewModel.kt | 2 +- .../model/hashtag/HashtagRepository.kt | 2 +- 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/search/SearchResponse.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/search/SearchResponse.kt index 2cccd0b307..7c3367c676 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/search/SearchResponse.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/search/SearchResponse.kt @@ -3,6 +3,7 @@ package net.pantasystem.milktea.api.mastodon.search import kotlinx.serialization.SerialName import net.pantasystem.milktea.api.mastodon.accounts.MastodonAccountDTO import net.pantasystem.milktea.api.mastodon.status.TootStatusDTO +import net.pantasystem.milktea.api.mastodon.tag.MastodonTagDTO @kotlinx.serialization.Serializable data class SearchResponse( @@ -11,4 +12,7 @@ data class SearchResponse( @SerialName("statuses") val statuses: List, + + @SerialName("hashtags") + val hashtags: List, ) \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt index 211634c086..f49fad6f30 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt @@ -5,21 +5,48 @@ import kotlinx.coroutines.withContext import net.pantasystem.milktea.api.misskey.hashtag.SearchHashtagRequest import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common.throwIfHasError +import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.hashtag.HashtagRepository import javax.inject.Inject class HashtagRepositoryImpl @Inject constructor( - val misskeyAPIProvider: MisskeyAPIProvider, -): HashtagRepository { - override suspend fun search(baseUrl: String, query: String, limit: Int, offset: Int): Result> = runCancellableCatching{ + private val accountRepository: AccountRepository, + private val misskeyAPIProvider: MisskeyAPIProvider, + private val mastodonAPIProvider: MastodonAPIProvider, +) : HashtagRepository { + override suspend fun search( + accountId: Long, + query: String, + limit: Int, + offset: Int, + ): Result> = runCancellableCatching { withContext(Dispatchers.IO) { + val account = accountRepository.get(accountId).getOrThrow() + when (account.instanceType) { + Account.InstanceType.MISSKEY -> { + misskeyAPIProvider.get(account).searchHashtag( + SearchHashtagRequest( + query = query, + limit = limit, + offset = offset + ) + ).throwIfHasError().body() ?: emptyList() + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + mastodonAPIProvider.get(account).search( + q = query, + limit = limit, + offset = offset, + type = "hashtags" + ).throwIfHasError().body()?.hashtags?.map { + it.name + } ?: emptyList() + } + } - misskeyAPIProvider.get(baseUrl).searchHashtag(SearchHashtagRequest( - query = query, - limit = limit, - offset = offset - )).throwIfHasError().body() ?: emptyList() } } } \ No newline at end of file diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt index 8197e788ba..7093c101a2 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt @@ -46,7 +46,7 @@ class SearchViewModel @Inject constructor( }.flatMapLatest { suspend { hashtagRepository.search( - currentAccountWatcher.getAccount().normalizedInstanceUri, + currentAccountWatcher.getAccount().accountId, it ).getOrThrow() }.asLoadingStateFlow() diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt index 702fd74357..a38a04f739 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt @@ -1,5 +1,5 @@ package net.pantasystem.milktea.model.hashtag interface HashtagRepository { - suspend fun search(baseUrl: String, query: String, limit: Int = 10, offset: Int = 0): Result> + suspend fun search(accountId: Long, query: String, limit: Int = 10, offset: Int = 0): Result> } \ No newline at end of file From 6d5a6f4644ef9fc2f8df8f8b8477aa15e344d73e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:27:48 +0900 Subject: [PATCH 092/432] =?UTF-8?q?refactor:=20savedStateHandle=E7=B5=8C?= =?UTF-8?q?=E7=94=B1=E3=81=A7=E5=80=A4=E3=82=92=E5=8F=96=E5=BE=97=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/explore/ExploreFragment.kt | 54 +++-------------- .../search/explore/ExploreViewModel.kt | 59 +++++++++++++++---- 2 files changed, 58 insertions(+), 55 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt index a5a83836c4..b6fbcfc7ab 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt @@ -10,6 +10,7 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Surface import androidx.compose.material.Text +import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -17,6 +18,7 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -26,8 +28,7 @@ import com.google.android.material.composethemeadapter.MdcTheme import dagger.hilt.android.AndroidEntryPoint import net.pantasystem.milktea.common.ResultState import net.pantasystem.milktea.common.StateContent -import net.pantasystem.milktea.model.user.query.* -import net.pantasystem.milktea.search.R +import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.user.UserCardActionHandler import net.pantasystem.milktea.user.compose.UserDetailCard import net.pantasystem.milktea.user.compose.UserDetailCardAction @@ -75,7 +76,7 @@ class ExploreFragment : Fragment() { .fillMaxWidth() ) { Text( - item.title, + getString(item.title), fontSize = 16.sp, modifier = Modifier.padding(4.dp) ) @@ -120,48 +121,6 @@ class ExploreFragment : Fragment() { }.rootView } - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - - super.onViewCreated(view, savedInstanceState) - - val queries = when (ExploreType.values()[requireArguments().getInt("type")]) { - ExploreType.Local -> { - listOf( - ExploreItem( - getString(R.string.trending_users), - FindUsersQuery.trendingUser(), - ), - ExploreItem( - getString(R.string.users_with_recent_activity), - FindUsersQuery.usersWithRecentActivity(), - ), - ExploreItem( - getString(R.string.newly_joined_users), - FindUsersQuery.newlyJoinedUsers() - ) - - ) - } - ExploreType.Fediverse -> { - listOf( - ExploreItem( - getString(R.string.trending_users), - FindUsersQuery.remoteTrendingUser() - ), - ExploreItem( - getString(R.string.users_with_recent_activity), - FindUsersQuery.remoteUsersWithRecentActivity(), - ), - ExploreItem( - getString(R.string.newly_discovered_users), - FindUsersQuery.newlyDiscoveredUsers() - ), - ) - } - } - exploreViewModel.setExplores(queries) - - } fun onAction(event: UserDetailCardAction) { UserCardActionHandler(requireActivity(), toggleFollowViewModel) @@ -171,4 +130,9 @@ class ExploreFragment : Fragment() { enum class ExploreType { Local, Fediverse, +} + +@Composable +fun getString(src: StringSource): String { + return src.getString(LocalContext.current) } \ No newline at end of file diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt index 26be9951e8..63df039568 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.search.explore +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -10,10 +11,12 @@ import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.ResultState import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common.asLoadingStateFlow +import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserDataSource import net.pantasystem.milktea.model.user.UserRepository -import net.pantasystem.milktea.model.user.query.FindUsersQuery +import net.pantasystem.milktea.model.user.query.* +import net.pantasystem.milktea.search.R import javax.inject.Inject @@ -24,10 +27,51 @@ class ExploreViewModel @Inject constructor( val userDataSource: UserDataSource, val userRepository: UserRepository, loggerFactory: Logger.Factory, + savedStateHandle: SavedStateHandle, ) : ViewModel() { val logger = loggerFactory.create("ExploreViewModel") - private val findUsers = MutableStateFlow>(emptyList()) + private val type = savedStateHandle.getStateFlow("type", ExploreType.Local.ordinal).map { + ExploreType.values()[it] + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), ExploreType.Local) + + private val findUsers = type.map { + when(it) { + ExploreType.Local -> { + listOf( + ExploreItem( + StringSource(R.string.trending_users), + FindUsersQuery.trendingUser(), + ), + ExploreItem( + StringSource(R.string.users_with_recent_activity), + FindUsersQuery.usersWithRecentActivity(), + ), + ExploreItem( + StringSource(R.string.newly_joined_users), + FindUsersQuery.newlyJoinedUsers() + ) + + ) + } + ExploreType.Fediverse -> { + listOf( + ExploreItem( + StringSource(R.string.trending_users), + FindUsersQuery.remoteTrendingUser() + ), + ExploreItem( + StringSource(R.string.users_with_recent_activity), + FindUsersQuery.remoteUsersWithRecentActivity(), + ), + ExploreItem( + StringSource(R.string.newly_discovered_users), + FindUsersQuery.newlyDiscoveredUsers() + ), + ) + } + } + } private val rawLoadingStates = accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { ac -> @@ -97,26 +141,21 @@ class ExploreViewModel @Inject constructor( val account = accountStore.observeCurrentAccount.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) - fun setExplores(list: List) { - findUsers.update { - list - } - } } data class ExploreItem( - val title: String, + val title: StringSource, val findUsersQuery: FindUsersQuery, ) data class ExploreItemState( - val title: String, + val title: StringSource, val findUsersQuery: FindUsersQuery, val loadingState: ResultState> ) data class ExploreResultState( - val title: String, + val title: StringSource, val findUsersQuery: FindUsersQuery, val loadingState: ResultState> ) From d570ec6608b8c6d2bcb94fc3fa0404cde68fb907 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:30:18 +0900 Subject: [PATCH 093/432] =?UTF-8?q?refactor:=20=E5=85=B1=E9=80=9A=E3=83=A2?= =?UTF-8?q?=E3=82=B8=E3=83=A5=E3=83=BC=E3=83=AB=E3=81=AB=E7=A7=BB=E5=8B=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/StringSourceHelper.kt | 8 ++++++++ .../milktea/search/explore/ExploreFragment.kt | 10 ++-------- 2 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 modules/common_android_ui/src/main/java/StringSourceHelper.kt diff --git a/modules/common_android_ui/src/main/java/StringSourceHelper.kt b/modules/common_android_ui/src/main/java/StringSourceHelper.kt new file mode 100644 index 0000000000..c8707804c9 --- /dev/null +++ b/modules/common_android_ui/src/main/java/StringSourceHelper.kt @@ -0,0 +1,8 @@ +import androidx.compose.runtime.Composable +import androidx.compose.ui.platform.LocalContext +import net.pantasystem.milktea.common_android.resource.StringSource + +@Composable +fun getStringFromStringSource(src: StringSource): String { + return src.getString(LocalContext.current) +} \ No newline at end of file diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt index b6fbcfc7ab..f7bb560049 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt @@ -10,7 +10,6 @@ import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.CircularProgressIndicator import androidx.compose.material.Surface import androidx.compose.material.Text -import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment @@ -18,7 +17,6 @@ import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.ComposeView -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp @@ -26,9 +24,9 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import com.google.android.material.composethemeadapter.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import getStringFromStringSource import net.pantasystem.milktea.common.ResultState import net.pantasystem.milktea.common.StateContent -import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.user.UserCardActionHandler import net.pantasystem.milktea.user.compose.UserDetailCard import net.pantasystem.milktea.user.compose.UserDetailCardAction @@ -76,7 +74,7 @@ class ExploreFragment : Fragment() { .fillMaxWidth() ) { Text( - getString(item.title), + getStringFromStringSource(item.title), fontSize = 16.sp, modifier = Modifier.padding(4.dp) ) @@ -132,7 +130,3 @@ enum class ExploreType { Local, Fediverse, } -@Composable -fun getString(src: StringSource): String { - return src.getString(LocalContext.current) -} \ No newline at end of file From be2f3467b785577a1e9ecb38ad08fdc20fcacf4a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:48:38 +0900 Subject: [PATCH 094/432] =?UTF-8?q?feat:=20suggestion=E3=81=AE=E7=B5=90?= =?UTF-8?q?=E6=9E=9C=E3=82=92=E5=8F=97=E3=81=91=E3=82=8B=E3=81=9F=E3=82=81?= =?UTF-8?q?=E3=81=AE=E6=A7=8B=E9=80=A0=E4=BD=93=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/mastodon/suggestion/SuggestionDTO.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/suggestion/SuggestionDTO.kt diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/suggestion/SuggestionDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/suggestion/SuggestionDTO.kt new file mode 100644 index 0000000000..1677c4a5cd --- /dev/null +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/suggestion/SuggestionDTO.kt @@ -0,0 +1,13 @@ +package net.pantasystem.milktea.api.mastodon.suggestion + +import kotlinx.serialization.SerialName +import net.pantasystem.milktea.api.mastodon.accounts.MastodonAccountDTO + +@kotlinx.serialization.Serializable +data class SuggestionDTO( + @SerialName("source") + val source: String, + + @SerialName("account") + val account: MastodonAccountDTO +) \ No newline at end of file From d8496733d7b2da87fac2510a6e26032d0c281db6 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:48:59 +0900 Subject: [PATCH 095/432] =?UTF-8?q?feat:=20Misskey=E3=81=A8Mastodon?= =?UTF-8?q?=E3=81=A7=E5=88=86=E3=81=91=E3=82=89=E3=82=8C=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/user/query/FindUsersQuery.kt | 148 ++++++++++-------- 1 file changed, 84 insertions(+), 64 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/user/query/FindUsersQuery.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/user/query/FindUsersQuery.kt index 24ba1d9e34..fad5ccc0ce 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/user/query/FindUsersQuery.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/user/query/FindUsersQuery.kt @@ -1,10 +1,16 @@ package net.pantasystem.milktea.model.user.query -data class FindUsersQuery( +sealed interface FindUsersQuery + +sealed interface FindUsersQuery4Mastodon : FindUsersQuery { + data class SuggestUsers(val limit: Int? = null) : FindUsersQuery4Mastodon +} + +data class FindUsersQuery4Misskey( val origin: Origin?, val sort: OrderBy?, - val state: State? -) { + val state: State?, +) : FindUsersQuery { companion object; sealed interface Order { @@ -64,110 +70,124 @@ data class FindUsersQuery( } -fun FindUsersQuery.OrderBy.Companion.from(str: String): FindUsersQuery.OrderBy? { - return when(str) { - FindUsersQuery.Order.Follower.asc.str() -> FindUsersQuery.Order.Follower.asc - FindUsersQuery.Order.Follower.desc.str() -> FindUsersQuery.Order.Follower.desc - FindUsersQuery.Order.CreatedAt.asc.str() -> FindUsersQuery.Order.CreatedAt.asc - FindUsersQuery.Order.CreatedAt.desc.str() -> FindUsersQuery.Order.CreatedAt.desc - FindUsersQuery.Order.UpdatedAt.asc.str() -> FindUsersQuery.Order.UpdatedAt.asc - FindUsersQuery.Order.UpdatedAt.desc.str() -> FindUsersQuery.Order.UpdatedAt.desc +fun FindUsersQuery4Misskey.OrderBy.Companion.from(str: String): FindUsersQuery4Misskey.OrderBy? { + return when (str) { + FindUsersQuery4Misskey.Order.Follower.asc.str() -> FindUsersQuery4Misskey.Order.Follower.asc + FindUsersQuery4Misskey.Order.Follower.desc.str() -> FindUsersQuery4Misskey.Order.Follower.desc + FindUsersQuery4Misskey.Order.CreatedAt.asc.str() -> FindUsersQuery4Misskey.Order.CreatedAt.asc + FindUsersQuery4Misskey.Order.CreatedAt.desc.str() -> FindUsersQuery4Misskey.Order.CreatedAt.desc + FindUsersQuery4Misskey.Order.UpdatedAt.asc.str() -> FindUsersQuery4Misskey.Order.UpdatedAt.asc + FindUsersQuery4Misskey.Order.UpdatedAt.desc.str() -> FindUsersQuery4Misskey.Order.UpdatedAt.desc else -> null } } -fun FindUsersQuery.State.Companion.from(str: String): FindUsersQuery.State? { - - return when(str) { - FindUsersQuery.State.All.state -> FindUsersQuery.State.All - FindUsersQuery.State.Admin.state -> FindUsersQuery.State.Admin - FindUsersQuery.State.Moderator.state -> FindUsersQuery.State.Moderator - FindUsersQuery.State.AdminOrModerator.state -> FindUsersQuery.State.AdminOrModerator - FindUsersQuery.State.Alive.state -> FindUsersQuery.State.Alive + +fun FindUsersQuery4Misskey.State.Companion.from(str: String): FindUsersQuery4Misskey.State? { + + return when (str) { + FindUsersQuery4Misskey.State.All.state -> FindUsersQuery4Misskey.State.All + FindUsersQuery4Misskey.State.Admin.state -> FindUsersQuery4Misskey.State.Admin + FindUsersQuery4Misskey.State.Moderator.state -> FindUsersQuery4Misskey.State.Moderator + FindUsersQuery4Misskey.State.AdminOrModerator.state -> FindUsersQuery4Misskey.State.AdminOrModerator + FindUsersQuery4Misskey.State.Alive.state -> FindUsersQuery4Misskey.State.Alive else -> null } } -fun FindUsersQuery.Origin.Companion.from(str: String): FindUsersQuery.Origin? { - return when(str) { - FindUsersQuery.Origin.Local.origin -> FindUsersQuery.Origin.Local - FindUsersQuery.Origin.Combined.origin -> FindUsersQuery.Origin.Combined - FindUsersQuery.Origin.Remote.origin -> FindUsersQuery.Origin.Remote +fun FindUsersQuery4Misskey.Origin.Companion.from(str: String): FindUsersQuery4Misskey.Origin? { + return when (str) { + FindUsersQuery4Misskey.Origin.Local.origin -> FindUsersQuery4Misskey.Origin.Local + FindUsersQuery4Misskey.Origin.Combined.origin -> FindUsersQuery4Misskey.Origin.Combined + FindUsersQuery4Misskey.Origin.Remote.origin -> FindUsersQuery4Misskey.Origin.Remote else -> null } } -val FindUsersQuery.Order.desc: FindUsersQuery.OrderBy + +val FindUsersQuery4Misskey.Order.desc: FindUsersQuery4Misskey.OrderBy get() { return when (this) { - FindUsersQuery.Order.CreatedAt -> FindUsersQuery.OrderBy.CreatedAt(FindUsersQuery.OrderBy.By.Desc) - FindUsersQuery.Order.Follower -> FindUsersQuery.OrderBy.Follower(FindUsersQuery.OrderBy.By.Desc) - FindUsersQuery.Order.UpdatedAt -> FindUsersQuery.OrderBy.UpdatedAt(FindUsersQuery.OrderBy.By.Desc) + FindUsersQuery4Misskey.Order.CreatedAt -> FindUsersQuery4Misskey.OrderBy.CreatedAt( + FindUsersQuery4Misskey.OrderBy.By.Desc + ) + FindUsersQuery4Misskey.Order.Follower -> FindUsersQuery4Misskey.OrderBy.Follower( + FindUsersQuery4Misskey.OrderBy.By.Desc + ) + FindUsersQuery4Misskey.Order.UpdatedAt -> FindUsersQuery4Misskey.OrderBy.UpdatedAt( + FindUsersQuery4Misskey.OrderBy.By.Desc + ) } } -val FindUsersQuery.Order.asc: FindUsersQuery.OrderBy +val FindUsersQuery4Misskey.Order.asc: FindUsersQuery4Misskey.OrderBy get() { return when (this) { - FindUsersQuery.Order.CreatedAt -> FindUsersQuery.OrderBy.CreatedAt(FindUsersQuery.OrderBy.By.Asc) - FindUsersQuery.Order.Follower -> FindUsersQuery.OrderBy.Follower(FindUsersQuery.OrderBy.By.Asc) - FindUsersQuery.Order.UpdatedAt -> FindUsersQuery.OrderBy.UpdatedAt(FindUsersQuery.OrderBy.By.Asc) + FindUsersQuery4Misskey.Order.CreatedAt -> FindUsersQuery4Misskey.OrderBy.CreatedAt( + FindUsersQuery4Misskey.OrderBy.By.Asc + ) + FindUsersQuery4Misskey.Order.Follower -> FindUsersQuery4Misskey.OrderBy.Follower( + FindUsersQuery4Misskey.OrderBy.By.Asc + ) + FindUsersQuery4Misskey.Order.UpdatedAt -> FindUsersQuery4Misskey.OrderBy.UpdatedAt( + FindUsersQuery4Misskey.OrderBy.By.Asc + ) } } -fun FindUsersQuery.Companion.trendingUser(): FindUsersQuery { - return FindUsersQuery( - origin = FindUsersQuery.Origin.Local, - sort = FindUsersQuery.Order.Follower.asc, - state = FindUsersQuery.State.Alive +fun FindUsersQuery4Misskey.Companion.trendingUser(): FindUsersQuery4Misskey { + return FindUsersQuery4Misskey( + origin = FindUsersQuery4Misskey.Origin.Local, + sort = FindUsersQuery4Misskey.Order.Follower.asc, + state = FindUsersQuery4Misskey.State.Alive ) } -fun FindUsersQuery.Companion.usersWithRecentActivity(): FindUsersQuery { - return FindUsersQuery( - origin = FindUsersQuery.Origin.Local, - sort = FindUsersQuery.Order.UpdatedAt.asc, +fun FindUsersQuery4Misskey.Companion.usersWithRecentActivity(): FindUsersQuery4Misskey { + return FindUsersQuery4Misskey( + origin = FindUsersQuery4Misskey.Origin.Local, + sort = FindUsersQuery4Misskey.Order.UpdatedAt.asc, state = null, ) } -fun FindUsersQuery.Companion.newlyJoinedUsers(): FindUsersQuery { - return FindUsersQuery( - origin = FindUsersQuery.Origin.Local, - sort = FindUsersQuery.Order.CreatedAt.asc, - state = FindUsersQuery.State.Alive +fun FindUsersQuery4Misskey.Companion.newlyJoinedUsers(): FindUsersQuery4Misskey { + return FindUsersQuery4Misskey( + origin = FindUsersQuery4Misskey.Origin.Local, + sort = FindUsersQuery4Misskey.Order.CreatedAt.asc, + state = FindUsersQuery4Misskey.State.Alive ) } -fun FindUsersQuery.Companion.remoteTrendingUser(): FindUsersQuery { - return FindUsersQuery( - origin = FindUsersQuery.Origin.Remote, - sort = FindUsersQuery.Order.Follower.asc, - state = FindUsersQuery.State.Alive +fun FindUsersQuery4Misskey.Companion.remoteTrendingUser(): FindUsersQuery4Misskey { + return FindUsersQuery4Misskey( + origin = FindUsersQuery4Misskey.Origin.Remote, + sort = FindUsersQuery4Misskey.Order.Follower.asc, + state = FindUsersQuery4Misskey.State.Alive ) } -fun FindUsersQuery.Companion.remoteUsersWithRecentActivity(): FindUsersQuery { - return FindUsersQuery( - origin = FindUsersQuery.Origin.Combined, - sort = FindUsersQuery.Order.UpdatedAt.asc, - state = FindUsersQuery.State.Alive +fun FindUsersQuery4Misskey.Companion.remoteUsersWithRecentActivity(): FindUsersQuery4Misskey { + return FindUsersQuery4Misskey( + origin = FindUsersQuery4Misskey.Origin.Combined, + sort = FindUsersQuery4Misskey.Order.UpdatedAt.asc, + state = FindUsersQuery4Misskey.State.Alive ) } -fun FindUsersQuery.Companion.newlyDiscoveredUsers(): FindUsersQuery { - return FindUsersQuery.from(FindUsersQuery.Origin.Combined) - .whereState(FindUsersQuery.State.All) - .orderBy(FindUsersQuery.Order.CreatedAt.asc) +fun FindUsersQuery4Misskey.Companion.newlyDiscoveredUsers(): FindUsersQuery4Misskey { + return FindUsersQuery4Misskey.from(FindUsersQuery4Misskey.Origin.Combined) + .whereState(FindUsersQuery4Misskey.State.All) + .orderBy(FindUsersQuery4Misskey.Order.CreatedAt.asc) } -infix fun FindUsersQuery.orderBy(order: FindUsersQuery.OrderBy): FindUsersQuery { +infix fun FindUsersQuery4Misskey.orderBy(order: FindUsersQuery4Misskey.OrderBy): FindUsersQuery4Misskey { return this.copy(sort = order) } -fun FindUsersQuery.Companion.from(origin: FindUsersQuery.Origin): FindUsersQuery { - return FindUsersQuery(origin = origin, null, null) +fun FindUsersQuery4Misskey.Companion.from(origin: FindUsersQuery4Misskey.Origin): FindUsersQuery4Misskey { + return FindUsersQuery4Misskey(origin = origin, null, null) } -infix fun FindUsersQuery.whereState(state: FindUsersQuery.State): FindUsersQuery { +infix fun FindUsersQuery4Misskey.whereState(state: FindUsersQuery4Misskey.State): FindUsersQuery4Misskey { return this.copy(state = state) } From d6b32219c36540ecd70e0c9eab297b932336dbef Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:50:31 +0900 Subject: [PATCH 096/432] =?UTF-8?q?feat:=20Mastodon=E3=81=AE=E6=99=82?= =?UTF-8?q?=E3=81=AFSuggestion=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/search/SearchTopFragment.kt | 1 + .../net/pantasystem/milktea/search/SearchTopViewModel.kt | 7 ++++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt index d5613490b1..728ebcc117 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt @@ -118,6 +118,7 @@ class SearchPagerAdapterV2( SearchTopTabItem.TabType.MastodonTrends -> pageableFragmentFactory.create(Pageable.Mastodon.TrendTimeline) SearchTopTabItem.TabType.MisskeyExploreUsers -> ExploreFragment.newInstance(ExploreType.Local) SearchTopTabItem.TabType.MisskeyExploreFediverseUsers -> ExploreFragment.newInstance(ExploreType.Fediverse) + SearchTopTabItem.TabType.MastodonUserSuggestions -> ExploreFragment.newInstance(ExploreType.MastodonUserSuggestions) } } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt index 88b2fc270f..67a4fb1714 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt @@ -47,6 +47,10 @@ class SearchTopViewModel @Inject constructor( StringSource(R.string.title_featured), SearchTopTabItem.TabType.MastodonTrends, ), + SearchTopTabItem( + StringSource("Suggestions"), + SearchTopTabItem.TabType.MastodonUserSuggestions, + ) ) } null -> emptyList() @@ -68,7 +72,8 @@ data class SearchTopTabItem( enum class TabType { MisskeyFeatured, MastodonTrends, + MastodonUserSuggestions, MisskeyExploreUsers, - MisskeyExploreFediverseUsers + MisskeyExploreFediverseUsers, } } \ No newline at end of file From 4aecbb17df244bf4bf965d691ff99f586c7ba99e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:51:30 +0900 Subject: [PATCH 097/432] refactor --- .../net/pantasystem/milktea/api/misskey/users/RequestUser.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/users/RequestUser.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/users/RequestUser.kt index de14b78a15..10da1540d6 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/users/RequestUser.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/users/RequestUser.kt @@ -3,7 +3,7 @@ package net.pantasystem.milktea.api.misskey.users import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import net.pantasystem.milktea.model.user.query.FindUsersQuery +import net.pantasystem.milktea.model.user.query.FindUsersQuery4Misskey @Serializable data class RequestUser( @@ -52,7 +52,7 @@ data class RequestUser( } -fun RequestUser.Companion.from(query: FindUsersQuery, i: String): RequestUser { +fun RequestUser.Companion.from(query: FindUsersQuery4Misskey, i: String): RequestUser { return RequestUser( i = i, origin = query.origin?.origin, From 6c94fe7959e74f914520b6764caa3e1de846415a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:51:46 +0900 Subject: [PATCH 098/432] =?UTF-8?q?feat:=20Suggestion=E3=82=92=E5=8F=96?= =?UTF-8?q?=E5=BE=97=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/api/mastodon/MastodonAPI.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt index 51fdff280b..aacb02aef8 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/MastodonAPI.kt @@ -27,6 +27,7 @@ import net.pantasystem.milktea.api.mastodon.search.SearchResponse import net.pantasystem.milktea.api.mastodon.status.CreateStatus import net.pantasystem.milktea.api.mastodon.status.ScheduledStatus import net.pantasystem.milktea.api.mastodon.status.TootStatusDTO +import net.pantasystem.milktea.api.mastodon.suggestion.SuggestionDTO import net.pantasystem.milktea.api.mastodon.tag.MastodonTagDTO import retrofit2.Response import retrofit2.http.* @@ -337,4 +338,9 @@ interface MastodonAPI { @Query("limit") limit: Int? = null, @Query("offset") offset: Int? = null, ): Response> + + @GET("api/v2/suggestions") + suspend fun getSuggestionUsers( + @Query("limit") limit: Int? = null + ): Response> } \ No newline at end of file From 90c07a079630440cf15735ea82cdfa661add7a70 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:52:11 +0900 Subject: [PATCH 099/432] =?UTF-8?q?feat:=20suggestion=E3=82=92=E5=8F=96?= =?UTF-8?q?=E5=BE=97=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AE=E3=83=AD?= =?UTF-8?q?=E3=82=B8=E3=83=83=E3=82=AF=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search/explore/ExploreViewModel.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt index 63df039568..7333b9a52d 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt @@ -41,15 +41,15 @@ class ExploreViewModel @Inject constructor( listOf( ExploreItem( StringSource(R.string.trending_users), - FindUsersQuery.trendingUser(), + FindUsersQuery4Misskey.trendingUser(), ), ExploreItem( StringSource(R.string.users_with_recent_activity), - FindUsersQuery.usersWithRecentActivity(), + FindUsersQuery4Misskey.usersWithRecentActivity(), ), ExploreItem( StringSource(R.string.newly_joined_users), - FindUsersQuery.newlyJoinedUsers() + FindUsersQuery4Misskey.newlyJoinedUsers() ) ) @@ -58,18 +58,26 @@ class ExploreViewModel @Inject constructor( listOf( ExploreItem( StringSource(R.string.trending_users), - FindUsersQuery.remoteTrendingUser() + FindUsersQuery4Misskey.remoteTrendingUser() ), ExploreItem( StringSource(R.string.users_with_recent_activity), - FindUsersQuery.remoteUsersWithRecentActivity(), + FindUsersQuery4Misskey.remoteUsersWithRecentActivity(), ), ExploreItem( StringSource(R.string.newly_discovered_users), - FindUsersQuery.newlyDiscoveredUsers() + FindUsersQuery4Misskey.newlyDiscoveredUsers() ), ) } + ExploreType.MastodonUserSuggestions -> { + listOf( + ExploreItem( + StringSource("Suggestions"), + FindUsersQuery4Mastodon.SuggestUsers() + ) + ) + } } } From 1eddfe29ccc7570c39161f2af9deae726fd8ae00 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 12:52:17 +0900 Subject: [PATCH 100/432] =?UTF-8?q?feat:=20suggestion=E3=82=92=E5=8F=96?= =?UTF-8?q?=E5=BE=97=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AE=E3=83=AD?= =?UTF-8?q?=E3=82=B8=E3=83=83=E3=82=AF=E3=82=92=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/user/UserRepositoryImpl.kt | 46 +++++++++++++++---- .../milktea/search/explore/ExploreFragment.kt | 2 +- 2 files changed, 39 insertions(+), 9 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt index 56a086d9a0..4218c69da2 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt @@ -10,6 +10,7 @@ import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.converters.UserDTOEntityConverter +import net.pantasystem.milktea.data.infrastructure.toUserRelated import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.drive.FilePropertyDataSource @@ -18,6 +19,8 @@ import net.pantasystem.milktea.model.user.UserDataSource import net.pantasystem.milktea.model.user.UserNotFoundException import net.pantasystem.milktea.model.user.UserRepository import net.pantasystem.milktea.model.user.query.FindUsersQuery +import net.pantasystem.milktea.model.user.query.FindUsersQuery4Mastodon +import net.pantasystem.milktea.model.user.query.FindUsersQuery4Misskey import javax.inject.Inject @Suppress("BlockingMethodInNonBlockingContext") @@ -149,14 +152,41 @@ internal class UserRepositoryImpl @Inject constructor( override suspend fun findUsers(accountId: Long, query: FindUsersQuery): List { return withContext(ioDispatcher) { val account = accountRepository.get(accountId).getOrThrow() - val request = RequestUser.from(query, account.token) - val res = misskeyAPIProvider.get(account).getUsers(request) - .throwIfHasError() - res.body()?.map { - userDTOEntityConverter.convert(account, it, true) - }?.onEach { - userDataSource.add(it) - } ?: emptyList() + when(query) { + is FindUsersQuery4Mastodon.SuggestUsers -> { + val api = mastodonAPIProvider.get(account) + val body = requireNotNull(api.getSuggestionUsers( + limit = query.limit + ).throwIfHasError().body()) + val accounts = body.map { + it.account + } + val relationships = requireNotNull( + api.getAccountRelationships(ids = accounts.map { it.id }) + .throwIfHasError() + .body() + ).let { list -> + list.associateBy { + it.id + } + } + val models = accounts.map { + it.toModel(account, relationships[it.id]?.toUserRelated()) + } + userDataSource.addAll(models).getOrThrow() + models + } + is FindUsersQuery4Misskey -> { + val request = RequestUser.from(query, account.token) + val res = misskeyAPIProvider.get(account).getUsers(request) + .throwIfHasError() + res.body()?.map { + userDTOEntityConverter.convert(account, it, true) + }?.onEach { + userDataSource.add(it) + } ?: emptyList() + } + } } } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt index f7bb560049..952dc1acdf 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt @@ -127,6 +127,6 @@ class ExploreFragment : Fragment() { } enum class ExploreType { - Local, Fediverse, + Local, Fediverse, MastodonUserSuggestions, } From 7d04af9194d6926fabf17589b2414dca42fb49dd Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 13:19:54 +0900 Subject: [PATCH 101/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index dde3f200da..66206aeae9 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -592,6 +592,7 @@ フォローされています ノートコンテンツ文字サイズ(%fsp) ノートヘッダー文字サイズ(%fsp) + おすすめユーザ diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 0a112fec12..a0f85fdcd7 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -586,6 +586,7 @@ 正在关注你 Note content font size(%fsp) Note header font size(%fsp) + Suggestions \ No newline at end of file diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index bf6698eee6..67d576df19 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -588,6 +588,7 @@ Indefinite perio View remotely Follows you + Suggestions From 57194f529ad78f734cce76ccc7472734475417ac Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 13:20:29 +0900 Subject: [PATCH 102/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/search/SearchTopViewModel.kt | 2 +- .../net/pantasystem/milktea/search/explore/ExploreViewModel.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt index 67a4fb1714..4100b1edd7 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt @@ -48,7 +48,7 @@ class SearchTopViewModel @Inject constructor( SearchTopTabItem.TabType.MastodonTrends, ), SearchTopTabItem( - StringSource("Suggestions"), + StringSource(R.string.suggestion_users), SearchTopTabItem.TabType.MastodonUserSuggestions, ) ) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt index 7333b9a52d..fc58760513 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt @@ -73,7 +73,7 @@ class ExploreViewModel @Inject constructor( ExploreType.MastodonUserSuggestions -> { listOf( ExploreItem( - StringSource("Suggestions"), + StringSource(R.string.suggestion_users), FindUsersQuery4Mastodon.SuggestUsers() ) ) From 5ecce9722eba2b09c2e397f4573e5873ffebd45c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 13:41:16 +0900 Subject: [PATCH 103/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=97=E3=81=9F=E3=82=A2=E3=82=AB?= =?UTF-8?q?=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AEId=E3=81=A8=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=97=E3=81=9F?= =?UTF-8?q?=E5=AF=BE=E8=B1=A1=E3=81=AE=E6=8A=95=E7=A8=BF=E3=81=AEId?= =?UTF-8?q?=E3=81=A8=E3=81=9D=E3=81=AE=E6=8A=95=E7=A8=BF=E3=81=AE=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=81=AEId=E3=82=92=E5=B1=A5=E6=AD=B4?= =?UTF-8?q?=E3=81=AB=E4=BF=9D=E6=8C=81=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/reaction/ToggleReactionUseCase.kt | 7 +- .../notes/reaction/history/ReactionHistory.kt | 3 + .../reaction/ToggleReactionUseCaseTest.kt | 81 ++++++++++++++++--- 3 files changed, 76 insertions(+), 15 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt index 8e87fdfdcd..bf2180b038 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt @@ -72,8 +72,11 @@ class ToggleReactionUseCase @Inject constructor( if (reactionRepository.create(CreateReaction(noteId, sendReaction)).getOrThrow()) { reactionHistoryRepository.create( ReactionHistory( - sendReaction, - account.normalizedInstanceUri + reaction = sendReaction, + instanceDomain = account.normalizedInstanceUri, + accountId = account.accountId, + targetPostId = noteId.noteId, + targetUserId = note.userId.id, ) ) } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/history/ReactionHistory.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/history/ReactionHistory.kt index 003a6d1a24..0be8d805f2 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/history/ReactionHistory.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/history/ReactionHistory.kt @@ -3,5 +3,8 @@ package net.pantasystem.milktea.model.notes.reaction.history data class ReactionHistory( val reaction: String, val instanceDomain: String, + val accountId: Long?, + val targetUserId: String?, + val targetPostId: String?, var id: Long? = null ) \ No newline at end of file diff --git a/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt b/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt index d6c7b1043f..60831a551b 100644 --- a/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt +++ b/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt @@ -190,13 +190,19 @@ class ToggleReactionUseCaseTest { } verifyBlocking(reactionHistoryDao) { - create(ReactionHistory(":wakaranai:", "https://misskey.io")) + create( + ReactionHistory( + reaction = ":wakaranai:", + instanceDomain = "https://misskey.io", + accountId = account.accountId, + targetPostId = targetNote.id.noteId, + targetUserId = targetNote.userId.id, + ) + ) } } - - @Test fun giveCustomEmojiReaction() { val targetNote = generateEmptyNote().copy( @@ -260,7 +266,7 @@ class ToggleReactionUseCaseTest { instanceInfoService = mock() { onBlocking { find(any()) - } doReturn Result.success(InstanceInfoType.Misskey(meta)) + } doReturn Result.success(InstanceInfoType.Misskey(meta)) } ) @@ -272,7 +278,15 @@ class ToggleReactionUseCaseTest { } verifyBlocking(reactionHistoryDao) { - create(ReactionHistory(":kawaii:", "https://misskey.io")) + create( + ReactionHistory( + reaction = ":kawaii:", + instanceDomain = "https://misskey.io", + targetUserId = targetNote.userId.id, + targetPostId = targetNote.id.noteId, + accountId = account.accountId + ) + ) } } @@ -296,7 +310,7 @@ class ToggleReactionUseCaseTest { } doReturn true } - val meta = Meta(uri = "https://misskey.io",) + val meta = Meta(uri = "https://misskey.io") val reactionHistoryDao = mock() val account = Account( "testId", @@ -343,9 +357,18 @@ class ToggleReactionUseCaseTest { } verifyBlocking(reactionHistoryDao) { - create(ReactionHistory("👍", "https://misskey.io")) + create( + ReactionHistory( + reaction = "👍", + instanceDomain = "https://misskey.io", + accountId = account.accountId, + targetPostId = targetNote.id.noteId, + targetUserId = targetNote.userId.id, + ) + ) } } + @Test fun giveMultiByteEmoji() { val targetNote = generateEmptyNote().copy( @@ -367,7 +390,7 @@ class ToggleReactionUseCaseTest { } doReturn true } - val meta = Meta(uri = "https://misskey.io",) + val meta = Meta(uri = "https://misskey.io") val reactionHistoryDao = mock() val account = Account( "testId", @@ -415,7 +438,15 @@ class ToggleReactionUseCaseTest { } verifyBlocking(reactionHistoryDao) { - create(ReactionHistory("🥺", "https://misskey.io")) + create( + ReactionHistory( + reaction = "🥺", + instanceDomain = "https://misskey.io", + accountId = account.accountId, + targetUserId = targetNote.userId.id, + targetPostId = targetNote.id.noteId, + ) + ) } } @@ -439,7 +470,7 @@ class ToggleReactionUseCaseTest { } doReturn true } - val meta = Meta(uri = "https://misskey.io",) + val meta = Meta(uri = "https://misskey.io") val reactionHistoryDao = mock() val account = Account( "testId", @@ -486,7 +517,15 @@ class ToggleReactionUseCaseTest { } verifyBlocking(reactionHistoryDao) { - create(ReactionHistory("\uD83D\uDE06", "https://misskey.io")) + create( + ReactionHistory( + reaction = "\uD83D\uDE06", + instanceDomain = "https://misskey.io", + accountId = account.accountId, + targetPostId = targetNote.id.noteId, + targetUserId = targetNote.userId.id, + ) + ) } } @@ -581,7 +620,15 @@ class ToggleReactionUseCaseTest { } verifyBlocking(reactionHistoryDao) { - create(ReactionHistory("kawaii@misskey.io", "https://fedibird.com")) + create( + ReactionHistory( + reaction = "kawaii@misskey.io", + instanceDomain = "https://fedibird.com", + accountId = account.accountId, + targetUserId = targetNote.userId.id, + targetPostId = targetNote.id.noteId, + ) + ) } } @@ -688,7 +735,15 @@ class ToggleReactionUseCaseTest { } verifyBlocking(reactionHistoryDao) { - create(ReactionHistory("kawaii@misskey.io", "https://fedibird.com")) + create( + ReactionHistory( + reaction = "kawaii@misskey.io", + instanceDomain = "https://fedibird.com", + targetPostId = targetNote.id.noteId, + targetUserId = targetNote.userId.id, + accountId = account.accountId, + ) + ) } } } \ No newline at end of file From 91ff9d3c0ed177510471c1571127c82527a085ab Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 13:46:52 +0900 Subject: [PATCH 104/432] =?UTF-8?q?feat:=20db=E3=81=AB=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E3=81=97=E3=81=9F?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE=E6=83=85?= =?UTF-8?q?=E5=A0=B1=E3=82=84=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E5=AF=BE=E8=B1=A1=E3=81=AE=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=82=84=E3=81=9D=E3=81=AE=E6=8A=95=E7=A8=BF=E4=B8=BB=E3=81=AE?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=82=92=E8=A8=98=E9=8C=B2=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/DataBase.kt | 3 ++- .../impl/history/ReactionHistoryRecord.kt | 23 +++++++++++++++++-- .../history/ReactionHistoryRepositoryImpl.kt | 5 +++- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt index 8fa98bf259..7de0802c7c 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt @@ -117,7 +117,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut PleromaMetadataFeatures::class, ], - version = 44, + version = 45, exportSchema = true, autoMigrations = [ AutoMigration(from = 11, to = 12), @@ -153,6 +153,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut AutoMigration(from = 41, to = 42), AutoMigration(from = 42, to = 43), AutoMigration(from = 43, to = 44), + AutoMigration(from = 44, to = 45), ], views = [UserView::class, GroupMemberView::class, UserListMemberView::class] ) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryRecord.kt index e6c02a3cab..aaef5465eb 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryRecord.kt @@ -11,7 +11,16 @@ data class ReactionHistoryRecord( val reaction: String, @ColumnInfo(name = "instance_domain") - val instanceDomain: String + val instanceDomain: String, + + @ColumnInfo(name = "accountId") + val accountId: Long? = null, + + @ColumnInfo(name = "target_post_id") + val targetPostId: String? = null, + + @ColumnInfo(name = "target_user_id") + val targetUserId: String? = null, ){ @PrimaryKey(autoGenerate = true) @ColumnInfo("id") @@ -22,6 +31,9 @@ data class ReactionHistoryRecord( return ReactionHistoryRecord( reaction = history.reaction, instanceDomain = history.instanceDomain, + targetUserId = history.targetUserId, + targetPostId = history.targetPostId, + accountId = history.accountId, ).apply { id = history.id } @@ -29,6 +41,13 @@ data class ReactionHistoryRecord( } fun toHistory(): ReactionHistory { - return ReactionHistory(reaction, instanceDomain, id) + return ReactionHistory( + reaction = reaction, + instanceDomain = instanceDomain, + accountId = accountId, + targetPostId = targetPostId, + targetUserId = targetUserId, + id = id, + ) } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryRepositoryImpl.kt index 057855c2e3..429245331b 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryRepositoryImpl.kt @@ -54,7 +54,10 @@ class ReactionHistoryRepositoryImpl @Inject constructor( ReactionHistory( instanceDomain = history.instanceDomain, reaction = history.reaction, - id = history.id + id = history.id, + accountId = history.accountId, + targetPostId = history.targetPostId, + targetUserId = history.targetUserId, ) } }.flowOn(Dispatchers.IO) From fcc0a63c7f288abd4e661285f1cc21d7eb7cecdc Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 13:46:55 +0900 Subject: [PATCH 105/432] =?UTF-8?q?feat:=20db=E3=81=AB=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92=E3=81=97=E3=81=9F?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE=E6=83=85?= =?UTF-8?q?=E5=A0=B1=E3=82=84=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E5=AF=BE=E8=B1=A1=E3=81=AE=E6=8A=95=E7=A8=BF?= =?UTF-8?q?=E3=82=84=E3=81=9D=E3=81=AE=E6=8A=95=E7=A8=BF=E4=B8=BB=E3=81=AE?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=82=92=E8=A8=98=E9=8C=B2=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../45.json | 3872 +++++++++++++++++ 1 file changed, 3872 insertions(+) create mode 100644 modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/45.json diff --git a/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/45.json b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/45.json new file mode 100644 index 0000000000..f950dc199d --- /dev/null +++ b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/45.json @@ -0,0 +1,3872 @@ +{ + "formatVersion": 1, + "database": { + "version": 45, + "identityHash": "6422cba92843d6010b114ced1b08f79c", + "entities": [ + { + "tableName": "connection_information", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT NOT NULL, `instanceBaseUrl` TEXT NOT NULL, `encryptedI` TEXT NOT NULL, `viaName` TEXT, `createdAt` TEXT NOT NULL, `isDirect` INTEGER NOT NULL, `updatedAt` TEXT NOT NULL, PRIMARY KEY(`accountId`, `encryptedI`, `instanceBaseUrl`), FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceBaseUrl", + "columnName": "instanceBaseUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedI", + "columnName": "encryptedI", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "viaName", + "columnName": "viaName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirect", + "columnName": "isDirect", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "encryptedI", + "instanceBaseUrl" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "reaction_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `accountId` INTEGER, `target_post_id` TEXT, `target_user_id` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetPostId", + "columnName": "target_post_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetUserId", + "columnName": "target_user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "reaction_user_setting", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`reaction`, `instance_domain`))", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "reaction", + "instance_domain" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "page", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT, `title` TEXT NOT NULL, `pageNumber` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `global_timeline_with_files` INTEGER, `global_timeline_type` TEXT, `local_timeline_with_files` INTEGER, `local_timeline_exclude_nsfw` INTEGER, `local_timeline_type` TEXT, `hybrid_timeline_withFiles` INTEGER, `hybrid_timeline_includeLocalRenotes` INTEGER, `hybrid_timeline_includeMyRenotes` INTEGER, `hybrid_timeline_includeRenotedMyRenotes` INTEGER, `hybrid_timeline_type` TEXT, `home_timeline_withFiles` INTEGER, `home_timeline_includeLocalRenotes` INTEGER, `home_timeline_includeMyRenotes` INTEGER, `home_timeline_includeRenotedMyRenotes` INTEGER, `home_timeline_type` TEXT, `user_list_timeline_listId` TEXT, `user_list_timeline_withFiles` INTEGER, `user_list_timeline_includeLocalRenotes` INTEGER, `user_list_timeline_includeMyRenotes` INTEGER, `user_list_timeline_includeRenotedMyRenotes` INTEGER, `user_list_timeline_type` TEXT, `mention_following` INTEGER, `mention_visibility` TEXT, `mention_type` TEXT, `show_noteId` TEXT, `show_type` TEXT, `tag_tag` TEXT, `tag_reply` INTEGER, `tag_renote` INTEGER, `tag_withFiles` INTEGER, `tag_poll` INTEGER, `tag_type` TEXT, `featured_offset` INTEGER, `featured_type` TEXT, `notification_following` INTEGER, `notification_markAsRead` INTEGER, `notification_type` TEXT, `user_userId` TEXT, `user_includeReplies` INTEGER, `user_includeMyRenotes` INTEGER, `user_withFiles` INTEGER, `user_type` TEXT, `search_query` TEXT, `search_host` TEXT, `search_userId` TEXT, `search_type` TEXT, `favorite_type` TEXT, `antenna_antennaId` TEXT, `antenna_type` TEXT, FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageNumber", + "columnName": "pageNumber", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.withFiles", + "columnName": "global_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.type", + "columnName": "global_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localTimeline.withFiles", + "columnName": "local_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.excludeNsfw", + "columnName": "local_timeline_exclude_nsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.type", + "columnName": "local_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.withFiles", + "columnName": "hybrid_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeLocalRenotes", + "columnName": "hybrid_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeMyRenotes", + "columnName": "hybrid_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeRenotedMyRenotes", + "columnName": "hybrid_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.type", + "columnName": "hybrid_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "homeTimeline.withFiles", + "columnName": "home_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeLocalRenotes", + "columnName": "home_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeMyRenotes", + "columnName": "home_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeRenotedMyRenotes", + "columnName": "home_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.type", + "columnName": "home_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.listId", + "columnName": "user_list_timeline_listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.withFiles", + "columnName": "user_list_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeLocalRenotes", + "columnName": "user_list_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeMyRenotes", + "columnName": "user_list_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeRenotedMyRenotes", + "columnName": "user_list_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.type", + "columnName": "user_list_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.following", + "columnName": "mention_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mention.visibility", + "columnName": "mention_visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.type", + "columnName": "mention_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.noteId", + "columnName": "show_noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.type", + "columnName": "show_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.tag", + "columnName": "tag_tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.reply", + "columnName": "tag_reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.renote", + "columnName": "tag_renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.withFiles", + "columnName": "tag_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.poll", + "columnName": "tag_poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.type", + "columnName": "tag_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "featured.offset", + "columnName": "featured_offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "featured.type", + "columnName": "featured_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notification.following", + "columnName": "notification_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.markAsRead", + "columnName": "notification_markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.type", + "columnName": "notification_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.userId", + "columnName": "user_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeReplies", + "columnName": "user_includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeMyRenotes", + "columnName": "user_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.withFiles", + "columnName": "user_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.type", + "columnName": "user_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.query", + "columnName": "search_query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.host", + "columnName": "search_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.userId", + "columnName": "search_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.type", + "columnName": "search_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "favorite.type", + "columnName": "favorite_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.antennaId", + "columnName": "antenna_antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.type", + "columnName": "antenna_type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_page_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "poll_choice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`choice` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`choice`, `weight`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "choice", + "columnName": "choice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "choice", + "weight", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_poll_choice_table_draft_note_id_choice", + "unique": false, + "columnNames": [ + "draft_note_id", + "choice" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_poll_choice_table_draft_note_id_choice` ON `${TABLE_NAME}` (`draft_note_id`, `choice`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "user_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, PRIMARY KEY(`userId`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_user_id_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_id_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_file_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL DEFAULT 'name none', `remote_file_id` TEXT, `file_path` TEXT, `is_sensitive` INTEGER, `type` TEXT, `thumbnailUrl` TEXT, `draft_note_id` INTEGER NOT NULL, `folder_id` TEXT, `file_id` INTEGER PRIMARY KEY AUTOINCREMENT, FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'name none'" + }, + { + "fieldPath": "remoteFileId", + "columnName": "remote_file_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileId", + "columnName": "file_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "file_id" + ] + }, + "indices": [ + { + "name": "index_draft_file_table_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_table_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_note_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `visibility` TEXT NOT NULL, `text` TEXT, `cw` TEXT, `viaMobile` INTEGER, `localOnly` INTEGER, `noExtractMentions` INTEGER, `noExtractHashtags` INTEGER, `noExtractEmojis` INTEGER, `replyId` TEXT, `renoteId` TEXT, `channelId` TEXT, `scheduleWillPostAt` TEXT, `draft_note_id` INTEGER PRIMARY KEY AUTOINCREMENT, `isSensitive` INTEGER, `multiple` INTEGER, `expiresAt` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cw", + "columnName": "cw", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "viaMobile", + "columnName": "viaMobile", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localOnly", + "columnName": "localOnly", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractMentions", + "columnName": "noExtractMentions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractHashtags", + "columnName": "noExtractHashtags", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractEmojis", + "columnName": "noExtractEmojis", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "replyId", + "columnName": "replyId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "renoteId", + "columnName": "renoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "scheduleWillPostAt", + "columnName": "scheduleWillPostAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.multiple", + "columnName": "multiple", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.expiresAt", + "columnName": "expiresAt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_draft_note_table_accountId_text", + "unique": false, + "columnNames": [ + "accountId", + "text" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_note_table_accountId_text` ON `${TABLE_NAME}` (`accountId`, `text`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "url_preview", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `icon` TEXT, `description` TEXT, `thumbnail` TEXT, `siteName` TEXT, PRIMARY KEY(`url`))", + "fields": [ + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnail", + "columnName": "thumbnail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "siteName", + "columnName": "siteName", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "url" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "account_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remoteId` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `userName` TEXT NOT NULL, `encryptedToken` TEXT NOT NULL, `instanceType` TEXT NOT NULL DEFAULT 'misskey', `accountId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedToken", + "columnName": "encryptedToken", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceType", + "columnName": "instanceType", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'misskey'" + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "accountId" + ] + }, + "indices": [ + { + "name": "index_account_table_remoteId", + "unique": false, + "columnNames": [ + "remoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_remoteId` ON `${TABLE_NAME}` (`remoteId`)" + }, + { + "name": "index_account_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_account_table_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_userName` ON `${TABLE_NAME}` (`userName`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "page_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `title` TEXT NOT NULL, `weight` INTEGER NOT NULL, `pageId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `withFiles` INTEGER, `excludeNsfw` INTEGER, `includeLocalRenotes` INTEGER, `includeMyRenotes` INTEGER, `includeRenotedMyRenotes` INTEGER, `listId` TEXT, `following` INTEGER, `visibility` TEXT, `noteId` TEXT, `tag` TEXT, `reply` INTEGER, `renote` INTEGER, `poll` INTEGER, `offset` INTEGER, `markAsRead` INTEGER, `userId` TEXT, `includeReplies` INTEGER, `query` TEXT, `host` TEXT, `antennaId` TEXT, `channelId` TEXT, `clipId` TEXT)", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageParams.type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageParams.withFiles", + "columnName": "withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.excludeNsfw", + "columnName": "excludeNsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeLocalRenotes", + "columnName": "includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeMyRenotes", + "columnName": "includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeRenotedMyRenotes", + "columnName": "includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.listId", + "columnName": "listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.following", + "columnName": "following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.tag", + "columnName": "tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.reply", + "columnName": "reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.renote", + "columnName": "renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.poll", + "columnName": "poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.offset", + "columnName": "offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.markAsRead", + "columnName": "markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.includeReplies", + "columnName": "includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.query", + "columnName": "query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.antennaId", + "columnName": "antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.clipId", + "columnName": "clipId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "pageId" + ] + }, + "indices": [ + { + "name": "index_page_table_weight", + "unique": false, + "columnNames": [ + "weight" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_weight` ON `${TABLE_NAME}` (`weight`)" + }, + { + "name": "index_page_table_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "meta_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `bannerUrl` TEXT, `cacheRemoteFiles` INTEGER, `description` TEXT, `disableGlobalTimeline` INTEGER, `disableLocalTimeline` INTEGER, `disableRegistration` INTEGER, `driveCapacityPerLocalUserMb` INTEGER, `driveCapacityPerRemoteUserMb` INTEGER, `enableDiscordIntegration` INTEGER, `enableEmail` INTEGER, `enableEmojiReaction` INTEGER, `enableGithubIntegration` INTEGER, `enableRecaptcha` INTEGER, `enableServiceWorker` INTEGER, `enableTwitterIntegration` INTEGER, `errorImageUrl` TEXT, `feedbackUrl` TEXT, `iconUrl` TEXT, `maintainerEmail` TEXT, `maintainerName` TEXT, `mascotImageUrl` TEXT, `maxNoteTextLength` INTEGER, `name` TEXT, `recaptchaSiteKey` TEXT, `secure` INTEGER, `swPublicKey` TEXT, `toSUrl` TEXT, `version` TEXT NOT NULL, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cacheRemoteFiles", + "columnName": "cacheRemoteFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "disableGlobalTimeline", + "columnName": "disableGlobalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableLocalTimeline", + "columnName": "disableLocalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableRegistration", + "columnName": "disableRegistration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerLocalUserMb", + "columnName": "driveCapacityPerLocalUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerRemoteUserMb", + "columnName": "driveCapacityPerRemoteUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableDiscordIntegration", + "columnName": "enableDiscordIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmail", + "columnName": "enableEmail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmojiReaction", + "columnName": "enableEmojiReaction", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableGithubIntegration", + "columnName": "enableGithubIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableRecaptcha", + "columnName": "enableRecaptcha", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableServiceWorker", + "columnName": "enableServiceWorker", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableTwitterIntegration", + "columnName": "enableTwitterIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "errorImageUrl", + "columnName": "errorImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "feedbackUrl", + "columnName": "feedbackUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerEmail", + "columnName": "maintainerEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerName", + "columnName": "maintainerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mascotImageUrl", + "columnName": "mascotImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maxNoteTextLength", + "columnName": "maxNoteTextLength", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recaptchaSiteKey", + "columnName": "recaptchaSiteKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secure", + "columnName": "secure", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "swPublicKey", + "columnName": "swPublicKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toSUrl", + "columnName": "toSUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "emoji_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `host` TEXT, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` TEXT, PRIMARY KEY(`name`, `instanceDomain`), FOREIGN KEY(`instanceDomain`) REFERENCES `meta_table`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_emoji_table_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [ + { + "table": "meta_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "instanceDomain" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "emoji_alias_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`alias` TEXT NOT NULL, `name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, PRIMARY KEY(`alias`, `name`, `instanceDomain`), FOREIGN KEY(`name`, `instanceDomain`) REFERENCES `emoji_table`(`name`, `instanceDomain`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "alias", + "columnName": "alias", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "alias", + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_alias_table_name_instanceDomain", + "unique": false, + "columnNames": [ + "name", + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_alias_table_name_instanceDomain` ON `${TABLE_NAME}` (`name`, `instanceDomain`)" + } + ], + "foreignKeys": [ + { + "table": "emoji_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "name", + "instanceDomain" + ], + "referencedColumns": [ + "name", + "instanceDomain" + ] + } + ] + }, + { + "tableName": "unread_notifications_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, PRIMARY KEY(`accountId`, `notificationId`), FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "nicknames", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`nickname` TEXT NOT NULL, `username` TEXT NOT NULL, `host` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "nickname", + "columnName": "nickname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_nicknames_username_host", + "unique": true, + "columnNames": [ + "username", + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_nicknames_username_host` ON `${TABLE_NAME}` (`username`, `host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "utf8_emojis_by_amio", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`codes` TEXT NOT NULL, `name` TEXT NOT NULL, `char` TEXT NOT NULL, PRIMARY KEY(`codes`))", + "fields": [ + { + "fieldPath": "codes", + "columnName": "codes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "charCode", + "columnName": "char", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "codes" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "drive_file_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `relatedAccountId` INTEGER NOT NULL, `createdAt` TEXT, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `md5` TEXT, `size` INTEGER, `url` TEXT NOT NULL, `isSensitive` INTEGER NOT NULL, `thumbnailUrl` TEXT, `folderId` TEXT, `userId` TEXT, `comment` TEXT, `blurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`relatedAccountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "relatedAccountId", + "columnName": "relatedAccountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "md5", + "columnName": "md5", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folderId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "blurhash", + "columnName": "blurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_drive_file_v1_serverId_relatedAccountId", + "unique": true, + "columnNames": [ + "serverId", + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_drive_file_v1_serverId_relatedAccountId` ON `${TABLE_NAME}` (`serverId`, `relatedAccountId`)" + }, + { + "name": "index_drive_file_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_drive_file_v1_relatedAccountId", + "unique": false, + "columnNames": [ + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_relatedAccountId` ON `${TABLE_NAME}` (`relatedAccountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "relatedAccountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "draft_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`draftNoteId` INTEGER NOT NULL, `filePropertyId` INTEGER, `localFileId` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`filePropertyId`) REFERENCES `drive_file_v1`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`localFileId`) REFERENCES `draft_local_file_v2_table`(`localFileId`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "draftNoteId", + "columnName": "draftNoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filePropertyId", + "columnName": "filePropertyId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId", + "unique": true, + "columnNames": [ + "draftNoteId", + "filePropertyId", + "localFileId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId` ON `${TABLE_NAME}` (`draftNoteId`, `filePropertyId`, `localFileId`)" + }, + { + "name": "index_draft_file_v2_table_draftNoteId", + "unique": false, + "columnNames": [ + "draftNoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId` ON `${TABLE_NAME}` (`draftNoteId`)" + }, + { + "name": "index_draft_file_v2_table_localFileId", + "unique": false, + "columnNames": [ + "localFileId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_localFileId` ON `${TABLE_NAME}` (`localFileId`)" + }, + { + "name": "index_draft_file_v2_table_filePropertyId", + "unique": false, + "columnNames": [ + "filePropertyId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_filePropertyId` ON `${TABLE_NAME}` (`filePropertyId`)" + } + ], + "foreignKeys": [ + { + "table": "drive_file_v1", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "filePropertyId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "draft_local_file_v2_table", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "localFileId" + ], + "referencedColumns": [ + "localFileId" + ] + } + ] + }, + { + "tableName": "draft_local_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `file_path` TEXT NOT NULL, `is_sensitive` INTEGER, `type` TEXT NOT NULL, `thumbnailUrl` TEXT, `folder_id` TEXT, `file_size` INTEGER, `comment` TEXT, `localFileId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "localFileId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "group_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `ownerId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "ownerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_v1_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_group_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_group_v1_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_v1_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "group_member_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`groupId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`groupId`) REFERENCES `group_v1`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_member_v1_groupId", + "unique": false, + "columnNames": [ + "groupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_member_v1_groupId` ON `${TABLE_NAME}` (`groupId`)" + }, + { + "name": "index_group_member_v1_groupId_userId", + "unique": true, + "columnNames": [ + "groupId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_member_v1_groupId_userId` ON `${TABLE_NAME}` (`groupId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "group_v1", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "groupId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `userName` TEXT NOT NULL, `name` TEXT, `avatarUrl` TEXT, `isCat` INTEGER, `isBot` INTEGER, `host` TEXT NOT NULL, `isSameHost` INTEGER NOT NULL, `avatarBlurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatarUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isCat", + "columnName": "isCat", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isBot", + "columnName": "isBot", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSameHost", + "columnName": "isSameHost", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "avatarBlurhash", + "columnName": "avatarBlurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_serverId_accountId", + "unique": true, + "columnNames": [ + "serverId", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_serverId_accountId` ON `${TABLE_NAME}` (`serverId`, `accountId`)" + }, + { + "name": "index_user_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_userName` ON `${TABLE_NAME}` (`userName`)" + }, + { + "name": "index_user_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_host", + "unique": false, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_detailed_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_detailed_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_detailed_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_emoji", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_emoji_name_userId", + "unique": true, + "columnNames": [ + "name", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_emoji_name_userId` ON `${TABLE_NAME}` (`name`, `userId`)" + }, + { + "name": "index_user_emoji_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_emoji_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "pinned_note_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_pinned_note_id_noteId_userId", + "unique": true, + "columnNames": [ + "noteId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_pinned_note_id_noteId_userId` ON `${TABLE_NAME}` (`noteId`, `userId`)" + }, + { + "name": "index_pinned_note_id_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pinned_note_id_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`faviconUrl` TEXT, `iconUrl` TEXT, `name` TEXT, `softwareName` TEXT, `softwareVersion` TEXT, `themeColor` TEXT, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "faviconUrl", + "columnName": "faviconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareName", + "columnName": "softwareName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareVersion", + "columnName": "softwareVersion", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_instance_info_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_instance_info_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_profile_field", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `value` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_profile_field_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_profile_field_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "word_filter_regex_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pattern` TEXT NOT NULL, `parentId` INTEGER NOT NULL, PRIMARY KEY(`parentId`), FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "pattern", + "columnName": "pattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentId" + ] + }, + "indices": [ + { + "name": "index_word_filter_regex_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_regex_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_word_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`word` TEXT NOT NULL, `parentId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "word", + "columnName": "word", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_word_filter_word_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_word_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_list", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_list_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_user_list_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_list_member", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userListId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userListId`) REFERENCES `user_list`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userListId", + "columnName": "userListId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_member_userListId", + "unique": false, + "columnNames": [ + "userListId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_member_userListId` ON `${TABLE_NAME}` (`userListId`)" + }, + { + "name": "index_user_list_member_userListId_userId", + "unique": true, + "columnNames": [ + "userListId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_member_userListId_userId` ON `${TABLE_NAME}` (`userListId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "user_list", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userListId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "instance_info_v1_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `host` TEXT NOT NULL, `name` TEXT, `description` TEXT, `clientMaxBodyByteSize` INTEGER, `iconUrl` TEXT, `themeColor` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "clientMaxBodyByteSize", + "columnName": "clientMaxBodyByteSize", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_instance_info_v1_table_host", + "unique": true, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_instance_info_v1_table_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "search_histories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `keyword` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keyword", + "columnName": "keyword", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_search_histories_keyword_accountId", + "unique": true, + "columnNames": [ + "keyword", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_search_histories_keyword_accountId` ON `${TABLE_NAME}` (`keyword`, `accountId`)" + }, + { + "name": "index_search_histories_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_search_histories_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_info_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_info_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_info_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_related_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_related_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_related_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nodeinfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `nodeInfoVersion` TEXT NOT NULL, `name` TEXT NOT NULL, `version` TEXT NOT NULL, PRIMARY KEY(`host`))", + "fields": [ + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nodeInfoVersion", + "columnName": "nodeInfoVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "host" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `title` TEXT NOT NULL, `description` TEXT NOT NULL, `email` TEXT NOT NULL, `version` TEXT NOT NULL, `urls_streamingApi` TEXT, `configuration_statuses_maxCharacters` INTEGER, `configuration_statuses_maxMediaAttachments` INTEGER, `configuration_polls_maxOptions` INTEGER, `configuration_polls_maxCharactersPerOption` INTEGER, `configuration_polls_minExpiration` INTEGER, `configuration_polls_maxExpiration` INTEGER, `configuration_emoji_reactions_myReactions` INTEGER, `configuration_emoji_reactions_maxReactionsPerAccount` INTEGER, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "urls.streamingApi", + "columnName": "urls_streamingApi", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxCharacters", + "columnName": "configuration_statuses_maxCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxMediaAttachments", + "columnName": "configuration_statuses_maxMediaAttachments", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxOptions", + "columnName": "configuration_polls_maxOptions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxCharactersPerOption", + "columnName": "configuration_polls_maxCharactersPerOption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.minExpiration", + "columnName": "configuration_polls_minExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxExpiration", + "columnName": "configuration_polls_maxExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactions", + "columnName": "configuration_emoji_reactions_myReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactionsPerAccount", + "columnName": "configuration_emoji_reactions_maxReactionsPerAccount", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "custom_emojis", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT, `name` TEXT NOT NULL, `emojiHost` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "emojiHost", + "columnName": "emojiHost", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_custom_emojis_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_custom_emojis_emojiHost", + "unique": false, + "columnNames": [ + "emojiHost" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost` ON `${TABLE_NAME}` (`emojiHost`)" + }, + { + "name": "index_custom_emojis_category", + "unique": false, + "columnNames": [ + "category" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_category` ON `${TABLE_NAME}` (`category`)" + }, + { + "name": "index_custom_emojis_emojiHost_name", + "unique": true, + "columnNames": [ + "emojiHost", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost_name` ON `${TABLE_NAME}` (`emojiHost`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "custom_emoji_aliases", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`emojiId` INTEGER NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`emojiId`, `value`), FOREIGN KEY(`emojiId`) REFERENCES `custom_emojis`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "emojiId", + "columnName": "emojiId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "emojiId", + "value" + ] + }, + "indices": [ + { + "name": "index_custom_emoji_aliases_emojiId_value", + "unique": false, + "columnNames": [ + "emojiId", + "value" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emoji_aliases_emojiId_value` ON `${TABLE_NAME}` (`emojiId`, `value`)" + } + ], + "foreignKeys": [ + { + "table": "custom_emojis", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "emojiId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "notification_json_cache_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, `json` TEXT NOT NULL, `key` TEXT, `weight` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `notificationId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "json", + "columnName": "json", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [ + { + "name": "index_notification_json_cache_v1_key", + "unique": false, + "columnNames": [ + "key" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notification_json_cache_v1_key` ON `${TABLE_NAME}` (`key`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_word_filters_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `filterId` TEXT NOT NULL, `phrase` TEXT NOT NULL, `wholeWord` INTEGER NOT NULL, `expiresAt` TEXT, `irreversible` INTEGER NOT NULL, `isContextHome` INTEGER NOT NULL, `isContextNotifications` INTEGER NOT NULL, `isContextPublic` INTEGER NOT NULL, `isContextThread` INTEGER NOT NULL, `isContextAccount` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `filterId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filterId", + "columnName": "filterId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phrase", + "columnName": "phrase", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "wholeWord", + "columnName": "wholeWord", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expiresAt", + "columnName": "expiresAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "irreversible", + "columnName": "irreversible", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextHome", + "columnName": "isContextHome", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextNotifications", + "columnName": "isContextNotifications", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextPublic", + "columnName": "isContextPublic", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextThread", + "columnName": "isContextThread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextAccount", + "columnName": "isContextAccount", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "filterId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "renote_mute_users", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `postedAt` TEXT, PRIMARY KEY(`userId`, `accountId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "postedAt", + "columnName": "postedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "accountId" + ] + }, + "indices": [ + { + "name": "index_renote_mute_users_postedAt", + "unique": false, + "columnNames": [ + "postedAt" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_postedAt` ON `${TABLE_NAME}` (`postedAt`)" + }, + { + "name": "index_renote_mute_users_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_fedibird_capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_mastodon_instance_fedibird_capabilities_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mastodon_instance_fedibird_capabilities_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "pleroma_metadata_features", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_pleroma_metadata_features_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pleroma_metadata_features_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + } + ], + "views": [ + { + "viewName": "user_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select user.*, nicknames.nickname from user left join nicknames on user.userName = nicknames.username and user.host = nicknames.host" + }, + { + "viewName": "group_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.groupId, u.id as userId, u.avatarUrl, u.serverId from group_member_v1 as m \n inner join group_v1 as g\n inner join user as u\n on m.groupId = g.id\n and m.userId = u.serverId\n and g.accountId = u.accountId" + }, + { + "viewName": "user_list_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.userListId, u.id as userId, u.avatarUrl, u.serverId from user_list_member as m \n inner join user_list as ul\n inner join user as u\n on m.userListId = ul.id\n and m.userId = u.serverId\n and ul.accountId = u.accountId" + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '6422cba92843d6010b114ced1b08f79c')" + ] + } +} \ No newline at end of file From 349bae72a58921506f4eb4651b89d1f25759f254 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 14:07:59 +0900 Subject: [PATCH 106/432] =?UTF-8?q?feat:=20=E6=9C=AA=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=81=8B=E3=81=A4=E3=82=88=E3=81=8F=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=82=8B=E7=9B=B8=E6=89=8B=E3=81=A7=E9=9B=86=E8=A8=88?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...equentlyReactionAndUnFollowedUserRecord.kt | 9 ++++++++ .../impl/history/ReactionHistoryDao.kt | 21 +++++++++++++++++++ 2 files changed, 30 insertions(+) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/FrequentlyReactionAndUnFollowedUserRecord.kt diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/FrequentlyReactionAndUnFollowedUserRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/FrequentlyReactionAndUnFollowedUserRecord.kt new file mode 100644 index 0000000000..634e2d9918 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/FrequentlyReactionAndUnFollowedUserRecord.kt @@ -0,0 +1,9 @@ +package net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.history + +import androidx.room.ColumnInfo + +data class FrequentlyReactionAndUnFollowedUserRecord( + @ColumnInfo(name = "targetUserId") val targetUserId: String, + @ColumnInfo(name = "accountId") val accountId: Long, + @ColumnInfo(name = "reactionCount") val reactionCount: Int, +) \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryDao.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryDao.kt index 760422b241..b43baddd71 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryDao.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryDao.kt @@ -35,5 +35,26 @@ interface ReactionHistoryDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(reactionHistory: ReactionHistoryRecord) + + + @Query( + """ + select + r1.accountId as accountId, + r1.target_user_id as targetUserId, + count(r1.id) as reactionCount + from reaction_history as r1 + inner join user as u + on r1.accountId = u.accountId and r1.target_user_id = u.serverId + inner join user_related_state as ur + on u.id = ur.userId + where ur.isFollowing = 0 + and r1.accountId = :accountId + group by r1.accountId, r1.target_user_id + order by count(r1.id) desc + limit 100 + """ + ) + suspend fun findFrequentlyReactionUserAndUnFollowed(accountId: Long): List } From de1d48d21ee961f82ee91fb37a2c2cdcadef34aa Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 14:10:51 +0900 Subject: [PATCH 107/432] =?UTF-8?q?feat:=20limit=E3=82=92=E3=81=8B?= =?UTF-8?q?=E3=81=91=E3=82=89=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/reaction/impl/history/ReactionHistoryDao.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryDao.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryDao.kt index b43baddd71..3b8fe68864 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryDao.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/reaction/impl/history/ReactionHistoryDao.kt @@ -52,9 +52,9 @@ interface ReactionHistoryDao { and r1.accountId = :accountId group by r1.accountId, r1.target_user_id order by count(r1.id) desc - limit 100 + limit :limit """ ) - suspend fun findFrequentlyReactionUserAndUnFollowed(accountId: Long): List + suspend fun findFrequentlyReactionUserAndUnFollowed(accountId: Long, limit: Int): List } From fda80550a018426c2d96bc69b3be5fce38f0a69b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 14:21:57 +0900 Subject: [PATCH 108/432] =?UTF-8?q?feat:=20Misskey=E3=81=AE=E6=99=82?= =?UTF-8?q?=E3=81=AF=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=92=E3=81=97=E3=81=9F=E7=9B=B8=E6=89=8B=E3=81=AE=E3=83=A6?= =?UTF-8?q?=E3=83=BC=E3=82=B6=E3=82=92=E5=85=83=E3=81=AB=E3=81=97=E3=81=9F?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=81=AE=E5=80=99=E8=A3=9C=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/infrastructure/user/UserRepositoryImpl.kt | 12 ++++++++++++ .../pantasystem/milktea/search/SearchTopFragment.kt | 1 + .../pantasystem/milktea/search/SearchTopViewModel.kt | 5 +++++ .../milktea/search/explore/ExploreFragment.kt | 2 +- .../milktea/search/explore/ExploreViewModel.kt | 8 ++++++++ .../milktea/model/user/query/FindUsersQuery.kt | 2 ++ 6 files changed, 29 insertions(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt index 4218c69da2..7b37edd5de 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/UserRepositoryImpl.kt @@ -10,6 +10,7 @@ import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.converters.UserDTOEntityConverter +import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.history.ReactionHistoryDao import net.pantasystem.milktea.data.infrastructure.toUserRelated import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository @@ -18,6 +19,7 @@ import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserDataSource import net.pantasystem.milktea.model.user.UserNotFoundException import net.pantasystem.milktea.model.user.UserRepository +import net.pantasystem.milktea.model.user.query.FindUsersFromFrequentlyReactionUsers import net.pantasystem.milktea.model.user.query.FindUsersQuery import net.pantasystem.milktea.model.user.query.FindUsersQuery4Mastodon import net.pantasystem.milktea.model.user.query.FindUsersQuery4Misskey @@ -33,6 +35,7 @@ internal class UserRepositoryImpl @Inject constructor( val userApiAdapter: UserApiAdapter, private val mastodonAPIProvider: MastodonAPIProvider, val userDTOEntityConverter: UserDTOEntityConverter, + private val reactionHistoryDao: ReactionHistoryDao, @IODispatcher val ioDispatcher: CoroutineDispatcher, ) : UserRepository { private val logger: Logger by lazy { @@ -186,6 +189,15 @@ internal class UserRepositoryImpl @Inject constructor( userDataSource.add(it) } ?: emptyList() } + is FindUsersFromFrequentlyReactionUsers -> { + val userIds = reactionHistoryDao.findFrequentlyReactionUserAndUnFollowed( + accountId = accountId, + limit = 20, + ).map { + it.targetUserId + } + userDataSource.getIn(accountId, userIds).getOrThrow() + } } } } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt index 728ebcc117..78795a1f29 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt @@ -119,6 +119,7 @@ class SearchPagerAdapterV2( SearchTopTabItem.TabType.MisskeyExploreUsers -> ExploreFragment.newInstance(ExploreType.Local) SearchTopTabItem.TabType.MisskeyExploreFediverseUsers -> ExploreFragment.newInstance(ExploreType.Fediverse) SearchTopTabItem.TabType.MastodonUserSuggestions -> ExploreFragment.newInstance(ExploreType.MastodonUserSuggestions) + SearchTopTabItem.TabType.UserSuggestionByReaction -> ExploreFragment.newInstance(ExploreType.UserSuggestionsByReaction) } } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt index 4100b1edd7..49737e7473 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt @@ -38,6 +38,10 @@ class SearchTopViewModel @Inject constructor( SearchTopTabItem( StringSource(R.string.explore_fediverse), SearchTopTabItem.TabType.MisskeyExploreFediverseUsers, + ), + SearchTopTabItem( + StringSource(R.string.suggestion_users), + SearchTopTabItem.TabType.UserSuggestionByReaction, ) ) } @@ -75,5 +79,6 @@ data class SearchTopTabItem( MastodonUserSuggestions, MisskeyExploreUsers, MisskeyExploreFediverseUsers, + UserSuggestionByReaction, } } \ No newline at end of file diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt index 952dc1acdf..070e351e71 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreFragment.kt @@ -127,6 +127,6 @@ class ExploreFragment : Fragment() { } enum class ExploreType { - Local, Fediverse, MastodonUserSuggestions, + Local, Fediverse, MastodonUserSuggestions, UserSuggestionsByReaction } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt index fc58760513..b9315dfde6 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/explore/ExploreViewModel.kt @@ -78,6 +78,14 @@ class ExploreViewModel @Inject constructor( ) ) } + ExploreType.UserSuggestionsByReaction -> { + listOf( + ExploreItem( + StringSource(R.string.suggestion_users), + FindUsersFromFrequentlyReactionUsers, + ) + ) + } } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/user/query/FindUsersQuery.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/user/query/FindUsersQuery.kt index fad5ccc0ce..895d947ad5 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/user/query/FindUsersQuery.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/user/query/FindUsersQuery.kt @@ -6,6 +6,8 @@ sealed interface FindUsersQuery4Mastodon : FindUsersQuery { data class SuggestUsers(val limit: Int? = null) : FindUsersQuery4Mastodon } +object FindUsersFromFrequentlyReactionUsers : FindUsersQuery + data class FindUsersQuery4Misskey( val origin: Origin?, val sort: OrderBy?, From 549d2f1790b1ee103a208a14e8343cfa51f1f12d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 14:27:04 +0900 Subject: [PATCH 109/432] =?UTF-8?q?feat:=20Suggestion=E3=82=92=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AB=E3=83=AA?= =?UTF-8?q?=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E6=99=82=E3=81=AB?= =?UTF-8?q?=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5=E3=82=92=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/reaction/ToggleReactionUseCase.kt | 5 +++ .../reaction/ToggleReactionUseCaseTest.kt | 40 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt index bf2180b038..7a643dcd7e 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCase.kt @@ -12,6 +12,7 @@ import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.notes.NoteRepository import net.pantasystem.milktea.model.notes.reaction.history.ReactionHistory import net.pantasystem.milktea.model.notes.reaction.history.ReactionHistoryRepository +import net.pantasystem.milktea.model.user.UserRepository import javax.inject.Inject import javax.inject.Singleton @@ -29,6 +30,7 @@ class ToggleReactionUseCase @Inject constructor( private val instanceInfoService: InstanceInfoService, private val customEmojiRepository: CustomEmojiRepository, private val checkEmoji: CheckEmoji, + private val userRepository: UserRepository, ) : UseCase { suspend operator fun invoke(noteId: Note.Id, reaction: String): Result { @@ -80,6 +82,9 @@ class ToggleReactionUseCase @Inject constructor( ) ) } + + // NOTE: Suggestionを表示するためにユーザのデータが必要になるので、キャッシュを更新しておく + userRepository.sync(note.userId).getOrThrow() }.mapCancellableCatching { noteRepository.sync(noteId).getOrThrow() } diff --git a/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt b/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt index 60831a551b..81135094cf 100644 --- a/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt +++ b/modules/model/src/test/java/net/pantasystem/milktea/model/notes/reaction/ToggleReactionUseCaseTest.kt @@ -97,6 +97,11 @@ class ToggleReactionUseCaseTest { onBlocking { find(any()) } doReturn Result.success(InstanceInfoType.Misskey(meta)) + }, + userRepository = mock() { + onBlocking { + sync(any()) + } doReturn Result.success(Unit) } ) runBlocking { @@ -178,6 +183,11 @@ class ToggleReactionUseCaseTest { onBlocking { find(any()) } doReturn Result.success(InstanceInfoType.Misskey(meta)) + }, + userRepository = mock() { + onBlocking { + sync(any()) + } doReturn Result.success(Unit) } ) runBlocking { @@ -267,6 +277,11 @@ class ToggleReactionUseCaseTest { onBlocking { find(any()) } doReturn Result.success(InstanceInfoType.Misskey(meta)) + }, + userRepository = mock() { + onBlocking { + sync(any()) + } doReturn Result.success(Unit) } ) @@ -346,6 +361,11 @@ class ToggleReactionUseCaseTest { onBlocking { findByName(any(), any()) } doReturn Result.success(emptyList()) + }, + userRepository = mock() { + onBlocking { + sync(any()) + } doReturn Result.success(Unit) } ) @@ -427,6 +447,11 @@ class ToggleReactionUseCaseTest { onBlocking { findByName(any(), any()) } doReturn Result.success(emptyList()) + }, + userRepository = mock() { + onBlocking { + sync(any()) + } doReturn Result.success(Unit) } ) @@ -506,6 +531,11 @@ class ToggleReactionUseCaseTest { onBlocking { find(any()) } doReturn Result.success(InstanceInfoType.Misskey(meta)) + }, + userRepository = mock() { + onBlocking { + sync(any()) + } doReturn Result.success(Unit) } ) @@ -609,6 +639,11 @@ class ToggleReactionUseCaseTest { ) ) ) + }, + userRepository = mock() { + onBlocking { + sync(any()) + } doReturn Result.success(Unit) } ) @@ -724,6 +759,11 @@ class ToggleReactionUseCaseTest { ) ) ) + }, + userRepository = mock() { + onBlocking { + sync(any()) + } doReturn Result.success(Unit) } ) From e30a8a6f8704f278b9c66debfa26bf245d37f6ca Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 14:50:20 +0900 Subject: [PATCH 110/432] =?UTF-8?q?feat:=20migration=20test=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/infrastructure/DatabaseMigrationTest.kt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/DatabaseMigrationTest.kt b/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/DatabaseMigrationTest.kt index 279e0311bb..d4462fab66 100644 --- a/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/DatabaseMigrationTest.kt +++ b/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/DatabaseMigrationTest.kt @@ -215,4 +215,18 @@ class DatabaseMigrationTest { helper.createDatabase(testDb, 42) helper.runMigrationsAndValidate(testDb, 43, true) } + + @Test + @Throws(IOException::class) + fun migrate43To44() { + helper.createDatabase(testDb, 43) + helper.runMigrationsAndValidate(testDb, 44, true) + } + + @Test + @Throws(IOException::class) + fun migrate44To45() { + helper.createDatabase(testDb, 44) + helper.runMigrationsAndValidate(testDb, 45, true) + } } \ No newline at end of file From d215f384d0ec3831fd5399d8302b6de4eaa5535d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 16:41:21 +0900 Subject: [PATCH 111/432] =?UTF-8?q?rm:=20=E4=B8=8D=E8=A6=81=E3=81=AB?= =?UTF-8?q?=E3=81=AA=E3=81=A3=E3=81=9F=E6=A7=8B=E9=80=A0=E3=82=92=E5=89=8A?= =?UTF-8?q?=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/api/misskey/MisskeyAPI.kt | 4 ---- .../pantasystem/milktea/model/hashtag/HashTag.kt | 15 --------------- 2 files changed, 19 deletions(-) delete mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt index 36ba57c4f6..4f5f43a155 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt @@ -7,7 +7,6 @@ import net.pantasystem.milktea.api.misskey.auth.App import net.pantasystem.milktea.api.misskey.clip.* import net.pantasystem.milktea.api.misskey.drive.* import net.pantasystem.milktea.api.misskey.favorite.Favorite -import net.pantasystem.milktea.api.misskey.hashtag.RequestHashTagList import net.pantasystem.milktea.api.misskey.hashtag.SearchHashtagRequest import net.pantasystem.milktea.api.misskey.list.* import net.pantasystem.milktea.api.misskey.messaging.MessageAction @@ -36,7 +35,6 @@ import net.pantasystem.milktea.api.misskey.users.renote.mute.RenoteMutesRequest import net.pantasystem.milktea.api.misskey.users.report.ReportDTO import net.pantasystem.milktea.api.misskey.v13.EmojisResponse import net.pantasystem.milktea.model.drive.Directory -import net.pantasystem.milktea.model.hashtag.HashTag import net.pantasystem.milktea.model.instance.Meta import net.pantasystem.milktea.model.instance.RequestMeta import net.pantasystem.milktea.model.messaging.RequestMessageHistory @@ -241,8 +239,6 @@ interface MisskeyAPI { @POST("api/mute/delete") suspend fun unmuteUser(@Body requestUser: RequestUser): Response - @POST("api/hashtags/list") - suspend fun getHashTagList(@Body requestHashTagList: RequestHashTagList): Response> @POST("api/sw/register") suspend fun swRegister(@Body subscription: Subscription) : Response diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt deleted file mode 100644 index 27bdc12c86..0000000000 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt +++ /dev/null @@ -1,15 +0,0 @@ -package net.pantasystem.milktea.model.hashtag - -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable - -@Serializable -data class HashTag( - @SerialName("tag") val tag: String, - @SerialName("mentionedUserCount") val mentionedUserCount: Int?, - @SerialName("mentionedLocalUserCount") val mentionedLocalUserCount: Int?, - @SerialName("mentionedRemoteUserCount") val mentionedRemoteUserCount: Int?, - @SerialName("attachedUsersCount") val attachedUsersCount: Int?, - @SerialName("attachedLocalUsersCount") val attachedLocalUsersCount: Int?, - @SerialName("attachedRemoteUsersCount") val attachedRemoteUsersCount: Int? -) \ No newline at end of file From eed8958ecfb40ffa17a1fd19745eeb70b8f1fdd3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 16:50:49 +0900 Subject: [PATCH 112/432] =?UTF-8?q?feat:=20=E3=83=88=E3=83=AC=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=81=AE=E3=82=BF=E3=82=B0=E3=82=92=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/mastodon/tag/MastodonTagDTO.kt | 9 +++++ .../milktea/api/misskey/trend/HashtagTrend.kt | 11 +++++- .../hashtag/HashtagRepositoryImpl.kt | 37 ++++++++++++++++++- .../milktea/model/hashtag/Hashtag.kt | 7 ++++ .../model/hashtag/HashtagRepository.kt | 2 + 5 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/Hashtag.kt diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt index 7b014c49ff..24449dc8e1 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt @@ -1,6 +1,7 @@ package net.pantasystem.milktea.api.mastodon.tag import kotlinx.serialization.SerialName +import net.pantasystem.milktea.model.hashtag.Hashtag @kotlinx.serialization.Serializable data class MastodonTagDTO( @@ -14,4 +15,12 @@ data class MastodonTagDTO( @SerialName("uses") val uses: Int, @SerialName("accounts") val accounts: Int, ) + + fun toModel(): Hashtag { + return Hashtag( + name, + history.sumOf { it.uses }, + history.map { it.uses } + ) + } } \ No newline at end of file diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt index adf3e23f43..8ff20a2e15 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt @@ -1,10 +1,19 @@ package net.pantasystem.milktea.api.misskey.trend import kotlinx.serialization.SerialName +import net.pantasystem.milktea.model.hashtag.Hashtag @kotlinx.serialization.Serializable data class HashtagTrend( @SerialName("tag") val tag: String, @SerialName("chart") val chart: List, @SerialName("usesCount") val usesCount: Int, -) \ No newline at end of file +) { + fun toModel(): Hashtag { + return Hashtag( + tag, + usesCount, + chart + ) + } +} \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt index f49fad6f30..f0aba9588e 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt @@ -1,14 +1,17 @@ package net.pantasystem.milktea.data.infrastructure.hashtag -import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext +import net.pantasystem.milktea.api.misskey.EmptyRequest import net.pantasystem.milktea.api.misskey.hashtag.SearchHashtagRequest import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common.throwIfHasError +import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.hashtag.Hashtag import net.pantasystem.milktea.model.hashtag.HashtagRepository import javax.inject.Inject @@ -16,6 +19,7 @@ class HashtagRepositoryImpl @Inject constructor( private val accountRepository: AccountRepository, private val misskeyAPIProvider: MisskeyAPIProvider, private val mastodonAPIProvider: MastodonAPIProvider, + @IODispatcher private val ioDispatcher: CoroutineDispatcher, ) : HashtagRepository { override suspend fun search( accountId: Long, @@ -23,7 +27,7 @@ class HashtagRepositoryImpl @Inject constructor( limit: Int, offset: Int, ): Result> = runCancellableCatching { - withContext(Dispatchers.IO) { + withContext(ioDispatcher) { val account = accountRepository.get(accountId).getOrThrow() when (account.instanceType) { Account.InstanceType.MISSKEY -> { @@ -49,4 +53,33 @@ class HashtagRepositoryImpl @Inject constructor( } } + + override suspend fun trends(accountId: Long): Result> = runCancellableCatching { + withContext(ioDispatcher) { + val account = accountRepository.get(accountId).getOrThrow() + when(account.instanceType) { + Account.InstanceType.MISSKEY -> { + val body = requireNotNull( + misskeyAPIProvider.get(account).getTrendingHashtags(EmptyRequest) + .throwIfHasError() + .body() + ) + body.map { + it.toModel() + } + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + val body = requireNotNull( + mastodonAPIProvider.get(account).getTagTrends() + .throwIfHasError() + .body() + ) + body.map { + it.toModel() + } + } + } + } + + } } \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/Hashtag.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/Hashtag.kt new file mode 100644 index 0000000000..7bb3dffb6e --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/Hashtag.kt @@ -0,0 +1,7 @@ +package net.pantasystem.milktea.model.hashtag + +data class Hashtag( + val name: String, + val usesCount: Int, + val chart: List, +) \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt index a38a04f739..5dcabd4116 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt @@ -2,4 +2,6 @@ package net.pantasystem.milktea.model.hashtag interface HashtagRepository { suspend fun search(accountId: Long, query: String, limit: Int = 10, offset: Int = 0): Result> + + suspend fun trends(accountId: Long): Result> } \ No newline at end of file From d2b6a3f488dbb86ca42e5480faeab94ce2930545 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 19:40:22 +0900 Subject: [PATCH 113/432] =?UTF-8?q?feat:=20viewModel=E3=81=A8Fragment?= =?UTF-8?q?=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/trend/TrendFragment.kt | 34 +++++++++++ .../milktea/search/trend/TrendViewModel.kt | 58 +++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt create mode 100644 modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendViewModel.kt diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt new file mode 100644 index 0000000000..82d4b4c045 --- /dev/null +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt @@ -0,0 +1,34 @@ +package net.pantasystem.milktea.search.trend + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.ComposeView +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import com.google.android.material.composethemeadapter.MdcTheme +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class TrendFragment : Fragment() { + + private val viewModel by viewModels() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + return ComposeView(requireContext()).apply { + setContent { + MdcTheme { + val uiState by viewModel.uiState.collectAsState() + + } + } + } + } +} \ No newline at end of file diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendViewModel.kt new file mode 100644 index 0000000000..41014f3d78 --- /dev/null +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendViewModel.kt @@ -0,0 +1,58 @@ +package net.pantasystem.milktea.search.trend + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* +import net.pantasystem.milktea.app_store.account.AccountStore +import net.pantasystem.milktea.common.ResultState +import net.pantasystem.milktea.common.StateContent +import net.pantasystem.milktea.common.asLoadingStateFlow +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.hashtag.Hashtag +import net.pantasystem.milktea.model.hashtag.HashtagRepository +import javax.inject.Inject + +@HiltViewModel +class TrendViewModel @Inject constructor( + val accountStore: AccountStore, + val hashtagRepository: HashtagRepository, +) : ViewModel() { + private val currentAccount = accountStore.observeCurrentAccount.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + null, + ) + + @OptIn(ExperimentalCoroutinesApi::class) + private val trends = currentAccount.filterNotNull().flatMapLatest { + suspend { + hashtagRepository.trends(it.accountId).getOrThrow() + }.asLoadingStateFlow() + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + ResultState.Loading(StateContent.NotExist()) + ) + + val uiState = combine(currentAccount, trends) { ca, t -> + TrendUiState( + currentAccount = ca, + trendTags = t + ) + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + TrendUiState( + null, + ResultState.Loading(StateContent.NotExist()) + ) + ) + +} + +data class TrendUiState( + val currentAccount: Account?, + val trendTags: ResultState>, +) \ No newline at end of file From 1155f3cf87d36b68818fdaa42b1f0ad5a4dacca4 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 19:51:25 +0900 Subject: [PATCH 114/432] refactor --- .../milktea/api/mastodon/tag/MastodonTagDTO.kt | 6 +++--- .../milktea/api/misskey/trend/HashtagTrend.kt | 6 +++--- .../data/infrastructure/hashtag/HashtagRepositoryImpl.kt | 4 ++-- .../net/pantasystem/milktea/search/SearchTopFragment.kt | 2 ++ .../net/pantasystem/milktea/search/SearchTopViewModel.kt | 9 +++++++++ .../pantasystem/milktea/search/trend/TrendViewModel.kt | 4 ++-- .../milktea/model/hashtag/{Hashtag.kt => HashTag.kt} | 2 +- .../milktea/model/hashtag/HashtagRepository.kt | 2 +- 8 files changed, 23 insertions(+), 12 deletions(-) rename modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/{Hashtag.kt => HashTag.kt} (85%) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt index 24449dc8e1..1dfb09cb79 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/tag/MastodonTagDTO.kt @@ -1,7 +1,7 @@ package net.pantasystem.milktea.api.mastodon.tag import kotlinx.serialization.SerialName -import net.pantasystem.milktea.model.hashtag.Hashtag +import net.pantasystem.milktea.model.hashtag.HashTag @kotlinx.serialization.Serializable data class MastodonTagDTO( @@ -16,8 +16,8 @@ data class MastodonTagDTO( @SerialName("accounts") val accounts: Int, ) - fun toModel(): Hashtag { - return Hashtag( + fun toModel(): HashTag { + return HashTag( name, history.sumOf { it.uses }, history.map { it.uses } diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt index 8ff20a2e15..153a4b71d0 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt @@ -1,7 +1,7 @@ package net.pantasystem.milktea.api.misskey.trend import kotlinx.serialization.SerialName -import net.pantasystem.milktea.model.hashtag.Hashtag +import net.pantasystem.milktea.model.hashtag.HashTag @kotlinx.serialization.Serializable data class HashtagTrend( @@ -9,8 +9,8 @@ data class HashtagTrend( @SerialName("chart") val chart: List, @SerialName("usesCount") val usesCount: Int, ) { - fun toModel(): Hashtag { - return Hashtag( + fun toModel(): HashTag { + return HashTag( tag, usesCount, chart diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt index f0aba9588e..3e38a85fa5 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/hashtag/HashtagRepositoryImpl.kt @@ -11,7 +11,7 @@ import net.pantasystem.milktea.data.api.mastodon.MastodonAPIProvider import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository -import net.pantasystem.milktea.model.hashtag.Hashtag +import net.pantasystem.milktea.model.hashtag.HashTag import net.pantasystem.milktea.model.hashtag.HashtagRepository import javax.inject.Inject @@ -54,7 +54,7 @@ class HashtagRepositoryImpl @Inject constructor( } } - override suspend fun trends(accountId: Long): Result> = runCancellableCatching { + override suspend fun trends(accountId: Long): Result> = runCancellableCatching { withContext(ioDispatcher) { val account = accountRepository.get(accountId).getOrThrow() when(account.instanceType) { diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt index 78795a1f29..e099858976 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt @@ -29,6 +29,7 @@ import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.search.databinding.FragmentSearchTopBinding import net.pantasystem.milktea.search.explore.ExploreFragment import net.pantasystem.milktea.search.explore.ExploreType +import net.pantasystem.milktea.search.trend.TrendFragment import javax.inject.Inject @@ -120,6 +121,7 @@ class SearchPagerAdapterV2( SearchTopTabItem.TabType.MisskeyExploreFediverseUsers -> ExploreFragment.newInstance(ExploreType.Fediverse) SearchTopTabItem.TabType.MastodonUserSuggestions -> ExploreFragment.newInstance(ExploreType.MastodonUserSuggestions) SearchTopTabItem.TabType.UserSuggestionByReaction -> ExploreFragment.newInstance(ExploreType.UserSuggestionsByReaction) + SearchTopTabItem.TabType.HashtagTrend -> TrendFragment() } } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt index 49737e7473..c9a3b2963a 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt @@ -42,6 +42,10 @@ class SearchTopViewModel @Inject constructor( SearchTopTabItem( StringSource(R.string.suggestion_users), SearchTopTabItem.TabType.UserSuggestionByReaction, + ), + SearchTopTabItem( + StringSource("Trends"), + SearchTopTabItem.TabType.HashtagTrend, ) ) } @@ -54,6 +58,10 @@ class SearchTopViewModel @Inject constructor( SearchTopTabItem( StringSource(R.string.suggestion_users), SearchTopTabItem.TabType.MastodonUserSuggestions, + ), + SearchTopTabItem( + StringSource("Trends"), + SearchTopTabItem.TabType.HashtagTrend, ) ) } @@ -80,5 +88,6 @@ data class SearchTopTabItem( MisskeyExploreUsers, MisskeyExploreFediverseUsers, UserSuggestionByReaction, + HashtagTrend, } } \ No newline at end of file diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendViewModel.kt index 41014f3d78..cc5e698ebe 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendViewModel.kt @@ -10,7 +10,7 @@ import net.pantasystem.milktea.common.ResultState import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common.asLoadingStateFlow import net.pantasystem.milktea.model.account.Account -import net.pantasystem.milktea.model.hashtag.Hashtag +import net.pantasystem.milktea.model.hashtag.HashTag import net.pantasystem.milktea.model.hashtag.HashtagRepository import javax.inject.Inject @@ -54,5 +54,5 @@ class TrendViewModel @Inject constructor( data class TrendUiState( val currentAccount: Account?, - val trendTags: ResultState>, + val trendTags: ResultState>, ) \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/Hashtag.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt similarity index 85% rename from modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/Hashtag.kt rename to modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt index 7bb3dffb6e..1eb9592a34 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/Hashtag.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt @@ -1,6 +1,6 @@ package net.pantasystem.milktea.model.hashtag -data class Hashtag( +data class HashTag( val name: String, val usesCount: Int, val chart: List, diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt index 5dcabd4116..f6969d3b9f 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashtagRepository.kt @@ -3,5 +3,5 @@ package net.pantasystem.milktea.model.hashtag interface HashtagRepository { suspend fun search(accountId: Long, query: String, limit: Int = 10, offset: Int = 0): Result> - suspend fun trends(accountId: Long): Result> + suspend fun trends(accountId: Long): Result> } \ No newline at end of file From 141bedfef98bdd441d055531b5b230f850a2f838 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 20:19:11 +0900 Subject: [PATCH 115/432] =?UTF-8?q?feat:=20=E3=83=88=E3=83=AC=E3=83=B3?= =?UTF-8?q?=E3=83=89=E3=82=92=E4=B8=80=E8=A6=A7=E3=81=A7=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api/misskey/trend/HashtagTrend.kt | 4 +- .../milktea/search/trend/TrendFragment.kt | 84 ++++++++++++++++++- .../milktea/model/hashtag/HashTag.kt | 2 +- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt index 153a4b71d0..ab2cfd88cf 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/trend/HashtagTrend.kt @@ -7,12 +7,12 @@ import net.pantasystem.milktea.model.hashtag.HashTag data class HashtagTrend( @SerialName("tag") val tag: String, @SerialName("chart") val chart: List, - @SerialName("usesCount") val usesCount: Int, + @SerialName("usersCount") val usersCount: Int, ) { fun toModel(): HashTag { return HashTag( tag, - usesCount, + usersCount, chart ) } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt index 82d4b4c045..d24c976999 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt @@ -4,29 +4,109 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.Surface +import androidx.compose.material.Text import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.rememberNestedScrollInteropConnection +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import com.google.android.material.composethemeadapter.MdcTheme import dagger.hilt.android.AndroidEntryPoint +import net.pantasystem.milktea.common.ResultState +import net.pantasystem.milktea.common.StateContent +import net.pantasystem.milktea.common_navigation.SearchNavType +import net.pantasystem.milktea.common_navigation.SearchNavigation +import javax.inject.Inject @AndroidEntryPoint class TrendFragment : Fragment() { + @Inject + lateinit var searchNavigation: SearchNavigation + private val viewModel by viewModels() + @OptIn(ExperimentalComposeUiApi::class) override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View { return ComposeView(requireContext()).apply { setContent { MdcTheme { val uiState by viewModel.uiState.collectAsState() - + LazyColumn( + modifier = Modifier + .fillMaxSize() + .nestedScroll(rememberNestedScrollInteropConnection()) + ) { + when (val content = uiState.trendTags.content) { + is StateContent.Exist -> { + items(content.rawContent.size) { index -> + val item = content.rawContent[index] + Surface( + Modifier + .fillMaxWidth() + .clickable { + requireActivity().startActivity( + searchNavigation.newIntent( + SearchNavType.ResultScreen( + "#${item.name}" + ) + ) + ) + } + ) { + Column( + Modifier + .fillMaxWidth() + .padding( + vertical = 12.dp, + horizontal = 14.dp + ) + ) { + Text( + "#${item.name}", + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + Text("${item.usersCount}人が投稿") + } + } + } + } + is StateContent.NotExist -> { + item { + Box( + Modifier + .fillMaxWidth() + .padding(vertical = 12.dp, horizontal = 14.dp), + contentAlignment = Alignment.Center + ) { + when (val state = uiState.trendTags) { + is ResultState.Error -> Text("Error:${state.throwable}") + is ResultState.Fixed -> Text("トレンドはありません") + is ResultState.Loading -> CircularProgressIndicator() + } + } + } + } + } + } } } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt index 1eb9592a34..91421ad772 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/hashtag/HashTag.kt @@ -2,6 +2,6 @@ package net.pantasystem.milktea.model.hashtag data class HashTag( val name: String, - val usesCount: Int, + val usersCount: Int, val chart: List, ) \ No newline at end of file From 880be0fcb7aa37233a832fb0730bd7ab1724ce22 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 20:21:34 +0900 Subject: [PATCH 116/432] refactor --- .../milktea/search/trend/HashtagTrendItem.kt | 45 +++++++++++++++++++ .../milktea/search/trend/TrendFragment.kt | 38 +++------------- 2 files changed, 52 insertions(+), 31 deletions(-) create mode 100644 modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/HashtagTrendItem.kt diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/HashtagTrendItem.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/HashtagTrendItem.kt new file mode 100644 index 0000000000..70b292c113 --- /dev/null +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/HashtagTrendItem.kt @@ -0,0 +1,45 @@ +package net.pantasystem.milktea.search.trend + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import net.pantasystem.milktea.model.hashtag.HashTag + +@Composable +fun HashtagTrendItem( + hashtag: HashTag, + onClick: () -> Unit, +) { + + Surface( + Modifier + .fillMaxWidth() + .clickable { + onClick() + } + ) { + Column( + Modifier + .fillMaxWidth() + .padding( + vertical = 12.dp, + horizontal = 14.dp + ) + ) { + Text( + "#${hashtag.name}", + fontSize = 18.sp, + fontWeight = FontWeight.Bold + ) + Text("${hashtag.usersCount}人が投稿") + } + } +} \ No newline at end of file diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt index d24c976999..1211d326e1 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/TrendFragment.kt @@ -4,11 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.CircularProgressIndicator -import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -18,9 +16,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.rememberNestedScrollInteropConnection -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import com.google.android.material.composethemeadapter.MdcTheme @@ -58,35 +54,15 @@ class TrendFragment : Fragment() { is StateContent.Exist -> { items(content.rawContent.size) { index -> val item = content.rawContent[index] - Surface( - Modifier - .fillMaxWidth() - .clickable { - requireActivity().startActivity( - searchNavigation.newIntent( - SearchNavType.ResultScreen( - "#${item.name}" - ) - ) - ) - } - ) { - Column( - Modifier - .fillMaxWidth() - .padding( - vertical = 12.dp, - horizontal = 14.dp + HashtagTrendItem(hashtag = item, onClick = { + requireActivity().startActivity( + searchNavigation.newIntent( + SearchNavType.ResultScreen( + "#${item.name}" ) - ) { - Text( - "#${item.name}", - fontSize = 18.sp, - fontWeight = FontWeight.Bold ) - Text("${item.usersCount}人が投稿") - } - } + ) + }) } } is StateContent.NotExist -> { From 55f3958ae77aa9699ba81c0fc55442dc34b91a68 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 20:38:28 +0900 Subject: [PATCH 117/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 2 ++ 3 files changed, 4 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 66206aeae9..b2c88ccf05 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -593,6 +593,7 @@ ノートコンテンツ文字サイズ(%fsp) ノートヘッダー文字サイズ(%fsp) おすすめユーザ + %d人が投稿 diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index a0f85fdcd7..ecfb9efcea 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -587,6 +587,7 @@ Note content font size(%fsp) Note header font size(%fsp) Suggestions + %d people posting \ No newline at end of file diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 67d576df19..ec8925d1c0 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -591,4 +591,6 @@ Suggestions + + %d people posting From 402d29103931ec78a67788a1645d90e045b514ba Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 8 May 2023 20:41:10 +0900 Subject: [PATCH 118/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/search/SearchTopViewModel.kt | 4 ++-- .../net/pantasystem/milktea/search/trend/HashtagTrendItem.kt | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt index c9a3b2963a..7c5391d741 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopViewModel.kt @@ -44,7 +44,7 @@ class SearchTopViewModel @Inject constructor( SearchTopTabItem.TabType.UserSuggestionByReaction, ), SearchTopTabItem( - StringSource("Trends"), + StringSource(R.string.trending_tag), SearchTopTabItem.TabType.HashtagTrend, ) ) @@ -60,7 +60,7 @@ class SearchTopViewModel @Inject constructor( SearchTopTabItem.TabType.MastodonUserSuggestions, ), SearchTopTabItem( - StringSource("Trends"), + StringSource(R.string.trending_tag), SearchTopTabItem.TabType.HashtagTrend, ) ) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/HashtagTrendItem.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/HashtagTrendItem.kt index 70b292c113..8b3e987f0d 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/HashtagTrendItem.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/trend/HashtagTrendItem.kt @@ -8,10 +8,12 @@ import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import net.pantasystem.milktea.model.hashtag.HashTag +import net.pantasystem.milktea.search.R @Composable fun HashtagTrendItem( @@ -39,7 +41,7 @@ fun HashtagTrendItem( fontSize = 18.sp, fontWeight = FontWeight.Bold ) - Text("${hashtag.usersCount}人が投稿") + Text(stringResource(id = R.string.trend_posted_person_count_msg, hashtag.usersCount)) } } } \ No newline at end of file From 31512fc4b487947bafe3324367a69f458532dce9 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 10 May 2023 17:53:53 +0900 Subject: [PATCH 119/432] =?UTF-8?q?feat:=20scrollable=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/search/src/main/res/layout/fragment_search_top.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/features/search/src/main/res/layout/fragment_search_top.xml b/modules/features/search/src/main/res/layout/fragment_search_top.xml index 416571cea8..853e79a0f4 100644 --- a/modules/features/search/src/main/res/layout/fragment_search_top.xml +++ b/modules/features/search/src/main/res/layout/fragment_search_top.xml @@ -35,6 +35,8 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" + app:tabMode="scrollable" + app:tabGravity="fill" > From 5758322e6b4d3425e4b16142043add096436cd80 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 10 May 2023 17:54:04 +0900 Subject: [PATCH 120/432] =?UTF-8?q?feat:=20=E6=96=87=E8=A8=80=E3=82=92?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index b2c88ccf05..1f45a8670b 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -267,7 +267,7 @@ 成功しました 失敗しました - Explore Fediverse + Fediverse 見つける 人気ユーザー 最近投稿したユーザー From 1ff6d565362cabf4cb42e77401ecd4bd7a67d3e0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 10 May 2023 17:56:19 +0900 Subject: [PATCH 121/432] =?UTF-8?q?feat:=20=E6=96=87=E8=A8=80=E3=82=92?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index ec8925d1c0..4c334c7cb9 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -266,7 +266,7 @@ Success Failure - Explore the Fediverse + Fediverse Explore Trending users Users with recent activity From 15e90c81c3fbddfee185c7040f25b5ed71c67f03 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 10:26:41 +0900 Subject: [PATCH 122/432] refactor --- .../milktea/note/editor/NoteEditorFragment.kt | 157 ++++++++---------- 1 file changed, 71 insertions(+), 86 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index c023457001..1bf31cbe00 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -161,12 +161,10 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti private val accountId: Long? by lazy(LazyThreadSafetyMode.NONE) { if (requireArguments().getLong( - EXTRA_ACCOUNT_ID, - -1 + EXTRA_ACCOUNT_ID, -1 ) == -1L ) null else requireArguments().getLong( - EXTRA_ACCOUNT_ID, - -1 + EXTRA_ACCOUNT_ID, -1 ) } private val replyToNoteId by lazy(LazyThreadSafetyMode.NONE) { @@ -247,8 +245,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti accountViewModel.switchAccountEvent.onEach { AccountSwitchingDialog().show(childFragmentManager, "tag") }.flowWithLifecycle( - viewLifecycleOwner.lifecycle, - Lifecycle.State.RESUMED + viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED ).launchIn(viewLifecycleOwner.lifecycleScope) accountViewModel.showProfileEvent.onEach { @@ -260,8 +257,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti intent.putActivity(Activities.ACTIVITY_IN_APP) startActivity(intent) }.flowWithLifecycle( - viewLifecycleOwner.lifecycle, - Lifecycle.State.RESUMED + viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED ).launchIn(viewLifecycleOwner.lifecycleScope) @@ -272,16 +268,14 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti }.distinctUntilChanged().onEach { emojis -> binding.inputMain.setAdapter( CustomEmojiCompleteAdapter( - emojis, - requireContext() + emojis, requireContext() ) ) binding.inputMain.setTokenizer(CustomEmojiTokenizer()) binding.cw.setAdapter( CustomEmojiCompleteAdapter( - emojis, - requireContext() + emojis, requireContext() ) ) binding.cw.setTokenizer(CustomEmojiTokenizer()) @@ -302,26 +296,21 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti binding.filePreview.apply { setContent { MdcTheme { - NoteFilePreview( - noteEditorViewModel = noteEditorViewModel, - onShow = { - val intent = mediaNavigation.newIntent( - MediaNavigationArgs.AFile( - it - ) + NoteFilePreview(noteEditorViewModel = noteEditorViewModel, onShow = { + val intent = mediaNavigation.newIntent( + MediaNavigationArgs.AFile( + it ) + ) - requireActivity().startActivity(intent) - }, - onEditFileCaptionSelectionClicked = { - EditFileCaptionDialog.newInstance(it.file, it.comment ?: "") - .show(childFragmentManager, "editCaption") - }, - onEditFileNameSelectionClicked = { - EditFileNameDialog.newInstance(it.file, it.name) - .show(childFragmentManager, "editFileName") - } - ) + requireActivity().startActivity(intent) + }, onEditFileCaptionSelectionClicked = { + EditFileCaptionDialog.newInstance(it.file, it.comment ?: "") + .show(childFragmentManager, "editCaption") + }, onEditFileNameSelectionClicked = { + EditFileNameDialog.newInstance(it.file, it.name) + .show(childFragmentManager, "editFileName") + }) } } @@ -330,8 +319,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti MdcTheme { val state by noteEditorViewModel.enableFeatures.collectAsState() val uiState by noteEditorViewModel.uiState.collectAsState() - NoteEditorUserActionMenuLayout( - iconColor = getColor(color = R.attr.normalIconTint), + NoteEditorUserActionMenuLayout(iconColor = getColor(color = R.attr.normalIconTint), isEnableDrive = state.contains(FeatureType.Drive), isCw = uiState.formState.hasCw, isPoll = uiState.poll != null, @@ -355,8 +343,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti }, onToggleCwButtonClicked = { noteEditorViewModel.changeCwEnabled() - } - ) + }) } } viewLifecycleOwner.lifecycleScope.launch { @@ -451,15 +438,13 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti } } }.flowWithLifecycle( - viewLifecycleOwner.lifecycle, - Lifecycle.State.RESUMED + viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED ).launchIn(viewLifecycleOwner.lifecycleScope) confirmViewModel.confirmEvent.onEach { ConfirmDialog.newInstance(it).show(childFragmentManager, "confirm") }.flowWithLifecycle( - viewLifecycleOwner.lifecycle, - Lifecycle.State.RESUMED + viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED ).launchIn(viewLifecycleOwner.lifecycleScope) @@ -490,9 +475,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti noteEditorViewModel.fileSizeInvalidEvent.collect { whenStarted { NoteEditorFileSizeWarningDialog.newInstance( - it.account.getHost(), - it.instanceInfo.clientMaxBodyByteSize ?: 0, - it.file + it.account.getHost(), it.instanceInfo.clientMaxBodyByteSize ?: 0, it.file ).show(childFragmentManager, "fileSizeInvalidDialog") } } @@ -679,8 +662,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti val upIntent = mainNavigation.newIntent(Unit) upIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP) if (requireActivity().shouldUpRecreateTask(upIntent)) { - TaskStackBuilder.create(requireActivity()) - .addNextIntentWithParentStack(upIntent) + TaskStackBuilder.create(requireActivity()).addNextIntentWithParentStack(upIntent) .startActivities() requireActivity().finish() } else { @@ -690,56 +672,57 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti } @Suppress("DEPRECATION") - private val openDriveActivityResult = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - val ids = - (result?.data?.getSerializableExtra(EXTRA_SELECTED_FILE_PROPERTY_IDS) as List<*>?)?.mapNotNull { - it as? FileProperty.Id - } - logger.debug("result:${ids}") - val size = noteEditorViewModel.fileTotal() - - if (ids != null && ids.isNotEmpty() && size + ids.size <= noteEditorViewModel.maxFileCount.value) { - noteEditorViewModel.addFilePropertyFromIds(ids) + private val openDriveActivityResult = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + val ids = + (result?.data?.getSerializableExtra(EXTRA_SELECTED_FILE_PROPERTY_IDS) as List<*>?)?.mapNotNull { + it as? FileProperty.Id } - } + logger.debug("result:${ids}") + val size = noteEditorViewModel.fileTotal() - private val openLocalStorageResult = registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris -> - uris?.map { uri -> - appendFile(uri) + if (ids != null && ids.isNotEmpty() && size + ids.size <= noteEditorViewModel.maxFileCount.value) { + noteEditorViewModel.addFilePropertyFromIds(ids) } } - - private val requestReadStoragePermissionResult = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { - if (it) { - showFileManager() - } else { - Toast.makeText( - requireContext(), - "ストレージへのアクセスを許可しないとファイルを読み込めないぽよ", - Toast.LENGTH_LONG - ).show() + private val openLocalStorageResult = + registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris -> + uris?.map { uri -> + appendFile(uri) } } - private val requestReadMediasPermissionResult = - registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { results -> - if (results.any { it.value }) { - showFileManager() - } else { - Toast.makeText( - requireContext(), - "ストレージへのアクセスを許可しないとファイルを読み込めないぽよ", - Toast.LENGTH_LONG - ).show() - } + + private val requestReadStoragePermissionResult = registerForActivityResult( + ActivityResultContracts.RequestPermission() + ) { + if (it) { + showFileManager() + } else { + Toast.makeText( + requireContext(), "ストレージへのアクセスを許可しないとファイルを読み込めないぽよ", Toast.LENGTH_LONG + ).show() } + } + + private val requestReadMediasPermissionResult = registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { results -> + if (results.any { it.value }) { + showFileManager() + } else { + Toast.makeText( + requireContext(), "ストレージへのアクセスを許可しないとファイルを読み込めないぽよ", Toast.LENGTH_LONG + ).show() + } + } @Suppress("DEPRECATION") - private val selectUserResult = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + private val selectUserResult = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) { val changed = result.data?.getSerializableExtra(SearchAndSelectUserNavigation.EXTRA_SELECTED_USER_CHANGED_DIFF) as? ChangedDiffResult @@ -751,8 +734,9 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti @Suppress("DEPRECATION") - private val selectMentionToUserResult = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + private val selectMentionToUserResult = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) { val changed = result.data?.getSerializableExtra(SearchAndSelectUserNavigation.EXTRA_SELECTED_USER_CHANGED_DIFF) as? ChangedDiffResult @@ -765,8 +749,9 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti } - private val pickMultipleMedia = - registerForActivityResult(ActivityResultContracts.PickMultipleVisualMedia()) { uris -> + private val pickMultipleMedia = registerForActivityResult( + ActivityResultContracts.PickMultipleVisualMedia() + ) { uris -> uris?.map { appendFile(it) } From f9680ddb25391c09c7acbd6d5359e4754d66505f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 10:57:04 +0900 Subject: [PATCH 123/432] =?UTF-8?q?feat:=20=E3=81=A9=E3=81=93=E3=81=ABfocu?= =?UTF-8?q?s=E3=81=8C=E5=BD=93=E3=81=9F=E3=81=A3=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=82=8B=E3=81=8B=E7=8A=B6=E6=85=8B=E3=82=92=E5=A4=89=E6=95=B0?= =?UTF-8?q?=E3=81=A7=E4=BF=9D=E6=8C=81=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/viewmodel/NoteEditorViewModel.kt | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt index a94a2b2357..9040ec93f7 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt @@ -321,6 +321,8 @@ class NoteEditorViewModel @Inject constructor( val isSaveNoteAsDraft = EventBus() + var focusType: NoteEditorFocusEditTextType = NoteEditorFocusEditTextType.Text + init { accountStore.observeCurrentAccount.filterNotNull().map { it to noteEditorSwitchAccountExecutor( @@ -629,6 +631,7 @@ class NoteEditorViewModel @Inject constructor( fun setCw(text: String?) { savedStateHandle.setCw(text) + focusType = NoteEditorFocusEditTextType.Text } fun setVisibility(visibility: Visibility) { @@ -682,11 +685,24 @@ class NoteEditorViewModel @Inject constructor( } fun addEmoji(emoji: String, pos: Int): Int { - val builder = StringBuilder(savedStateHandle.getText() ?: "") - builder.insert(pos, emoji) - savedStateHandle.setText(builder.toString()) - logger.debug("position:${pos + emoji.length - 1}") - return pos + emoji.length + when(focusType) { + NoteEditorFocusEditTextType.Cw -> { + val builder = StringBuilder(savedStateHandle.getCw() ?: "") + logger.debug("pos:$pos") + builder.insert(pos, emoji) + savedStateHandle.setCw(builder.toString()) + logger.debug("position:${pos + emoji.length - 1}") + return pos + emoji.length + } + NoteEditorFocusEditTextType.Text -> { + val builder = StringBuilder(savedStateHandle.getText() ?: "") + builder.insert(pos, emoji) + savedStateHandle.setText(builder.toString()) + logger.debug("position:${pos + emoji.length - 1}") + return pos + emoji.length + } + } + } fun setSchedulePostAt(instant: Instant?) { @@ -735,4 +751,8 @@ data class FileSizeInvalidEvent( val file: AppFile.Local, val instanceInfo: InstanceInfo, val account: Account -) \ No newline at end of file +) + +enum class NoteEditorFocusEditTextType { + Cw, Text +} \ No newline at end of file From f9bdcee3a65452750e8b8346438a05928790be9f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 10:57:19 +0900 Subject: [PATCH 124/432] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A9=E3=83=BC?= =?UTF-8?q?=E3=82=AB=E3=82=B9=E3=81=8C=E5=BD=93=E3=81=9F=E3=81=A3=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=82=8B=E3=82=A8=E3=83=87=E3=82=A3=E3=82=BF=E3=83=BC?= =?UTF-8?q?=E3=81=AB=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=81=8C=E5=85=A5=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/editor/NoteEditorFragment.kt | 54 +++++++++++++++---- 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index 1bf31cbe00..a476da385b 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -58,6 +58,7 @@ import net.pantasystem.milktea.note.databinding.FragmentNoteEditorBinding import net.pantasystem.milktea.note.databinding.ViewNoteEditorToolbarBinding import net.pantasystem.milktea.note.editor.file.EditFileCaptionDialog import net.pantasystem.milktea.note.editor.file.EditFileNameDialog +import net.pantasystem.milktea.note.editor.viewmodel.NoteEditorFocusEditTextType import net.pantasystem.milktea.note.editor.viewmodel.NoteEditorViewModel import net.pantasystem.milktea.note.emojis.CustomEmojiPickerDialog import net.pantasystem.milktea.note.emojis.viewmodel.EmojiSelection @@ -339,6 +340,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti startMentionToSearchAndSelectUser() }, onSelectEmojiButtonClicked = { + binding.cw.isFocused CustomEmojiPickerDialog().show(childFragmentManager, "Editor") }, onToggleCwButtonClicked = { @@ -413,6 +415,17 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti ReservationPostTimePickerDialog().show(childFragmentManager, "Pick time") } + binding.cw.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + noteEditorViewModel.focusType = NoteEditorFocusEditTextType.Cw + } + } + + binding.inputMain.setOnFocusChangeListener { _, hasFocus -> + if (hasFocus) { + noteEditorViewModel.focusType = NoteEditorFocusEditTextType.Text + } + } viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -492,20 +505,43 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti override fun onSelect(emoji: Emoji) { - val pos = binding.inputMain.selectionEnd - noteEditorViewModel.addEmoji(emoji, pos).let { newPos -> - binding.inputMain.setText(noteEditorViewModel.text.value ?: "") - binding.inputMain.setSelection(newPos) - logger.debug("入力されたデータ:${binding.inputMain.text}") + when(noteEditorViewModel.focusType) { + NoteEditorFocusEditTextType.Cw -> { + val pos = binding.cw.selectionEnd + noteEditorViewModel.addEmoji(emoji, pos).let { newPos -> + binding.cw.setText(noteEditorViewModel.cw.value ?: "") + binding.cw.setSelection(newPos) + } + } + NoteEditorFocusEditTextType.Text -> { + val pos = binding.inputMain.selectionEnd + noteEditorViewModel.addEmoji(emoji, pos).let { newPos -> + binding.inputMain.setText(noteEditorViewModel.text.value ?: "") + binding.inputMain.setSelection(newPos) + } + } } + } override fun onSelect(emoji: String) { - val pos = binding.inputMain.selectionEnd - noteEditorViewModel.addEmoji(emoji, pos).let { newPos -> - binding.inputMain.setText(noteEditorViewModel.text.value ?: "") - binding.inputMain.setSelection(newPos) + when(noteEditorViewModel.focusType) { + NoteEditorFocusEditTextType.Cw -> { + val pos = binding.cw.selectionEnd + noteEditorViewModel.addEmoji(emoji, pos).let { newPos -> + binding.cw.setText(noteEditorViewModel.cw.value ?: "") + binding.cw.setSelection(newPos) + } + } + NoteEditorFocusEditTextType.Text -> { + val pos = binding.inputMain.selectionEnd + noteEditorViewModel.addEmoji(emoji, pos).let { newPos -> + binding.inputMain.setText(noteEditorViewModel.text.value ?: "") + binding.inputMain.setSelection(newPos) + } + } } + } override fun onAttach(context: Context) { From f38f3001140e7ec543f121d3248e9c4e2612e84c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 12:03:58 +0900 Subject: [PATCH 125/432] =?UTF-8?q?feat:=20Url=E3=81=AB=E3=83=9E=E3=83=83?= =?UTF-8?q?=E3=83=81=E3=81=99=E3=82=8B=E3=81=8B=E3=82=92=E3=83=81=E3=82=A7?= =?UTF-8?q?=E3=83=83=E3=82=AF=E3=81=99=E3=82=8BUtil=E3=82=92=E4=BD=9C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/common/text/UrlPatternChecker.kt | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 modules/common/src/main/java/net/pantasystem/milktea/common/text/UrlPatternChecker.kt diff --git a/modules/common/src/main/java/net/pantasystem/milktea/common/text/UrlPatternChecker.kt b/modules/common/src/main/java/net/pantasystem/milktea/common/text/UrlPatternChecker.kt new file mode 100644 index 0000000000..c62c43adf8 --- /dev/null +++ b/modules/common/src/main/java/net/pantasystem/milktea/common/text/UrlPatternChecker.kt @@ -0,0 +1,9 @@ +package net.pantasystem.milktea.common.text + +object UrlPatternChecker { + private val urlPattern = Regex("""(https?)(://)([-_.!~*'()\[\]a-zA-Z0-9;/?:@&=+${'$'},%#]+)""") + fun isMatch(text: String): Boolean { + return urlPattern.matches(text) + } + +} \ No newline at end of file From 946cc896fc2df6b58b2771d44753909c2754ef9c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 12:05:33 +0900 Subject: [PATCH 126/432] =?UTF-8?q?feat:=20URL=E3=82=92=E8=B2=BC=E3=82=8A?= =?UTF-8?q?=E4=BB=98=E3=81=91=E3=82=89=E3=82=8C=E3=81=9F=E6=99=82=E3=81=AB?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E3=81=A8=E3=81=97=E3=81=A6=E8=B2=BC=E3=82=8A?= =?UTF-8?q?=E4=BB=98=E3=81=91=E3=82=89=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/viewmodel/NoteEditorViewModel.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt index 9040ec93f7..2addfc409f 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt @@ -11,11 +11,14 @@ import kotlinx.datetime.Clock import kotlinx.datetime.Instant import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.* +import net.pantasystem.milktea.common.text.UrlPatternChecker import net.pantasystem.milktea.common_android.eventbus.EventBus import net.pantasystem.milktea.common_viewmodel.UserViewData import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.UnauthorizedException +import net.pantasystem.milktea.model.ap.ApResolver +import net.pantasystem.milktea.model.ap.ApResolverRepository import net.pantasystem.milktea.model.channel.Channel import net.pantasystem.milktea.model.channel.ChannelRepository import net.pantasystem.milktea.model.drive.DriveFileRepository @@ -65,6 +68,7 @@ class NoteEditorViewModel @Inject constructor( private val noteRelationGetter: NoteRelationGetter, private val instanceInfoRepository: InstanceInfoRepository, private val updateSensitiveUseCase: UpdateAppFileSensitiveUseCase, + private val apResolverRepository: ApResolverRepository, private val savedStateHandle: SavedStateHandle, ) : ViewModel() { @@ -730,6 +734,22 @@ class NoteEditorViewModel @Inject constructor( } } + fun onPastePostUrl(text: String, start: Int, beforeText: String, count: Int) = viewModelScope.launch { + val urlText = text.substring(start, start + count) + val ca = currentAccount.value ?: return@launch + if (UrlPatternChecker.isMatch(urlText)) { + apResolverRepository.resolve(ca.accountId, urlText).onSuccess { + when(it) { + is ApResolver.TypeNote -> { + setRenoteTo(it.note.id) + setText(beforeText) + } + is ApResolver.TypeUser -> return@launch + } + } + } + } + fun canSaveDraft(): Boolean { return uiState.value.shouldDiscardingConfirmation() } From 3599ec2d71a6977151bb83a4236a77b1e341a8b2 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 12:10:34 +0900 Subject: [PATCH 127/432] =?UTF-8?q?feat:=20URL=E3=82=92=E8=B2=BC=E3=82=8A?= =?UTF-8?q?=E4=BB=98=E3=81=91=E3=82=89=E3=82=8C=E3=81=9F=E6=99=82=E3=81=AB?= =?UTF-8?q?=E3=80=81=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=A6=E3=80=81=E3=80=8C=E3=81=AF?= =?UTF-8?q?=E3=81=84=E3=80=8D=E3=82=92=E6=8E=A8=E3=81=97=E3=81=9F=E3=82=89?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E3=81=A8=E3=81=97=E3=81=A6=E6=B7=BB=E4=BB=98?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/editor/NoteEditorFragment.kt | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index a476da385b..00429260f7 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -25,6 +25,7 @@ import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.lifecycle.* import com.google.android.material.composethemeadapter.MdcTheme +import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.wada811.databinding.dataBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -33,6 +34,7 @@ import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.app_store.setting.SettingStore import net.pantasystem.milktea.common.Logger +import net.pantasystem.milktea.common.text.UrlPatternChecker import net.pantasystem.milktea.common_android.platform.PermissionUtil import net.pantasystem.milktea.common_android.ui.Activities import net.pantasystem.milktea.common_android.ui.listview.applyFlexBoxLayout @@ -366,8 +368,25 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti noteEditorViewModel.setCw(e?.toString()) } - binding.inputMain.addTextChangedListener { e -> - logger.debug("text changed:$e") + binding.inputMain.addTextChangedListener( + onTextChanged = { text, start, _, count -> + val inputText = text?.substring(start, start + count)?: return@addTextChangedListener + if (UrlPatternChecker.isMatch(inputText)) { + MaterialAlertDialogBuilder(requireContext()) + .setMessage("引用として添付しますか?") + .setPositiveButton("はい") { _, _ -> + noteEditorViewModel.onPastePostUrl( + text.toString(), + start, + text.removeRange(start, start + count).toString(), + count, + ) + } + .setNegativeButton("いいえ") { _, _ -> } + .show() + } + } + ) { e -> noteEditorViewModel.setText((e?.toString() ?: "")) } From 697c49192f63a4f515c99449ff0cd80d0f85588f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 12:15:31 +0900 Subject: [PATCH 128/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 1f45a8670b..3b05a2b160 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -594,6 +594,7 @@ ノートヘッダー文字サイズ(%fsp) おすすめユーザ %d人が投稿 + 引用として添付しますか? diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index ecfb9efcea..0fd426aba1 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -588,6 +588,7 @@ Note header font size(%fsp) Suggestions %d people posting + Attach as a quote post? \ No newline at end of file diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 4c334c7cb9..da273eab79 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -593,4 +593,5 @@ %d people posting + Attach as a quote post? From ac3379b9c49bc8985b6bd757c139fee2ac7b9407 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 12:15:40 +0900 Subject: [PATCH 129/432] =?UTF-8?q?feat:=20=E3=83=AD=E3=83=BC=E3=82=AB?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=82=BC=E3=83=BC=E3=82=B7=E3=83=A7=E3=83=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/note/editor/NoteEditorFragment.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index 00429260f7..921292359c 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -373,8 +373,8 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti val inputText = text?.substring(start, start + count)?: return@addTextChangedListener if (UrlPatternChecker.isMatch(inputText)) { MaterialAlertDialogBuilder(requireContext()) - .setMessage("引用として添付しますか?") - .setPositiveButton("はい") { _, _ -> + .setMessage(R.string.notes_confirm_attach_quote_note_by_url) + .setPositiveButton(android.R.string.ok) { _, _ -> noteEditorViewModel.onPastePostUrl( text.toString(), start, @@ -382,7 +382,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti count, ) } - .setNegativeButton("いいえ") { _, _ -> } + .setNegativeButton(android.R.string.cancel) { _, _ -> } .show() } } From 2d8f03de3544713191b4a8a48078faeccb3fc3fb Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 13:09:54 +0900 Subject: [PATCH 130/432] =?UTF-8?q?feat:=20=E3=82=AD=E3=83=BC=E3=83=AF?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=81=8CURL=E3=81=A0=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E5=A0=B4=E5=90=88ap=20resolver=E3=81=AB=E5=95=8F=E3=81=84?= =?UTF-8?q?=E5=90=88=E3=82=8F=E3=81=9B=E3=81=AB=E8=A1=8C=E3=81=8F=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/SearchViewModel.kt | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt index 7093c101a2..33944cb6af 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt @@ -9,8 +9,11 @@ import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import net.pantasystem.milktea.common.* +import net.pantasystem.milktea.common.text.UrlPatternChecker import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.CurrentAccountWatcher +import net.pantasystem.milktea.model.ap.ApResolver +import net.pantasystem.milktea.model.ap.ApResolverRepository import net.pantasystem.milktea.model.hashtag.HashtagRepository import net.pantasystem.milktea.model.search.SearchHistory import net.pantasystem.milktea.model.search.SearchHistoryRepository @@ -28,6 +31,7 @@ class SearchViewModel @Inject constructor( private val userDataSource: UserDataSource, private val hashtagRepository: HashtagRepository, private val searchHistoryRepository: SearchHistoryRepository, + private val apResolverRepository: ApResolverRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { @@ -144,20 +148,33 @@ class SearchViewModel @Inject constructor( savedStateHandle["keyword"] = word } - suspend fun onQueryTextSubmit(word: String) { + suspend fun onQueryTextSubmit(word: String): SubmitResult { if (word.isBlank()) { - return + return SubmitResult.Cancelled } + val accountId = currentAccountWatcher.getAccount().accountId runCancellableCatching { searchHistoryRepository.add( SearchHistory( - accountId = currentAccountWatcher.getAccount().accountId, + accountId = accountId, keyword = word, ) ).getOrThrow() }.onFailure { logger.error("検索履歴の保存に失敗", it) } + if (!UrlPatternChecker.isMatch(word)) { + return SubmitResult.Search(word) + } + + return apResolverRepository.resolve(accountId, word).fold( + onSuccess = { + SubmitResult.ApResolved(it) + }, + onFailure = { + SubmitResult.Search(word) + } + ) } fun deleteSearchHistory(id: Long) = viewModelScope.launch { @@ -182,6 +199,13 @@ private data class States( val hashtagsState: ResultState>, ) +sealed interface SubmitResult { + data class Search(val query: String) : SubmitResult + + data class ApResolved(val apResolve: ApResolver) : SubmitResult + + object Cancelled : SubmitResult +} private fun String.isHashTagFormat(): Boolean { return startsWith("#") && length > 1 } \ No newline at end of file From fd34a3dd012775f7128cb06ba54c6db87179ecb0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 13:10:06 +0900 Subject: [PATCH 131/432] =?UTF-8?q?feat:=20=E7=B5=90=E6=9E=9C=E3=81=AB?= =?UTF-8?q?=E5=BF=9C=E3=81=98=E3=81=A6=E9=81=B7=E7=A7=BB=E5=85=88=E3=81=AE?= =?UTF-8?q?=E7=94=BB=E9=9D=A2=E3=82=92=E5=A4=89=E6=9B=B4=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/SearchActivity.kt | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchActivity.kt index a45f5e4fa6..c7a7d353e5 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchActivity.kt @@ -17,7 +17,11 @@ import com.wada811.databinding.dataBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch import net.pantasystem.milktea.common.ui.ApplyTheme +import net.pantasystem.milktea.common_navigation.UserDetailNavigation +import net.pantasystem.milktea.common_navigation.UserDetailNavigationArgs +import net.pantasystem.milktea.model.ap.ApResolver import net.pantasystem.milktea.model.user.User +import net.pantasystem.milktea.note.NoteDetailActivity import net.pantasystem.milktea.search.databinding.ActivitySearchBinding import net.pantasystem.milktea.user.activity.UserDetailActivity import javax.inject.Inject @@ -33,6 +37,9 @@ class SearchActivity : AppCompatActivity() { @Inject internal lateinit var applyTheme: ApplyTheme + @Inject + internal lateinit var userDetailNavigation: UserDetailNavigation + private var mSearchView: SearchView? = null @@ -119,12 +126,42 @@ class SearchActivity : AppCompatActivity() { fun showSearchResult(searchWord: String) { lifecycleScope.launch { - val intent = Intent(this@SearchActivity, SearchResultActivity::class.java) - intent.putExtra(SearchResultActivity.EXTRA_SEARCH_WORLD, searchWord) - searchViewModel.onQueryTextSubmit(searchWord) - startActivity(intent) - overridePendingTransition(0, 0) - finish() + + when (val result = searchViewModel.onQueryTextSubmit(searchWord)) { + is SubmitResult.ApResolved -> { + when (result.apResolve) { + is ApResolver.TypeNote -> { + startActivity( + NoteDetailActivity.newIntent( + this@SearchActivity, + result.apResolve.note.id + ), + ) + finish() + } + is ApResolver.TypeUser -> { + startActivity( + userDetailNavigation.newIntent( + UserDetailNavigationArgs.UserId( + result.apResolve.user.id + ), + ), + ) + finish() + } + } + } + is SubmitResult.Search -> { + val intent = Intent(this@SearchActivity, SearchResultActivity::class.java) + intent.putExtra(SearchResultActivity.EXTRA_SEARCH_WORLD, searchWord) + startActivity(intent) + overridePendingTransition(0, 0) + finish() + } + SubmitResult.Cancelled -> return@launch + + } + } } From c888d8ba58d3617adae599e11ff9ee8ba6479769 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 13:55:53 +0900 Subject: [PATCH 132/432] fix: memory leak probrem --- .../milktea/search/SearchResultActivity.kt | 14 ++++++++++++-- .../milktea/search/SearchTopFragment.kt | 15 +++++++++++++-- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt index 1f0c48802e..cc0af4f578 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt @@ -62,6 +62,8 @@ class SearchResultActivity : AppCompatActivity() { private val searchResultViewModel: SearchResultViewModel by viewModels() + private var tabLayoutMediator: TabLayoutMediator? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) applyTheme() @@ -87,12 +89,13 @@ class SearchResultActivity : AppCompatActivity() { val pager = SearchResultViewPagerAdapter(this, pageableFragmentFactory) binding.searchResultPager.adapter = pager - TabLayoutMediator( + tabLayoutMediator = TabLayoutMediator( binding.searchResultTab, binding.searchResultPager, ) { tab, position -> tab.text = pager.items[position].title.getString(this) - }.attach() + } + tabLayoutMediator?.attach() @@ -166,6 +169,13 @@ class SearchResultActivity : AppCompatActivity() { } } + override fun onDestroy() { + super.onDestroy() + + tabLayoutMediator?.detach() + tabLayoutMediator = null + } + } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt index e099858976..b2ebc4bcb9 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchTopFragment.kt @@ -48,17 +48,21 @@ class SearchTopFragment : Fragment(R.layout.fragment_search_top) { val viewModel: SearchTopViewModel by viewModels() + private var tabLayoutMediator: TabLayoutMediator? = null + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val adapter = SearchPagerAdapterV2(pageableFragmentFactory, this) mBinding.searchViewPager.adapter = adapter - TabLayoutMediator( + tabLayoutMediator = TabLayoutMediator( mBinding.searchTabLayout, mBinding.searchViewPager ) { tab, position -> tab.text = adapter.tabs[position].title.getString(requireContext()) - }.attach() + } + tabLayoutMediator?.attach() + (requireActivity() as MenuHost).addMenuProvider(object : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menuInflater.inflate(R.menu.search_top_menu, menu) @@ -98,6 +102,13 @@ class SearchTopFragment : Fragment(R.layout.fragment_search_top) { } } + override fun onDestroyView() { + super.onDestroyView() + + tabLayoutMediator?.detach() + tabLayoutMediator = null + } + } class SearchPagerAdapterV2( From c976c6268f0448426d64a7bf8a3222150f38902c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 14:41:44 +0900 Subject: [PATCH 133/432] =?UTF-8?q?feat:=20=E3=83=91=E3=83=83=E3=82=B1?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E5=90=8D=E3=82=92=E5=A4=89=E6=9B=B4renote->r?= =?UTF-8?q?epost?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/data/di/module/NoteModule.kt | 2 +- .../data/infrastructure/notes/renote/RenotesPagingService.kt | 4 ++-- .../net/pantasystem/milktea/note/renote/RenoteViewModel.kt | 2 +- .../net/pantasystem/milktea/note/renote/RenotesViewModel.kt | 4 ++-- .../{renote => repost}/CreateRenoteMultipleAccountUseCase.kt | 2 +- .../milktea/model/notes/{renote => repost}/Renotes.kt | 2 +- .../model/notes/{renote => repost}/RenotesPagingService.kt | 2 +- 7 files changed, 9 insertions(+), 9 deletions(-) rename modules/model/src/main/java/net/pantasystem/milktea/model/notes/{renote => repost}/CreateRenoteMultipleAccountUseCase.kt (98%) rename modules/model/src/main/java/net/pantasystem/milktea/model/notes/{renote => repost}/Renotes.kt (85%) rename modules/model/src/main/java/net/pantasystem/milktea/model/notes/{renote => repost}/RenotesPagingService.kt (88%) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt index 58184792d6..9166024364 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt @@ -21,7 +21,7 @@ import net.pantasystem.milktea.model.notes.NoteStreaming import net.pantasystem.milktea.model.notes.ReplyStreaming import net.pantasystem.milktea.model.notes.draft.DraftNoteRepository import net.pantasystem.milktea.model.notes.draft.DraftNoteService -import net.pantasystem.milktea.model.notes.renote.RenotesPagingService +import net.pantasystem.milktea.model.notes.repost.RenotesPagingService import javax.inject.Singleton @Module diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/renote/RenotesPagingService.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/renote/RenotesPagingService.kt index 8e632e9b8b..80a8a03b0f 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/renote/RenotesPagingService.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/renote/RenotesPagingService.kt @@ -15,8 +15,8 @@ import net.pantasystem.milktea.data.infrastructure.notes.NoteDataSourceAdder import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.notes.Note -import net.pantasystem.milktea.model.notes.renote.RenoteType -import net.pantasystem.milktea.model.notes.renote.RenotesPagingService +import net.pantasystem.milktea.model.notes.repost.RenoteType +import net.pantasystem.milktea.model.notes.repost.RenotesPagingService import net.pantasystem.milktea.model.user.UserDataSource import javax.inject.Inject diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt index 13a454a962..cac5d60e75 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt @@ -15,7 +15,7 @@ import net.pantasystem.milktea.common.asLoadingStateFlow import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.notes.* -import net.pantasystem.milktea.model.notes.renote.CreateRenoteMultipleAccountUseCase +import net.pantasystem.milktea.model.notes.repost.CreateRenoteMultipleAccountUseCase import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserDataSource import net.pantasystem.milktea.model.user.UserRepository diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt index 3c5b666623..07755ea9a9 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt @@ -13,8 +13,8 @@ import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.model.notes.* -import net.pantasystem.milktea.model.notes.renote.RenoteType -import net.pantasystem.milktea.model.notes.renote.RenotesPagingService +import net.pantasystem.milktea.model.notes.repost.RenoteType +import net.pantasystem.milktea.model.notes.repost.RenotesPagingService import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserRepository diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/CreateRenoteMultipleAccountUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CreateRenoteMultipleAccountUseCase.kt similarity index 98% rename from modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/CreateRenoteMultipleAccountUseCase.kt rename to modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CreateRenoteMultipleAccountUseCase.kt index a571dfce00..bc85371969 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/CreateRenoteMultipleAccountUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CreateRenoteMultipleAccountUseCase.kt @@ -1,4 +1,4 @@ -package net.pantasystem.milktea.model.notes.renote +package net.pantasystem.milktea.model.notes.repost import kotlinx.coroutines.coroutineScope import net.pantasystem.milktea.common.runCancellableCatching diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/Renotes.kt similarity index 85% rename from modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt rename to modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/Renotes.kt index beada9b57f..e75773108e 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/Renotes.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/Renotes.kt @@ -1,4 +1,4 @@ -package net.pantasystem.milktea.model.notes.renote +package net.pantasystem.milktea.model.notes.repost import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.user.User diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/RenotesPagingService.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/RenotesPagingService.kt similarity index 88% rename from modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/RenotesPagingService.kt rename to modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/RenotesPagingService.kt index 8f0437d753..cff5625c31 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/renote/RenotesPagingService.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/RenotesPagingService.kt @@ -1,4 +1,4 @@ -package net.pantasystem.milktea.model.notes.renote +package net.pantasystem.milktea.model.notes.repost import kotlinx.coroutines.flow.Flow import net.pantasystem.milktea.common.PageableState From e721bff6d79ae6e060feed66b632046b855e4be8 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 14:51:12 +0900 Subject: [PATCH 134/432] =?UTF-8?q?feat:=20repost=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E3=81=AA=E3=82=82=E3=81=AE=E3=81=8B=E3=83=81=E3=82=A7=E3=83=83?= =?UTF-8?q?=E3=82=AF=E3=81=99=E3=82=8BService=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/repost/CheckCanRepostService.kt | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CheckCanRepostService.kt diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CheckCanRepostService.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CheckCanRepostService.kt new file mode 100644 index 0000000000..6b9c329d46 --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CheckCanRepostService.kt @@ -0,0 +1,39 @@ +package net.pantasystem.milktea.model.notes.repost + +import net.pantasystem.milktea.common.runCancellableCatching +import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.notes.Note +import net.pantasystem.milktea.model.notes.NoteRepository +import net.pantasystem.milktea.model.user.User +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CheckCanRepostService @Inject constructor( + val accountRepository: AccountRepository, + val noteRepository: NoteRepository, +) { + + suspend fun canRepost(noteId: Note.Id): Result = runCancellableCatching { + val note = noteRepository.find(noteId).getOrThrow() + val account = accountRepository.get(noteId.accountId).getOrThrow() + val userId = User.Id(account.accountId, account.remoteId) + if (note.isRenoteOnly()) { + return@runCancellableCatching false + } + + if ((note.type as? Note.Type.Mastodon)?.reblogged == true) { + return@runCancellableCatching false + } + + if (userId == note.userId) { + return@runCancellableCatching true + } + + if (!note.canRenote(userId)) { + return@runCancellableCatching false + } + + true + } +} \ No newline at end of file From 27e440e72881615b20bfd0dffc3de83fe4e6f1a9 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:05:08 +0900 Subject: [PATCH 135/432] refactor --- .../milktea/model/notes/repost/CheckCanRepostService.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CheckCanRepostService.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CheckCanRepostService.kt index 6b9c329d46..de892714ae 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CheckCanRepostService.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CheckCanRepostService.kt @@ -30,10 +30,6 @@ class CheckCanRepostService @Inject constructor( return@runCancellableCatching true } - if (!note.canRenote(userId)) { - return@runCancellableCatching false - } - - true + note.canRenote(userId) } } \ No newline at end of file From 92969ed27963c01e495dd393b98b1dc229f1749a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:05:32 +0900 Subject: [PATCH 136/432] =?UTF-8?q?feat:=20=E5=AF=BE=E8=B1=A1=E3=81=AE?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=8CMastodon?= =?UTF-8?q?=E3=81=A7=E3=82=82=E5=8B=95=E4=BD=9C=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CreateRenoteMultipleAccountUseCase.kt | 74 +++++++++---------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CreateRenoteMultipleAccountUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CreateRenoteMultipleAccountUseCase.kt index bc85371969..035d66b9ac 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CreateRenoteMultipleAccountUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/repost/CreateRenoteMultipleAccountUseCase.kt @@ -3,14 +3,11 @@ package net.pantasystem.milktea.model.notes.repost import kotlinx.coroutines.coroutineScope import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.model.UseCase -import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.ap.ApResolver import net.pantasystem.milktea.model.ap.ApResolverRepository -import net.pantasystem.milktea.model.notes.CreateNote import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.notes.NoteRepository -import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserRepository import javax.inject.Inject import javax.inject.Singleton @@ -21,11 +18,12 @@ class CreateRenoteMultipleAccountUseCase @Inject constructor( private val apResolverRepository: ApResolverRepository, private val userRepository: UserRepository, private val noteRepository: NoteRepository, + private val checkCanRepostService: CheckCanRepostService, ) : UseCase { suspend operator fun invoke( noteId: Note.Id, - accountIds: List + accountIds: List, ): Result>> = runCancellableCatching { coroutineScope { val accounts = accountIds.map { @@ -38,45 +36,45 @@ class CreateRenoteMultipleAccountUseCase @Inject constructor( } } - private suspend fun resolveAndRenote(sourceNote: Note, accountId: Long): Result = runCancellableCatching { - val account = accountRepository.get(accountId).getOrThrow() - val relatedSourceNoteAccount = accountRepository.get(sourceNote.id.accountId).getOrThrow() - val user = userRepository.find(sourceNote.userId) - val noteUri = sourceNote.uri ?: "https://${user.host}/notes/${sourceNote.id.noteId}" - val relatedNote = if (account.getHost() != relatedSourceNoteAccount.getHost()) { - (apResolverRepository.resolve(accountId, noteUri).getOrThrow() as ApResolver.TypeNote) - .note - } else { - noteRepository.find(Note.Id(account.accountId, sourceNote.id.noteId)).getOrThrow() - } + private suspend fun resolveAndRenote(sourceNote: Note, accountId: Long): Result = + runCancellableCatching { + val account = accountRepository.get(accountId).getOrThrow() + val relatedSourceNoteAccount = + accountRepository.get(sourceNote.id.accountId).getOrThrow() + val user = userRepository.find(sourceNote.userId) + val noteUri = sourceNote.uri ?: "https://${user.host}/notes/${sourceNote.id.noteId}" + val relatedNote = if (account.getHost() != relatedSourceNoteAccount.getHost()) { + (apResolverRepository.resolve(accountId, noteUri) + .getOrThrow() as ApResolver.TypeNote) + .note + } else { + noteRepository.find(Note.Id(account.accountId, sourceNote.id.noteId)).getOrThrow() + } - renote(account, relatedNote).getOrThrow() - } + renote(relatedNote).getOrThrow() + } - private suspend fun renote(account: Account, note: Note): Result = runCancellableCatching { - if (note.canRenote(User.Id(account.accountId, account.remoteId))) { - noteRepository.create( - CreateNote( - author = account, - text = null, - visibility = note.visibility, - renoteId = note.id, - channelId = note.channelId - ) - ).getOrThrow() - } else { - throw IllegalArgumentException() + private suspend fun renote(note: Note): Result = + runCancellableCatching { + if (checkCanRepostService + .canRepost(note.id) + .getOrElse { false } + ) { + noteRepository.renote(note.id).getOrThrow() + } else { + throw IllegalArgumentException() + } } - } - private suspend fun recursiveSearchHasContentNote(noteId: Note.Id): Result = runCancellableCatching { - val note = noteRepository.find(noteId).getOrThrow() - if (note.hasContent()) { - note - } else { - recursiveSearchHasContentNote(note.renoteId!!).getOrThrow() + private suspend fun recursiveSearchHasContentNote(noteId: Note.Id): Result = + runCancellableCatching { + val note = noteRepository.find(noteId).getOrThrow() + if (note.hasContent()) { + note + } else { + recursiveSearchHasContentNote(note.renoteId!!).getOrThrow() + } } - } } From 89120b19bda8e5f12385e779bfe2373dc830a18a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:11:24 +0900 Subject: [PATCH 137/432] =?UTF-8?q?feat:=20=E3=82=B3=E3=83=B3=E3=83=86?= =?UTF-8?q?=E3=83=B3=E3=83=84=E8=A1=A8=E7=A4=BA=E7=94=A8=E3=81=AE=E6=8A=95?= =?UTF-8?q?=E7=A8=BF=E3=83=87=E3=83=BC=E3=82=BF=E3=82=92=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/viewmodel/PlaneNoteViewData.kt | 9 +-------- .../java/net/pantasystem/milktea/model/notes/Note.kt | 9 ++++++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/PlaneNoteViewData.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/PlaneNoteViewData.kt index 803f76a4d1..7e21062350 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/PlaneNoteViewData.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/PlaneNoteViewData.kt @@ -40,14 +40,7 @@ open class PlaneNoteViewData( var filterResult: FilterResult = FilterResult.NotExecuted - val toShowNote: NoteRelation - get() { - return if (note.note.isRenote() && !note.note.hasContent()) { - note.renote ?: note - } else { - note - } - } + val toShowNote: NoteRelation = note.contentNote val currentNote: StateFlow = noteDataSource.observeOne(toShowNote.note.id).map { it ?: toShowNote.note diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/Note.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/Note.kt index 2ea9967889..c43300015e 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/Note.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/Note.kt @@ -242,7 +242,14 @@ data class NoteRelation( val renote: NoteRelation?, val reply: NoteRelation?, val files: List?, -) : JSerializable +) : JSerializable { + + val contentNote: NoteRelation = if (note.isRenote() && !note.hasContent()) { + renote ?: this + } else { + this + } +} fun Note.Companion.make( id: Note.Id, From 02bcef8f34f8a709d4a82cd5c10cbca514bf8dd3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:14:08 +0900 Subject: [PATCH 138/432] fix: ui bug --- .../net/pantasystem/milktea/note/renote/RenoteViewModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt index cac5d60e75..08c3e2f0ab 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt @@ -92,7 +92,7 @@ class RenoteViewModel @Inject constructor( accountId = account.accountId, user = user, isSelected = selectedIds.any { id -> id == account.accountId }, - isEnable = note?.canRenote(account, user) == true, + isEnable = note?.contentNote?.canRenote(account, user) == true, ) } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) @@ -110,7 +110,7 @@ class RenoteViewModel @Inject constructor( private val _noteState = combine(note, _syncState) { n, s -> RenoteViewModelTargetNoteState( - note = n?.note, + note = n?.contentNote?.note, syncState = s ) }.stateIn( @@ -118,7 +118,7 @@ class RenoteViewModel @Inject constructor( SharingStarted.WhileSubscribed(5_000), RenoteViewModelTargetNoteState( _syncState.value, - note.value?.note, + note.value?.contentNote?.note, ) ) From af9eb4b8c29a8f56665acae0df316121a7418418 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:25:02 +0900 Subject: [PATCH 139/432] =?UTF-8?q?feat:=20feature=5Fquote=E3=82=92?= =?UTF-8?q?=E6=89=B1=E3=81=88=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/api/mastodon/instance/Instance.kt | 3 +++ .../milktea/data/infrastructure/TootEntityConverters.kt | 6 +++++- .../milktea/model/instance/MastodonInstanceInfo.kt | 4 ++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/instance/Instance.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/instance/Instance.kt index b2af757932..ec085add62 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/instance/Instance.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/instance/Instance.kt @@ -31,6 +31,9 @@ data class Instance( @SerialName("pleroma") val pleroma: Pleroma? = null, + + @SerialName("feature_quote") + val featureQuote: Boolean? = null, ) { @Serializable data class Configuration( diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/TootEntityConverters.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/TootEntityConverters.kt index c9d1ef9aea..989dd5161a 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/TootEntityConverters.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/TootEntityConverters.kt @@ -238,7 +238,11 @@ fun Instance.toModel(): MastodonInstanceInfo { } ) }, - fedibirdCapabilities = fedibirdCapabilities, + fedibirdCapabilities = fedibirdCapabilities?.let { + it + listOfNotNull( + if (featureQuote == true) "feature_quote" else null, + ) + }, pleroma = pleroma?.let { pleroma -> MastodonInstanceInfo.Pleroma( metadata = pleroma.metadata.let { m -> diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt index f1fc9e8748..d80235b1d2 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/MastodonInstanceInfo.kt @@ -54,6 +54,10 @@ data class MastodonInstanceInfo( ?: Int.MAX_VALUE.takeIf { pleroma?.metadata?.features?.contains("pleroma_emoji_reactions") == true } ?: 0 + + val featureQuote: Boolean + get() = fedibirdCapabilities?.contains("feature_quote") == true + data class Pleroma( val metadata: Metadata, ) { From 882e26491917c6d0edb76737be44afc856402bb0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:41:50 +0900 Subject: [PATCH 140/432] =?UTF-8?q?fix:=20=E3=82=AD=E3=83=A3=E3=83=83?= =?UTF-8?q?=E3=82=B7=E3=83=A5=E3=81=8C=E6=9B=B4=E6=96=B0=E3=81=95=E3=82=8C?= =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../instance/MastodonInstanceInfoRepositoryImpl.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MastodonInstanceInfoRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MastodonInstanceInfoRepositoryImpl.kt index 6ebfb0e000..7ebf5c927c 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MastodonInstanceInfoRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MastodonInstanceInfoRepositoryImpl.kt @@ -60,13 +60,8 @@ class MastodonInstanceInfoRepositoryImpl @Inject constructor( override suspend fun sync(instanceDomain: String): Result = runCancellableCatching { withContext(ioDispatcher) { val model = mastodonAPIProvider.get(instanceDomain).getInstance().toModel() - val exists = mastodonInstanceInfoDAO.findBy(URL(instanceDomain).host) cache.put(instanceDomain, model) - if (exists == null) { - mastodonInstanceInfoDAO.insert(MastodonInstanceInfoRecord.from(model)) - } else { - mastodonInstanceInfoDAO.update(MastodonInstanceInfoRecord.from(model)) - } + upInsert(model) } } From d67164a9d52b0c3f316fcd6236a7422ebdca1bd0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:42:05 +0900 Subject: [PATCH 141/432] =?UTF-8?q?feat:=20=E5=BC=95=E7=94=A8=E3=81=AB?= =?UTF-8?q?=E5=AF=BE=E5=BF=9C=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E5=A0=B4?= =?UTF-8?q?=E5=90=88=E3=81=AF=E5=BC=95=E7=94=A8=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt index 79e981b016..9140671491 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/NoteApiAdapter.kt @@ -94,6 +94,7 @@ class NoteApiAdapter @Inject constructor( }?.toInt() ?: (5 * 60), ) }, + quoteId = createNote.renoteId?.noteId, ) ).throwIfHasError().body() NoteResultType.Mastodon(requireNotNull(body)) From e4a3a1b337cee13feb4d89fddf424622c8434d9a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:42:18 +0900 Subject: [PATCH 142/432] =?UTF-8?q?feat:=20Mastodon=E3=81=A7=E3=81=82?= =?UTF-8?q?=E3=81=A3=E3=81=A6=E3=82=82=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/view/NoteCardActionHandler.kt | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteCardActionHandler.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteCardActionHandler.kt index b8ee79923c..efa9ebcb85 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteCardActionHandler.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteCardActionHandler.kt @@ -97,17 +97,10 @@ class NoteCardActionHandler( ).show(activity.supportFragmentManager, "") } is NoteCardAction.OnRenoteButtonClicked -> { - when(action.note.note.note.type) { - is Note.Type.Mastodon -> { - notesViewModel.toggleReblog(action.note.toShowNote.note.id) - } - is Note.Type.Misskey -> { - RenoteBottomSheetDialog.newInstance( - action.note.note.note.id, - action.note.isRenotedByMe - ).show(activity.supportFragmentManager, "") - } - } + RenoteBottomSheetDialog.newInstance( + action.note.note.note.id, + action.note.isRenotedByMe + ).show(activity.supportFragmentManager, "") } is NoteCardAction.OnRenoteButtonLongClicked -> { RenotesBottomSheetDialog.newInstance(action.note.toShowNote.note.id) From c1741001ec5d330af54ce4e1e19d2e848e50538b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:42:40 +0900 Subject: [PATCH 143/432] =?UTF-8?q?feat:=20=E5=BC=95=E7=94=A8=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=81=AA=E3=81=84=E3=82=B1=E3=83=BC=E3=82=B9=E3=81=AE?= =?UTF-8?q?=E5=A0=B4=E5=90=88=E3=81=AF=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/api/mastodon/status/CreateStatus.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/CreateStatus.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/CreateStatus.kt index 85dd324c76..40adfc0bd4 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/CreateStatus.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/CreateStatus.kt @@ -30,7 +30,10 @@ data class CreateStatus( val language: String? = null, @SerialName("scheduled_at") - val scheduledAt: Instant? = null + val scheduledAt: Instant? = null, + + @SerialName("quote_id") + val quoteId: String? = null, ) { @kotlinx.serialization.Serializable From 7886b53d68ee5dca86dd5cf39dcc6505520332c5 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:42:45 +0900 Subject: [PATCH 144/432] =?UTF-8?q?feat:=20=E5=BC=95=E7=94=A8=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=81=AA=E3=81=84=E3=82=B1=E3=83=BC=E3=82=B9=E3=81=AE?= =?UTF-8?q?=E5=A0=B4=E5=90=88=E3=81=AF=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/renote/RenoteDialogLayout.kt | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteDialogLayout.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteDialogLayout.kt index ccaf03155d..44486245ed 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteDialogLayout.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteDialogLayout.kt @@ -47,11 +47,14 @@ fun RenoteDialogContent( } - NormalBottomSheetDialogSelectionLayout( - onClick = onQuoteRenoteButtonClicked, - icon = Icons.Default.FormatQuote, - text = stringResource(id = R.string.quote_renote) - ) + + if (uiState.canQuote) { + NormalBottomSheetDialogSelectionLayout( + onClick = onQuoteRenoteButtonClicked, + icon = Icons.Default.FormatQuote, + text = stringResource(id = R.string.quote_renote) + ) + } } } } \ No newline at end of file From 4f9a0578e2802aa8502b542aea8e7c5060094fe1 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:42:48 +0900 Subject: [PATCH 145/432] =?UTF-8?q?feat:=20=E5=BC=95=E7=94=A8=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=81=AA=E3=81=84=E3=82=B1=E3=83=BC=E3=82=B9=E3=81=AE?= =?UTF-8?q?=E5=A0=B4=E5=90=88=E3=81=AF=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/renote/RenoteViewModel.kt | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt index 08c3e2f0ab..752df19f67 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteViewModel.kt @@ -14,6 +14,8 @@ import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common.asLoadingStateFlow import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.instance.InstanceInfoService +import net.pantasystem.milktea.model.instance.InstanceInfoType import net.pantasystem.milktea.model.notes.* import net.pantasystem.milktea.model.notes.repost.CreateRenoteMultipleAccountUseCase import net.pantasystem.milktea.model.user.User @@ -30,6 +32,7 @@ class RenoteViewModel @Inject constructor( val userDataSource: UserDataSource, val renoteUseCase: CreateRenoteMultipleAccountUseCase, val noteRelationGetter: NoteRelationGetter, + val instanceInfoService: InstanceInfoService, loggerFactory: Logger.Factory ) : ViewModel() { @@ -122,15 +125,25 @@ class RenoteViewModel @Inject constructor( ) ) + @OptIn(ExperimentalCoroutinesApi::class) + private val currentAccountInstanceInfo = accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { + instanceInfoService.observe(it.normalizedInstanceUri) + }.catch { + logger.error("observe current account error", it) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + val uiState = combine( _targetNoteId, _noteState, - accountWithUsers - ) { noteId, syncState, accounts -> + accountWithUsers, + currentAccountInstanceInfo, + ) { noteId, syncState, accounts, instanceInfo -> RenoteViewModelUiState( targetNoteId = noteId, noteState = syncState, accounts = accounts, + canQuote = instanceInfo is InstanceInfoType.Misskey + || (instanceInfo as? InstanceInfoType.Mastodon)?.info?.featureQuote == true ) }.stateIn( viewModelScope, @@ -139,6 +152,7 @@ class RenoteViewModel @Inject constructor( _targetNoteId.value, _noteState.value, accountWithUsers.value, + true ) ) @@ -196,6 +210,7 @@ data class RenoteViewModelUiState( val targetNoteId: Note.Id?, val noteState: RenoteViewModelTargetNoteState, val accounts: List, + val canQuote: Boolean, ) data class RenoteViewModelTargetNoteState( From 77b130374de7798a53b1390f851f79a44d90affc Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:53:15 +0900 Subject: [PATCH 146/432] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E3=81=AEURL?= =?UTF-8?q?=E3=82=92=E8=B2=BC=E3=82=8A=E4=BB=98=E3=81=91=E3=82=89=E3=82=8C?= =?UTF-8?q?=E3=81=9F=E6=99=82=E3=81=AB=E5=BC=95=E7=94=A8=E3=81=AE=E5=88=A4?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/editor/viewmodel/NoteEditorViewModel.kt | 8 ++++++++ .../milktea/model/instance/InstanceInfoType.kt | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt index 2addfc409f..b2782bdd25 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt @@ -737,6 +737,14 @@ class NoteEditorViewModel @Inject constructor( fun onPastePostUrl(text: String, start: Int, beforeText: String, count: Int) = viewModelScope.launch { val urlText = text.substring(start, start + count) val ca = currentAccount.value ?: return@launch + val canQuote = instanceInfoService.find(ca.normalizedInstanceUri).map { + it.canQuote + }.getOrElse { false } + + if (!canQuote) { + return@launch + } + if (UrlPatternChecker.isMatch(urlText)) { apResolverRepository.resolve(ca.accountId, urlText).onSuccess { when(it) { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt index 625c627c39..894e0ea116 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt @@ -54,4 +54,12 @@ sealed interface InstanceInfoType { val canMultipleReaction: Boolean get() { return maxReactionsPerAccount > 1 } + + val canQuote: Boolean get() { + return when(this) { + is Mastodon -> info.featureQuote + is Misskey -> true + is Pleroma -> true + } + } } \ No newline at end of file From a872fafeaf7944866394b2e73e7f7b42eb0a8b40 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 15:57:55 +0900 Subject: [PATCH 147/432] =?UTF-8?q?feat:=20=E6=8A=95=E7=A8=BF=E3=81=AEURL?= =?UTF-8?q?=E3=82=92=E8=B2=BC=E3=82=8A=E4=BB=98=E3=81=91=E3=82=89=E3=82=8C?= =?UTF-8?q?=E3=81=9F=E6=99=82=E3=81=AB=E5=BC=95=E7=94=A8=E3=81=AE=E5=88=A4?= =?UTF-8?q?=E5=AE=9A=E3=82=92=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/editor/NoteEditorFragment.kt | 27 +++++++++++-------- .../editor/viewmodel/NoteEditorViewModel.kt | 11 +++++--- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index 921292359c..b4b0e939d4 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -372,18 +372,23 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti onTextChanged = { text, start, _, count -> val inputText = text?.substring(start, start + count)?: return@addTextChangedListener if (UrlPatternChecker.isMatch(inputText)) { - MaterialAlertDialogBuilder(requireContext()) - .setMessage(R.string.notes_confirm_attach_quote_note_by_url) - .setPositiveButton(android.R.string.ok) { _, _ -> - noteEditorViewModel.onPastePostUrl( - text.toString(), - start, - text.removeRange(start, start + count).toString(), - count, - ) + lifecycleScope.launch { + if (noteEditorViewModel.canQuote()) { + MaterialAlertDialogBuilder(requireContext()) + .setMessage(R.string.notes_confirm_attach_quote_note_by_url) + .setPositiveButton(android.R.string.ok) { _, _ -> + noteEditorViewModel.onPastePostUrl( + text.toString(), + start, + text.removeRange(start, start + count).toString(), + count, + ) + } + .setNegativeButton(android.R.string.cancel) { _, _ -> } + .show() } - .setNegativeButton(android.R.string.cancel) { _, _ -> } - .show() + } + } } ) { e -> diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt index b2782bdd25..2aa08dd2d8 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt @@ -737,9 +737,7 @@ class NoteEditorViewModel @Inject constructor( fun onPastePostUrl(text: String, start: Int, beforeText: String, count: Int) = viewModelScope.launch { val urlText = text.substring(start, start + count) val ca = currentAccount.value ?: return@launch - val canQuote = instanceInfoService.find(ca.normalizedInstanceUri).map { - it.canQuote - }.getOrElse { false } + val canQuote = canQuote() if (!canQuote) { return@launch @@ -767,6 +765,13 @@ class NoteEditorViewModel @Inject constructor( savedStateHandle.applyBy(NoteEditorUiState()) } + suspend fun canQuote(): Boolean { + val ca = currentAccount.value ?: return false + return instanceInfoService.find(ca.normalizedInstanceUri).map { + it.canQuote + }.getOrElse { false } + } + private fun setUpUserViewData(userId: User.Id): UserViewData { return userViewDataFactory.create(userId, viewModelScope, dispatcher) } From 1f2d24c9d1cab3abcddf42c118ba7c0450abc49f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 16:00:58 +0900 Subject: [PATCH 148/432] =?UTF-8?q?feat:=20fedibird=E3=81=AF=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E3=81=AE=E5=A0=B4=E5=90=88=E3=82=82=E5=85=A5=E5=8A=9B?= =?UTF-8?q?=E3=81=8C=E5=BF=85=E9=A0=88=E3=81=AE=E3=81=9F=E3=82=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/editor/viewmodel/NoteEditorUiState.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorUiState.kt index cf2230c8af..d414370b80 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorUiState.kt @@ -58,7 +58,7 @@ data class NoteEditorUiState( return false } - if (this.sendToState.renoteId != null) { + if (this.sendToState.renoteId != null && currentAccount?.instanceType == Account.InstanceType.MISSKEY) { return true } if (this.poll != null && this.poll.checkValidate()) { From 4a7e5ecf45aa7bd2736da4f79819d893fa464749 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:26:27 +0900 Subject: [PATCH 149/432] =?UTF-8?q?feat:=20=E6=A4=9C=E7=B4=A2=E3=83=9C?= =?UTF-8?q?=E3=82=BF=E3=83=B3=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_resource/src/main/res/menu/activity_user_menu.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/common_resource/src/main/res/menu/activity_user_menu.xml b/modules/common_resource/src/main/res/menu/activity_user_menu.xml index 79be38839e..491a0b5d4a 100644 --- a/modules/common_resource/src/main/res/menu/activity_user_menu.xml +++ b/modules/common_resource/src/main/res/menu/activity_user_menu.xml @@ -2,6 +2,10 @@ + Date: Thu, 11 May 2023 17:30:14 +0900 Subject: [PATCH 150/432] =?UTF-8?q?feat:=20=E6=A4=9C=E7=B4=A2=E3=83=9C?= =?UTF-8?q?=E3=82=BF=E3=83=B3=E3=82=92=E6=8A=BC=E3=81=97=E3=81=9F=E6=99=82?= =?UTF-8?q?=E3=81=AE=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=92?= =?UTF-8?q?=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/user/activity/UserDetailActivity.kt | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt index b2c251e8f3..12b0027ee5 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt @@ -113,6 +113,9 @@ class UserDetailActivity : AppCompatActivity() { @Inject lateinit var pageableFragmentFactory: PageableFragmentFactory + @Inject + lateinit var searchNavigation: SearchNavigation + @ExperimentalCoroutinesApi val mViewModel: UserDetailViewModel by viewModels { @@ -400,6 +403,16 @@ class UserDetailActivity : AppCompatActivity() { R.id.renoteMute -> { mViewModel.muteRenotes() } + R.id.nav_search_by_user -> { + startActivity(searchNavigation.newIntent( + SearchNavType.SearchScreen( + acct = mViewModel.userState.value?.let { + "@${it.userName}@${it.host}" + } + ) + ) + ) + } else -> return false } @@ -429,7 +442,7 @@ class UserDetailActivity : AppCompatActivity() { private fun applyRemoteUserStateLayoutBackgroundColor( binding: ActivityUserDetailBinding, - config: Config + config: Config, ) { val typed = TypedValue() if (config.theme is Theme.Bread) { From f59360f3a74ddfb7d13bb4f6fa1258c51281832b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:30:28 +0900 Subject: [PATCH 151/432] =?UTF-8?q?feat:=20acct=E3=82=92=E5=8F=97=E3=81=91?= =?UTF-8?q?=E5=85=A5=E3=82=8C=E3=82=89=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_navigation/SearchNavigation.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchNavigation.kt b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchNavigation.kt index bfb073938b..1153305b12 100644 --- a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchNavigation.kt +++ b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchNavigation.kt @@ -4,7 +4,8 @@ interface SearchNavigation : ActivityNavigation sealed interface SearchNavType { val searchWord: String? - data class ResultScreen(override val searchWord: String) : SearchNavType - data class SearchScreen(override val searchWord: String? = null) : SearchNavType + val acct: String? + data class ResultScreen(override val searchWord: String, override val acct: String? = null) : SearchNavType + data class SearchScreen(override val searchWord: String? = null, override val acct: String? = null) : SearchNavType } \ No newline at end of file From 48a6a93745db75b6b9b591f06f2e02772c9b815f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:30:44 +0900 Subject: [PATCH 152/432] =?UTF-8?q?feat:=20acct=E3=82=92=E5=8F=97=E3=81=91?= =?UTF-8?q?=E6=B8=A1=E3=81=99=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/search/SearchActivity.kt | 3 +++ .../net/pantasystem/milktea/search/SearchResultActivity.kt | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchActivity.kt index c7a7d353e5..af9a3e3007 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchActivity.kt @@ -44,6 +44,7 @@ class SearchActivity : AppCompatActivity() { private var mSearchView: SearchView? = null private var mSearchWord: String? = null + private var mAcct: String? = null private val binding: ActivitySearchBinding by dataBinding() @@ -60,6 +61,7 @@ class SearchActivity : AppCompatActivity() { supportActionBar?.setDisplayShowTitleEnabled(false) mSearchWord = intent.getStringExtra(EXTRA_SEARCH_WORD) + mAcct = intent.getStringExtra(SearchResultViewModel.EXTRA_ACCT) findViewById(R.id.composeBase).setContent { MdcTheme { @@ -154,6 +156,7 @@ class SearchActivity : AppCompatActivity() { is SubmitResult.Search -> { val intent = Intent(this@SearchActivity, SearchResultActivity::class.java) intent.putExtra(SearchResultActivity.EXTRA_SEARCH_WORLD, searchWord) + intent.putExtra(SearchResultViewModel.EXTRA_ACCT, mAcct) startActivity(intent) overridePendingTransition(0, 0) finish() diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt index cc0af4f578..5a9aa16447 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt @@ -187,6 +187,9 @@ class SearchNavigationImpl @Inject constructor( is SearchNavType.ResultScreen -> { val intent = Intent(activity, SearchResultActivity::class.java) intent.putExtra(SearchResultActivity.EXTRA_SEARCH_WORLD, args.searchWord) + if (args.acct != null) { + intent.putExtra(SearchResultViewModel.EXTRA_ACCT, args.acct) + } intent } is SearchNavType.SearchScreen -> { @@ -194,6 +197,9 @@ class SearchNavigationImpl @Inject constructor( if (args.searchWord != null) { intent.putExtra(SearchActivity.EXTRA_SEARCH_WORD, args.searchWord) } + if (args.acct != null) { + intent.putExtra(SearchResultViewModel.EXTRA_ACCT, args.acct) + } intent } } From 91ba868c5d2a4211e3f8f4dc306e22831a505e60 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:39:43 +0900 Subject: [PATCH 153/432] =?UTF-8?q?feat:=20acct=E3=81=8B=E3=82=89userId?= =?UTF-8?q?=E3=82=92=E5=8F=96=E5=BE=97=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/search/SearchResultViewModel.kt | 54 ++++++++++++++++--- 1 file changed, 48 insertions(+), 6 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt index faeb62b824..889f490f09 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt @@ -4,43 +4,79 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore +import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.mapCancellableCatching import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.page.PageableTemplate +import net.pantasystem.milktea.model.user.Acct +import net.pantasystem.milktea.model.user.User +import net.pantasystem.milktea.model.user.UserRepository import javax.inject.Inject @HiltViewModel class SearchResultViewModel @Inject constructor( + loggerFactory: Logger.Factory, private val accountStore: AccountStore, private val accountRepository: AccountRepository, + private val userRepository: UserRepository, private val savedStateHandle: SavedStateHandle, ) : ViewModel() { companion object { const val EXTRA_KEYWORD = "net.pantasystem.milktea.search.SearchResultViewModel.EXTRA_KEYWORD" + const val EXTRA_ACCT = "net.pantasystem.milktea.search.SearchResultActivity.EXTRA_ACCT" + + } + + private val logger by lazy { + loggerFactory.create("SearchResultVM") } private val keyword = savedStateHandle.getStateFlow(EXTRA_KEYWORD, "") + private val acct = savedStateHandle.getStateFlow(EXTRA_ACCT, null) + private val user = combine( + acct, + accountStore.observeCurrentAccount.filterNotNull() + ) { acct, ac -> + userRepository.findByUserName( + ac.accountId, + Acct(acct ?: "").userName, + Acct(acct ?: "").host + ) + }.catch { + logger.debug("ユーザの情報の取得に失敗", e = it) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) val uiState = combine( accountStore.observeCurrentAccount, - keyword - ) { currentAccount, keyword -> + keyword, + acct, + user + ) { currentAccount, keyword, acct, user -> SearchResultUiState( currentAccount = currentAccount, - keyword = keyword + keyword = keyword, + acct = acct, + user = user ) - } + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + SearchResultUiState(null, "", null, null) + ) fun setKeyword(q: String) { savedStateHandle[EXTRA_KEYWORD] = q } + fun setAcct(acct: String?) { + savedStateHandle[EXTRA_ACCT] = acct + } fun toggleAddToTab() { viewModelScope.launch { val keyword = savedStateHandle.get(EXTRA_KEYWORD) ?: return@launch @@ -67,8 +103,10 @@ class SearchResultViewModel @Inject constructor( data class SearchResultUiState( val currentAccount: Account?, val keyword: String, + val acct: String?, + val user: User?, ) { - val isTag: Boolean = keyword.startsWith("#") + val isTag: Boolean = keyword.startsWith("#") && acct == null val tabItems: List = when (currentAccount?.instanceType) { Account.InstanceType.MISSKEY -> listOfNotNull( @@ -83,6 +121,7 @@ data class SearchResultUiState( title = StringSource(R.string.timeline), type = SearchResultTabItem.Type.SearchMisskeyPosts, query = keyword, + userId = user?.id?.id, ) }, if (isTag) SearchResultTabItem( @@ -94,6 +133,7 @@ data class SearchResultUiState( title = StringSource(R.string.user), type = SearchResultTabItem.Type.SearchMisskeyUsers, query = keyword, + userId = user?.id?.id, ) ) Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> listOfNotNull( @@ -108,6 +148,7 @@ data class SearchResultUiState( title = StringSource(R.string.timeline), type = SearchResultTabItem.Type.SearchMastodonPosts, query = keyword, + userId = user?.id?.id, ) }, SearchResultTabItem( @@ -125,6 +166,7 @@ data class SearchResultTabItem( val title: StringSource, val type: Type, val query: String, + val userId: String? = null, ) { enum class Type { SearchMisskeyPosts, From 76bad9ce183b943e983a73a4da1c460bb01e6839 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:46:45 +0900 Subject: [PATCH 154/432] =?UTF-8?q?hotfix:=20state=E3=81=8C=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E3=81=AB?= =?UTF-8?q?=E3=82=82=E9=96=A2=E3=82=8F=E3=82=89=E3=81=9AFragment=E3=81=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E3=81=95=E3=82=8C=E3=81=AA=E3=81=84=E4=B8=8D?= =?UTF-8?q?=E5=85=B7=E5=90=88=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../search/SearchResultViewPagerAdapter.kt | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt index 87943e8836..2df2a04a76 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt @@ -20,9 +20,9 @@ class SearchResultViewPagerAdapter( override fun createFragment(position: Int): Fragment { val item = items[position] - return when(item.type) { + return when (item.type) { SearchResultTabItem.Type.SearchMisskeyPosts -> pageableFragmentFactory.create( - Pageable.Search(query = item.query) + Pageable.Search(query = item.query, userId = item.userId) ) SearchResultTabItem.Type.SearchMisskeyPostsByTag -> pageableFragmentFactory.create( Pageable.SearchByTag(tag = item.query) @@ -31,9 +31,15 @@ class SearchResultViewPagerAdapter( Pageable.SearchByTag(tag = item.query, withFiles = true) ) SearchResultTabItem.Type.SearchMisskeyUsers -> SearchUserFragment.newInstance(item.query) - SearchResultTabItem.Type.SearchMastodonPosts -> pageableFragmentFactory.create(Pageable.Mastodon.SearchTimeline(item.query)) + SearchResultTabItem.Type.SearchMastodonPosts -> pageableFragmentFactory.create( + Pageable.Mastodon.SearchTimeline( + item.query, + userId = item.userId + ) + ) SearchResultTabItem.Type.SearchMastodonPostsByTag -> pageableFragmentFactory.create( - Pageable.Mastodon.TagTimeline(item.query)) + Pageable.Mastodon.TagTimeline(item.query) + ) SearchResultTabItem.Type.SearchMastodonUsers -> SearchUserFragment.newInstance(item.query) } } @@ -65,4 +71,14 @@ class SearchResultViewPagerAdapter( val result = DiffUtil.calculateDiff(callback) result.dispatchUpdatesTo(this) } + + override fun getItemId(position: Int): Long { + return items[position].hashCode().toLong() + } + + override fun containsItem(itemId: Long): Boolean { + return items.map { + it.hashCode().toLong() + }.contains(itemId) + } } \ No newline at end of file From 80633b66e537ceca35b7cded5c5761eeb92aa7ac Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:47:01 +0900 Subject: [PATCH 155/432] =?UTF-8?q?feat:=20acct=E3=82=92set=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/search/SearchResultActivity.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt index 5a9aa16447..f0368cea1c 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt @@ -75,6 +75,8 @@ class SearchResultActivity : AppCompatActivity() { ?: intent.data?.getQueryParameter("keyword") searchResultViewModel.setKeyword(keyword ?: "") + searchResultViewModel.setAcct(intent.getStringExtra(SearchResultViewModel.EXTRA_ACCT)) + mSearchWord = keyword From 365e0440d344c1ac813d5a6a90376a9873ad9c1c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:47:10 +0900 Subject: [PATCH 156/432] =?UTF-8?q?feat:=20userId=E3=82=92=E4=BF=9D?= =?UTF-8?q?=E6=8C=81=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/model/account/page/PageParams.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt index 2e00e51d00..58cfbd44bd 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt @@ -194,7 +194,8 @@ data class PageParams( } MASTODON_SEARCH_TIMELINE -> { Pageable.Mastodon.SearchTimeline( - requireNotNull(query) + query = requireNotNull(query), + userId = userId, ) } MASTODON_TAG_TIMELINE -> { From 9addd9e0ce14555f226c86eedac7b07f17e16bfc Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:47:14 +0900 Subject: [PATCH 157/432] fix --- .../pantasystem/milktea/data/infrastructure/notes/PageParams.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/PageParams.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/PageParams.kt index a48b9aaf23..0511ac21b2 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/PageParams.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/PageParams.kt @@ -24,5 +24,6 @@ fun PageParams.toNoteRequest(i: String?) : NoteRequest { offset = offset, markAsRead = markAsRead, channelId = channelId, + userId = userId ) } \ No newline at end of file From 130ea7d598818953ef8aa1aeda7a8a4dc6a19bac Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:47:25 +0900 Subject: [PATCH 158/432] =?UTF-8?q?feat:=20userId=E3=82=92=E4=BF=9D?= =?UTF-8?q?=E6=8C=81=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/model/account/page/Pageable.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt index 7bc25b10f7..d1b25655c6 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt @@ -481,12 +481,14 @@ sealed class Pageable : Serializable { } data class SearchTimeline( - val query: String + val query: String, + val userId: String? = null, ) : Mastodon() { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_SEARCH_TIMELINE, - query = query + query = query, + userId = userId, ) } } From 93ffe1aead580487a1f2f465766026844281ce59 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 11 May 2023 17:49:19 +0900 Subject: [PATCH 159/432] =?UTF-8?q?feat:=20mastodon=E3=81=AB=E3=82=82userI?= =?UTF-8?q?d=E6=8C=87=E5=AE=9A=E3=81=A7=E6=A4=9C=E7=B4=A2=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../notes/MastodonTimelineStorePagingStoreImpl.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt index ea11cf678d..4fd8ec9643 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt @@ -201,7 +201,8 @@ internal class MastodonTimelineStorePagingStoreImpl( q = pageableTimeline.query, type = "statuses", maxId = maxId, - offset = (getState().content as? StateContent.Exist)?.rawContent?.size ?: 0 + offset = (getState().content as? StateContent.Exist)?.rawContent?.size ?: 0, + accountId = pageableTimeline.userId ).throwIfHasError().also { updateMaxIdFrom(it) }.body()?.statuses From bfad07de17fb35489c93841e48cb31872d12ae4f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 09:41:52 +0900 Subject: [PATCH 160/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=AE=E6=A7=8B=E9=80=A0=E3=81=8C=E3=83=8D=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=97=E3=81=AA=E3=81=84=E6=96=B9=E5=BC=8F=E3=81=A7=E4=BD=9C?= =?UTF-8?q?=E6=88=90=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/EmojiPickerUiState.kt | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt index 98eede617c..872a20536d 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt @@ -187,6 +187,22 @@ data class EmojiPickerUiState( ) } + val emojiListItems: List = listOf( + EmojiListItemType.Header(StringSource.invoke(R.string.user)), + ) + userSettingEmojis.map { + EmojiListItemType.EmojiItem(it) + } + EmojiListItemType.Header(StringSource.invoke(R.string.often_use)) + frequencyUsedReactionsV2.map { + EmojiListItemType.EmojiItem(it) + } + EmojiListItemType.Header(StringSource(R.string.recently_used)) + recentlyUsed.map { + EmojiListItemType.EmojiItem(it) + } + EmojiListItemType.Header(StringSource.invoke(R.string.other)) + otherEmojis.map { + EmojiListItemType.EmojiItem(it) + } + categories.map { category -> + listOf(EmojiListItemType.Header(StringSource.invoke(category))) + getCategoryBy(category).map { + EmojiListItemType.EmojiItem(it) + } + }.flatten() + val searchFilteredEmojis = customEmojis.filterEmojiBy(keyword).map { EmojiType.CustomEmoji(it) }.sortedBy { @@ -229,6 +245,12 @@ sealed interface SegmentType { } } +sealed interface EmojiListItemType { + data class EmojiItem(val emoji: EmojiType) : EmojiListItemType + + data class Header(val label: StringSource) : EmojiListItemType +} + sealed interface EmojiType { data class Legacy(val type: String) : EmojiType data class CustomEmoji(val emoji: Emoji) : EmojiType From 89227c8400c24caeb39e7a04d8236b09fc78c31c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 09:54:29 +0900 Subject: [PATCH 161/432] =?UTF-8?q?feat:=20adapter=E3=81=AE=E5=AE=9F?= =?UTF-8?q?=E8=A3=85=E3=82=92=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reaction/choices/EmojiListItemsAdapter.kt | 147 ++++++++++++++++++ .../layout/item_emoji_list_item_header.xml | 9 ++ 2 files changed, 156 insertions(+) create mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt create mode 100644 modules/features/note/src/main/res/layout/item_emoji_list_item_header.xml diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt new file mode 100644 index 0000000000..649a2afbb9 --- /dev/null +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt @@ -0,0 +1,147 @@ +package net.pantasystem.milktea.note.reaction.choices + +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.ListAdapter +import androidx.recyclerview.widget.RecyclerView +import net.pantasystem.milktea.common.glide.GlideApp +import net.pantasystem.milktea.common_android.ui.VisibilityHelper.setMemoVisibility +import net.pantasystem.milktea.model.notes.reaction.LegacyReaction +import net.pantasystem.milktea.note.EmojiListItemType +import net.pantasystem.milktea.note.EmojiType +import net.pantasystem.milktea.note.R +import net.pantasystem.milktea.note.databinding.ItemEmojiChoiceBinding +import net.pantasystem.milktea.note.databinding.ItemEmojiListItemHeaderBinding + +class EmojiListItemsAdapter( + private val onEmojiSelected: (EmojiType) -> Unit, + private val onEmojiLongClicked: (EmojiType) -> Boolean, +) : ListAdapter( + DiffUtilItemCallback() +) { + class DiffUtilItemCallback : DiffUtil.ItemCallback() { + + + override fun areContentsTheSame( + oldItem: EmojiListItemType, + newItem: EmojiListItemType, + ): Boolean { + if (oldItem is EmojiListItemType.EmojiItem && newItem is EmojiListItemType.EmojiItem) { + return oldItem.emoji.areContentsTheSame(newItem.emoji) + } + return oldItem == newItem + } + + override fun areItemsTheSame( + oldItem: EmojiListItemType, + newItem: EmojiListItemType, + ): Boolean { + if (oldItem is EmojiListItemType.EmojiItem && newItem is EmojiListItemType.EmojiItem) { + return oldItem.emoji.areItemsTheSame(newItem.emoji) + } + return oldItem == newItem + } + + } + + sealed class VH(view: View) : RecyclerView.ViewHolder(view) + class EmojiVH(val binding: ItemEmojiChoiceBinding) : VH(binding.root) { + + fun onBind( + item: EmojiType, onEmojiSelected: (EmojiType) -> Unit, + onEmojiLongClicked: (EmojiType) -> Boolean, + ) { + when (item) { + is EmojiType.CustomEmoji -> { + GlideApp.with(binding.reactionImagePreview) + .load(item.emoji.url ?: item.emoji.uri) + // FIXME: webpの場合うまく表示できなくなる +// .centerCrop() + .override(60) + .into(binding.reactionImagePreview) + binding.reactionStringPreview.setMemoVisibility(View.GONE) + binding.reactionImagePreview.setMemoVisibility(View.VISIBLE) + } + is EmojiType.Legacy -> { + binding.reactionImagePreview.setMemoVisibility(View.GONE) + binding.reactionStringPreview.setMemoVisibility(View.VISIBLE) + binding.reactionStringPreview.text = + requireNotNull(LegacyReaction.reactionMap[item.type]) + } + is EmojiType.UtfEmoji -> { + binding.reactionStringPreview.setMemoVisibility(View.VISIBLE) + binding.reactionImagePreview.setMemoVisibility(View.GONE) + binding.reactionStringPreview.text = item.code + } + } + binding.root.setOnClickListener { + onEmojiSelected(item) + } + binding.root.setOnLongClickListener { + onEmojiLongClicked(item) + } + binding.executePendingBindings() + } + } + + class HeaderVH(val binding: ItemEmojiListItemHeaderBinding) : VH(binding.root) + + + override fun getItemViewType(position: Int): Int { + return when (getItem(position)) { + is EmojiListItemType.EmojiItem -> ItemType.Emoji.ordinal + is EmojiListItemType.Header -> ItemType.Header.ordinal + } + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH { + when (ItemType.values()[viewType]) { + ItemType.Header -> { + val binding = ItemEmojiListItemHeaderBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false + ) + return HeaderVH(binding) + } + ItemType.Emoji -> { + val binding = + DataBindingUtil.inflate( + LayoutInflater.from(parent.context), + R.layout.item_emoji_choice, + parent, + false + ) + return EmojiVH( + binding + ) + } + } + + } + + override fun onBindViewHolder(holder: VH, position: Int) { + when (val item = getItem(position)) { + is EmojiListItemType.EmojiItem -> { + (holder as EmojiVH).onBind( + item.emoji, + onEmojiLongClicked = onEmojiLongClicked, + onEmojiSelected = onEmojiSelected + ) + } + is EmojiListItemType.Header -> { + (holder as HeaderVH).binding.categoryName.text = + item.label.getString(holder.binding.root.context) + } + } + + } + + enum class ItemType { + Header, Emoji + } + +} \ No newline at end of file diff --git a/modules/features/note/src/main/res/layout/item_emoji_list_item_header.xml b/modules/features/note/src/main/res/layout/item_emoji_list_item_header.xml new file mode 100644 index 0000000000..48a08929bb --- /dev/null +++ b/modules/features/note/src/main/res/layout/item_emoji_list_item_header.xml @@ -0,0 +1,9 @@ + + \ No newline at end of file From 4c6eb76633c75312b0bb1acbc9a3e4a1c57184fa Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 10:43:11 +0900 Subject: [PATCH 162/432] fix --- .../layout/item_emoji_list_item_header.xml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/modules/features/note/src/main/res/layout/item_emoji_list_item_header.xml b/modules/features/note/src/main/res/layout/item_emoji_list_item_header.xml index 48a08929bb..bfab4c1611 100644 --- a/modules/features/note/src/main/res/layout/item_emoji_list_item_header.xml +++ b/modules/features/note/src/main/res/layout/item_emoji_list_item_header.xml @@ -1,9 +1,14 @@ - \ No newline at end of file + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content"> + + \ No newline at end of file From a5c472eeef8830cecfaafffacbd246a08d0d7e36 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 10:50:19 +0900 Subject: [PATCH 163/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=B9=E3=83=88?= =?UTF-8?q?=E3=81=8C=E3=83=8D=E3=82=B9=E3=83=88=E3=81=97=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/EmojiPickerUiState.kt | 4 ++ .../note/emojis/EmojiPickerFragment.kt | 65 ++++++++++++++++--- 2 files changed, 59 insertions(+), 10 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt index 872a20536d..93c78b6950 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt @@ -209,6 +209,10 @@ data class EmojiPickerUiState( LevenshteinDistance(it.emoji.name, keyword) } + val tabLabels = emojiListItems.mapNotNull { + (it as? EmojiListItemType.Header)?.label + } + fun isExistsConfig(emojiType: EmojiType): Boolean { return userSettingEmojis.any { emojiType.areItemsTheSame(it) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt index 5c25a72760..4c623c252f 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt @@ -3,6 +3,7 @@ package net.pantasystem.milktea.note.emojis import android.content.Context import android.os.Bundle import android.view.View +import android.view.ViewTreeObserver import android.view.inputmethod.EditorInfo import android.widget.EditText import androidx.fragment.app.Fragment @@ -12,7 +13,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.ahmadhamwi.tabsync.TabbedListMediator import com.google.android.flexbox.AlignItems @@ -23,14 +24,16 @@ import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.launch +import net.pantasystem.milktea.common_android.resource.convertDp2Px import net.pantasystem.milktea.model.notes.reaction.LegacyReaction import net.pantasystem.milktea.model.notes.reaction.Reaction import net.pantasystem.milktea.model.notes.reaction.ReactionSelection +import net.pantasystem.milktea.note.EmojiListItemType import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentEmojiPickerBinding import net.pantasystem.milktea.note.emojis.viewmodel.EmojiPickerViewModel import net.pantasystem.milktea.note.reaction.choices.EmojiChoicesAdapter -import net.pantasystem.milktea.note.reaction.choices.EmojiChoicesListAdapter +import net.pantasystem.milktea.note.reaction.choices.EmojiListItemsAdapter import net.pantasystem.milktea.note.toTextReaction @AndroidEntryPoint @@ -129,9 +132,8 @@ class EmojiSelectionBinder( searchSuggestionListView.adapter = searchedReactionAdapter searchSuggestionListView.layoutManager = flexBoxLayoutManager - val layoutManager = LinearLayoutManager(context) - val choicesAdapter = EmojiChoicesListAdapter( + val adapter = EmojiListItemsAdapter( onEmojiLongClicked = { emojiType -> val exists = emojiPickerViewModel.uiState.value.isExistsConfig(emojiType) if (!exists) { @@ -146,15 +148,52 @@ class EmojiSelectionBinder( onReactionSelected(it.toTextReaction()) } ) - recyclerView.adapter = choicesAdapter + val layoutManager = GridLayoutManager(context, 5) + + layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return when(EmojiListItemsAdapter.ItemType.values()[adapter.getItemViewType(position)]) { + EmojiListItemsAdapter.ItemType.Header -> 5 + EmojiListItemsAdapter.ItemType.Emoji -> 1 + } + } + + } recyclerView.layoutManager = layoutManager + fun calculateSpanCount(): Int { + val viewWidth = recyclerView.measuredWidth + val itemWidth = context.convertDp2Px(54f).toInt() + return viewWidth / itemWidth + } + + val listener = object : ViewTreeObserver.OnGlobalLayoutListener { + override fun onGlobalLayout() { + val count = calculateSpanCount().coerceAtLeast(4) + val lm = GridLayoutManager(context, count) + + lm.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { + override fun getSpanSize(position: Int): Int { + return when(EmojiListItemsAdapter.ItemType.values()[adapter.getItemViewType(position)]) { + EmojiListItemsAdapter.ItemType.Header -> count + EmojiListItemsAdapter.ItemType.Emoji -> 1 + } + } + } + recyclerView.viewTreeObserver.removeOnGlobalLayoutListener(this) + recyclerView.layoutManager = lm + } + } + recyclerView.viewTreeObserver.addOnGlobalLayoutListener(listener) + + recyclerView.adapter = adapter + scope.launch { lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { emojiPickerViewModel.uiState.collect { - choicesAdapter.submitList(it.segments) + adapter.submitList(it.emojiListItems) searchedReactionAdapter.submitList(it.searchFilteredEmojis) } } @@ -163,11 +202,12 @@ class EmojiSelectionBinder( var tabbedListMediator: TabbedListMediator? = null scope.launch { lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { - emojiPickerViewModel.tabLabels.filterNot { - it.isEmpty() + emojiPickerViewModel.uiState.filterNot { + it.tabLabels.isEmpty() }.collect { tabLayout.removeAllTabs() - it.map { + val labels = it.tabLabels + labels.map { val tab = tabLayout.newTab().apply { text = it.getString(context) } @@ -175,7 +215,12 @@ class EmojiSelectionBinder( } tabbedListMediator?.detach() tabbedListMediator = - TabbedListMediator(recyclerView, tabLayout, it.indices.toList()) + TabbedListMediator(recyclerView, tabLayout, it.emojiListItems.mapIndexedNotNull { index, emojiListItemType -> + when(emojiListItemType) { + is EmojiListItemType.EmojiItem -> null + is EmojiListItemType.Header -> index + } + }) tabbedListMediator?.attach() } } From a2a37ea2c256177e88dd93f3c3fce52f8be61433 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 11:16:59 +0900 Subject: [PATCH 164/432] refactor --- .../milktea/note/EmojiPickerUiState.kt | 81 ++++--------- .../emojis/viewmodel/EmojiPickerViewModel.kt | 2 - .../choices/EmojiChoicesListAdapter.kt | 113 ------------------ 3 files changed, 21 insertions(+), 175 deletions(-) delete mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiChoicesListAdapter.kt diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt index 93c78b6950..8a650d8122 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt @@ -30,24 +30,34 @@ class EmojiPickerUiStateService( @OptIn(ExperimentalCoroutinesApi::class) - private val emojis = accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { ac -> - customEmojiRepository.observeBy(ac.getHost()) - }.catch { - logger.error("絵文字の取得に失敗", it) - }.flowOn(Dispatchers.IO) + private val emojis = accountStore.observeCurrentAccount + .filterNotNull() + .distinctUntilChanged() + .flatMapLatest { ac -> + customEmojiRepository.observeBy(ac.getHost()) + }.catch { + logger.error("絵文字の取得に失敗", it) + }.flowOn(Dispatchers.IO) @OptIn(ExperimentalCoroutinesApi::class) - private val reactionCount = - accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { ac -> + private val reactionCount = accountStore + .observeCurrentAccount + .filterNotNull() + .distinctUntilChanged() + .flatMapLatest { ac -> reactionHistoryRepository.observeSumReactions(ac.normalizedInstanceUri) + .distinctUntilChanged() }.catch { logger.error("リアクション履歴の取得に失敗", it) }.flowOn(Dispatchers.IO) @OptIn(ExperimentalCoroutinesApi::class) - private val userSetting = - accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { ac -> + private val userSetting = accountStore.observeCurrentAccount + .filterNotNull() + .distinctUntilChanged() + .flatMapLatest { ac -> userEmojiConfigRepository.observeByInstanceDomain(ac.normalizedInstanceUri) + .distinctUntilChanged() }.catch { logger.error("ユーザーリアクション設定情報の取得に失敗", it) }.stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), emptyList()) @@ -109,12 +119,6 @@ class EmojiPickerUiStateService( ) ) - val tabLabels = uiState.map { uiState -> - uiState.segments.map { - it.label - } - }.distinctUntilChanged() - .stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), emptyList()) } data class EmojiPickerUiState( @@ -136,9 +140,7 @@ data class EmojiPickerUiState( }.distinct() } - - - val userSettingEmojis: List by lazy { + private val userSettingEmojis: List by lazy { userSettingReactions.mapNotNull { setting -> EmojiType.from(customEmojis, setting.reaction) }.ifEmpty { @@ -173,19 +175,6 @@ data class EmojiPickerUiState( EmojiType.from(customEmojis, it.reaction) } - val segments = listOfNotNull( - SegmentType.UserCustom(userSettingEmojis), - SegmentType.OftenUse(frequencyUsedReactionsV2), - SegmentType.RecentlyUsed(recentlyUsed), - otherEmojis.let { - SegmentType.OtherCategory(it) - }, - ) + categories.map { - SegmentType.Category( - it, - getCategoryBy(it) - ) - } val emojiListItems: List = listOf( EmojiListItemType.Header(StringSource.invoke(R.string.user)), @@ -220,34 +209,6 @@ data class EmojiPickerUiState( } } -sealed interface SegmentType { - val label: StringSource - val emojis: List - - data class Category(val name: String, override val emojis: List) : SegmentType { - override val label: StringSource - get() = StringSource.invoke(name) - } - - data class UserCustom(override val emojis: List) : SegmentType { - override val label: StringSource - get() = StringSource.invoke(R.string.user) - } - - data class OftenUse(override val emojis: List) : SegmentType { - override val label: StringSource - get() = StringSource.invoke(R.string.often_use) - } - - data class OtherCategory(override val emojis: List) : SegmentType { - override val label: StringSource - get() = StringSource.invoke(R.string.other) - } - data class RecentlyUsed(override val emojis: List) : SegmentType { - override val label: StringSource - get() = StringSource.invoke(R.string.recently_used) - } -} sealed interface EmojiListItemType { data class EmojiItem(val emoji: EmojiType) : EmojiListItemType @@ -268,7 +229,7 @@ sealed interface EmojiType { if (this.javaClass != other.javaClass) { return false } - return when(this) { + return when (this) { is CustomEmoji -> { emoji == (other as? CustomEmoji)?.emoji } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/viewmodel/EmojiPickerViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/viewmodel/EmojiPickerViewModel.kt index 435287fa71..dc0aade6b0 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/viewmodel/EmojiPickerViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/viewmodel/EmojiPickerViewModel.kt @@ -35,6 +35,4 @@ class EmojiPickerViewModel @Inject constructor( // 検索時の候補 val uiState = uiStateService.uiState - val tabLabels = uiStateService.tabLabels - } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiChoicesListAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiChoicesListAdapter.kt deleted file mode 100644 index 7e0e99a1f2..0000000000 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiChoicesListAdapter.kt +++ /dev/null @@ -1,113 +0,0 @@ -package net.pantasystem.milktea.note.reaction.choices - -import android.view.LayoutInflater -import android.view.ViewGroup -import android.view.ViewTreeObserver -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.GridLayoutManager -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.RecycledViewPool -import net.pantasystem.milktea.common_android.resource.convertDp2Px -import net.pantasystem.milktea.note.EmojiType -import net.pantasystem.milktea.note.SegmentType -import net.pantasystem.milktea.note.databinding.ItemCategoryWithListBinding -import kotlin.math.max - - -class EmojiChoicesListAdapter( - val onEmojiSelected: (EmojiType) -> Unit, - val onEmojiLongClicked: (EmojiType) -> Boolean, -) : ListAdapter( - object : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: SegmentType, newItem: SegmentType): Boolean { - return when (oldItem) { - is SegmentType.Category -> oldItem.name == (newItem as? SegmentType.Category)?.name - && oldItem.emojis == newItem.emojis - is SegmentType.OftenUse -> oldItem.emojis == newItem.emojis - is SegmentType.OtherCategory -> oldItem.emojis == newItem.emojis - is SegmentType.RecentlyUsed -> oldItem.emojis == newItem.emojis - is SegmentType.UserCustom -> oldItem.emojis == newItem.emojis - } - } - - override fun areItemsTheSame(oldItem: SegmentType, newItem: SegmentType): Boolean { - return oldItem.javaClass == newItem.javaClass && oldItem.label == newItem.label - } - } -) { - - private val viewPool = RecycledViewPool() - - override fun onBindViewHolder(holder: SegmentViewHolder, position: Int) { - holder.onBind(getItem(position)) - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SegmentViewHolder { - return SegmentViewHolder( - viewPool, - ItemCategoryWithListBinding.inflate(LayoutInflater.from(parent.context), parent, false), - onEmojiSelected = onEmojiSelected, - onEmojiLongClicked = onEmojiLongClicked - ) - } -} - - -class SegmentViewHolder( - recyclerViewPool: RecycledViewPool, - val binding: ItemCategoryWithListBinding, - private val onEmojiSelected: (EmojiType) -> Unit, - private val onEmojiLongClicked: (EmojiType) -> Boolean, -) : RecyclerView.ViewHolder(binding.root) { - - var isSatLayoutManager = false - - init { - binding.emojisView.setRecycledViewPool(recyclerViewPool) - } - - fun onBind(segmentType: SegmentType) { - val adapter = EmojiChoicesAdapter( - onEmojiSelected = onEmojiSelected, - onEmojiLongClicked = onEmojiLongClicked, - ) - val label = segmentType.label.getString(binding.root.context) - binding.categoryName.text = label - binding.emojisView.setHasFixedSize(true) - - if (!isSatLayoutManager) { - val listener = object : ViewTreeObserver.OnGlobalLayoutListener { - override fun onGlobalLayout() { - val count = max(calculateSpanCount(), 4) - val layoutManager = - GridLayoutManager(binding.root.context, count) - binding.emojisView.layoutManager = layoutManager - binding.emojisView.viewTreeObserver.removeOnGlobalLayoutListener(this) - isSatLayoutManager = true - } - } - binding.emojisView.viewTreeObserver.addOnGlobalLayoutListener(listener) - } - - if (!isSatLayoutManager) { - val layoutManager = - GridLayoutManager(binding.root.context, 4) - - binding.emojisView.layoutManager = layoutManager - } - - - adapter.submitList(segmentType.emojis) - binding.emojisView.isNestedScrollingEnabled = false - - binding.emojisView.adapter = adapter - } - - private fun calculateSpanCount(): Int { - val viewWidth = binding.emojisView.measuredWidth - val itemWidth = binding.root.context.convertDp2Px(54f).toInt() - return viewWidth / itemWidth - } - -} \ No newline at end of file From ad874fa40db339d2d3f8f4c8e3f5fd7580f44bde Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 12:05:27 +0900 Subject: [PATCH 165/432] fix --- .../milktea/note/reaction/choices/EmojiListItemsAdapter.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt index 649a2afbb9..9c5edbbced 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt @@ -60,7 +60,6 @@ class EmojiListItemsAdapter( .load(item.emoji.url ?: item.emoji.uri) // FIXME: webpの場合うまく表示できなくなる // .centerCrop() - .override(60) .into(binding.reactionImagePreview) binding.reactionStringPreview.setMemoVisibility(View.GONE) binding.reactionImagePreview.setMemoVisibility(View.VISIBLE) From 1e638f081c93e41a58147ddd778aad14e5cd7915 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 12:48:21 +0900 Subject: [PATCH 166/432] fix --- .../net/pantasystem/milktea/note/EmojiPickerUiState.kt | 9 +++------ .../milktea/note/emojis/EmojiPickerFragment.kt | 4 ++-- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt index 8a650d8122..8ba2e1640c 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt @@ -32,32 +32,29 @@ class EmojiPickerUiStateService( @OptIn(ExperimentalCoroutinesApi::class) private val emojis = accountStore.observeCurrentAccount .filterNotNull() - .distinctUntilChanged() .flatMapLatest { ac -> customEmojiRepository.observeBy(ac.getHost()) }.catch { logger.error("絵文字の取得に失敗", it) }.flowOn(Dispatchers.IO) + .stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), emptyList()) @OptIn(ExperimentalCoroutinesApi::class) private val reactionCount = accountStore .observeCurrentAccount .filterNotNull() - .distinctUntilChanged() .flatMapLatest { ac -> reactionHistoryRepository.observeSumReactions(ac.normalizedInstanceUri) - .distinctUntilChanged() }.catch { logger.error("リアクション履歴の取得に失敗", it) }.flowOn(Dispatchers.IO) + .stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), emptyList()) @OptIn(ExperimentalCoroutinesApi::class) private val userSetting = accountStore.observeCurrentAccount .filterNotNull() - .distinctUntilChanged() .flatMapLatest { ac -> userEmojiConfigRepository.observeByInstanceDomain(ac.normalizedInstanceUri) - .distinctUntilChanged() }.catch { logger.error("ユーザーリアクション設定情報の取得に失敗", it) }.stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), emptyList()) @@ -198,7 +195,7 @@ data class EmojiPickerUiState( LevenshteinDistance(it.emoji.name, keyword) } - val tabLabels = emojiListItems.mapNotNull { + val tabHeaderLabels = emojiListItems.mapNotNull { (it as? EmojiListItemType.Header)?.label } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt index 4c623c252f..1e46cce196 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt @@ -203,10 +203,10 @@ class EmojiSelectionBinder( scope.launch { lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { emojiPickerViewModel.uiState.filterNot { - it.tabLabels.isEmpty() + it.tabHeaderLabels.isEmpty() }.collect { tabLayout.removeAllTabs() - val labels = it.tabLabels + val labels = it.tabHeaderLabels labels.map { val tab = tabLayout.newTab().apply { text = it.getString(context) From e418c55671d9e7531c30f1d534b441928b1e9588 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 13:23:36 +0900 Subject: [PATCH 167/432] =?UTF-8?q?feat:=20=E3=83=91=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=BC=E3=83=9E=E3=83=B3=E3=82=B9=E3=81=AE=E6=94=B9=E5=96=84?= =?UTF-8?q?=E3=82=92=E8=A1=8C=E3=81=84=E3=81=BE=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../delegate/CustomEmojiUpInsertDelegate.kt | 52 +++++++++++-------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt index 1285313c96..2d11fea0d6 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.data.infrastructure.emoji.delegate +import androidx.room.Transaction import net.pantasystem.milktea.data.infrastructure.emoji.db.CustomEmojiAliasRecord import net.pantasystem.milktea.data.infrastructure.emoji.db.CustomEmojiDAO import net.pantasystem.milktea.data.infrastructure.emoji.db.toRecord @@ -9,40 +10,49 @@ import javax.inject.Inject internal class CustomEmojiUpInsertDelegate @Inject constructor( private val customEmojiDAO: CustomEmojiDAO, ) { + + @Transaction suspend operator fun invoke(host: String, emojis: List) { val record = emojis.map { it.toRecord(host) } - val ids = customEmojiDAO.insertAll(record) + val exists = customEmojiDAO.findBy(host).associateBy { + it.emoji.name + } - ids.mapIndexed { index, id -> + val insertResults = customEmojiDAO.insertAll(record) + + val removedEmojis = emojis.mapNotNull { + exists[it.name]?.emoji + } + customEmojiDAO.deleteBy(removedEmojis) + + val ids = insertResults.mapIndexed { index, id -> if (id == -1L) { - customEmojiDAO.findBy(host, emojis[index].name).firstOrNull()?.let { record -> - customEmojiDAO.update(emojis[index].toRecord(host, record.emoji.id)) - customEmojiDAO.deleteAliasByEmojiId(record.emoji.id) - emojis[index].aliases?.map { - CustomEmojiAliasRecord( - emojiId = record.emoji.id, - it - ) - }?.let { - customEmojiDAO.insertAliases(it) - } - record.emoji.id - } + customEmojiDAO.findBy(host, emojis[index].name).firstOrNull()?.emoji?.id ?: -1 } else { - emojis[index].aliases?.filterNot { + id + } + } + + customEmojiDAO.deleteAliasByEmojiIds(ids.filterNot { it == -1L }) + val aliasRecords = emojis.mapIndexedNotNull { index, emoji -> + val id = ids[index] + if (id == -1L) { + null + } else { + emoji.aliases?.filterNot { it.isBlank() - }?.map { + }?.map { alias -> CustomEmojiAliasRecord( emojiId = id, - it + value = alias ) - }?.let { - customEmojiDAO.insertAliases(it) } - id } + }.flatten() + aliasRecords.chunked(500).map { + customEmojiDAO.insertAliases(it) } } } \ No newline at end of file From fcb871ef74f7ea7c89ebfe4caaf317752d7bbfa2 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 13:31:08 +0900 Subject: [PATCH 168/432] =?UTF-8?q?feat:=20DB=E3=81=AE=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=BB=E3=82=B9=E5=87=A6=E7=90=86=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/emoji/db/CustomEmojiDAO.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiDAO.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiDAO.kt index 5d987fabe3..aadc49e43f 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiDAO.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiDAO.kt @@ -32,12 +32,18 @@ interface CustomEmojiDAO { @Query("delete from custom_emoji_aliases where emojiId = :emojiId") suspend fun deleteAliasByEmojiId(emojiId: Long) + @Query("delete from custom_emoji_aliases where emojiId in (:emojiIds)") + suspend fun deleteAliasByEmojiIds(emojiIds: List) + @Query("delete from custom_emojis where emojiHost = :host") suspend fun deleteByHost(host: String) @Query("delete from custom_emojis where emojiHost = :host and name in (:names)") suspend fun deleteByHostAndNames(host: String, names: List) + @Delete + suspend fun deleteBy(list: List) + @Update suspend fun update(customEmoji: CustomEmojiRecord) } \ No newline at end of file From 9233114bb5d690d615ee0d6cfe3097592fc83db7 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 13:31:19 +0900 Subject: [PATCH 169/432] =?UTF-8?q?feat:=20host=E3=82=92=E4=BB=A3=E5=85=A5?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt index b1bfbb2909..cc3fb26426 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt @@ -81,6 +81,7 @@ data class CustomEmojiRelated( url = emoji.url, category = emoji.category, type = emoji.type, + host = emoji.emojiHost, aliases = aliases.map { it.value }, From b813071d76eb790cfc8232ff7c3b4382aa3e0a77 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 13:31:53 +0900 Subject: [PATCH 170/432] =?UTF-8?q?feat:=20=E5=87=A6=E7=90=86=E3=83=95?= =?UTF-8?q?=E3=83=A9=E3=82=B0=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../delegate/CustomEmojiUpInsertDelegate.kt | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt index 2d11fea0d6..b9f052b8b3 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt @@ -12,20 +12,22 @@ internal class CustomEmojiUpInsertDelegate @Inject constructor( ) { @Transaction - suspend operator fun invoke(host: String, emojis: List) { + suspend operator fun invoke(host: String, emojis: List, isReplace: Boolean = false) { val record = emojis.map { it.toRecord(host) } - val exists = customEmojiDAO.findBy(host).associateBy { - it.emoji.name - } val insertResults = customEmojiDAO.insertAll(record) - val removedEmojis = emojis.mapNotNull { - exists[it.name]?.emoji + if (isReplace) { + val exists = customEmojiDAO.findBy(host).associateBy { + it.emoji.name + } + val removedEmojis = emojis.mapNotNull { + exists[it.name]?.emoji + } + customEmojiDAO.deleteBy(removedEmojis) } - customEmojiDAO.deleteBy(removedEmojis) val ids = insertResults.mapIndexed { index, id -> if (id == -1L) { From 0354ea5d6734186db72bf696904c86aeeda87f24 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 13:32:03 +0900 Subject: [PATCH 171/432] =?UTF-8?q?=E3=83=86=E3=82=B9=E3=83=88=E3=82=B1?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomEmojiUpInsertDelegateTest.kt | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegateTest.kt b/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegateTest.kt index 7825a92ab8..857bc61a52 100644 --- a/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegateTest.kt +++ b/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegateTest.kt @@ -70,30 +70,39 @@ class CustomEmojiUpInsertDelegateTest { val existsData = listOf( Emoji( name = "test1", - aliases = listOf("a", "b", "c") + aliases = listOf("a", "b", "c"), + host = "misskey.pantasystem.com" ), Emoji( name = "test2", - aliases = listOf("a2`", "b2", "c2") + aliases = listOf("a2`", "b2", "c2"), + host = "misskey.pantasystem.com" ), ) val emojis = listOf( Emoji( name = "test1", - aliases = listOf("a", "b", "c") + aliases = listOf("a", "b", "c"), + host = "misskey.pantasystem.com" + ), Emoji( name = "test2", - aliases = listOf("a2`", "b2", "c2", "updated-alias") + aliases = listOf("a2`", "b2", "c2", "updated-alias"), + host = "misskey.pantasystem.com" + ), Emoji( name = "test3", - aliases = listOf("a3", "b3", "c3", "d4") + aliases = listOf("a3", "b3", "c3", "d4"), + host = "misskey.pantasystem.com" + ), Emoji( name = "test4", - aliases = listOf("") + aliases = listOf(""), + host = "misskey.pantasystem.com" ) ) From 5d401dd1a58a881cd440c7b069f868218179f20e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 13:32:10 +0900 Subject: [PATCH 172/432] =?UTF-8?q?feat:=20=E5=91=BC=E3=81=B3=E5=87=BA?= =?UTF-8?q?=E3=81=97=E6=96=B9=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt index 3a143cd760..cd6d620d6e 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt @@ -55,9 +55,9 @@ internal class CustomEmojiRepositoryImpl @Inject constructor( withContext(ioDispatcher) { val nodeInfo = nodeInfoRepository.find(host).getOrThrow() val emojis = fetch(nodeInfo).getOrThrow() - customEmojiDAO.deleteByHost(nodeInfo.host) + customEmojiCache.put(host, emojis) - upInsert(nodeInfo.host, emojis) + upInsert(nodeInfo.host, emojis, isReplace = true) } } From d7003ba311762a843d1208574898d27bec907332 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 15:16:09 +0900 Subject: [PATCH 173/432] fix: bug fix --- .../emoji/CustomEmojiRepositoryImpl.kt | 3 ++- .../emoji/delegate/CustomEmojiUpInsertDelegate.kt | 14 +------------- .../milktea/worker/meta/SyncMetaWorker.kt | 9 +++++---- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt index cd6d620d6e..a76285e702 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt @@ -57,7 +57,8 @@ internal class CustomEmojiRepositoryImpl @Inject constructor( val emojis = fetch(nodeInfo).getOrThrow() customEmojiCache.put(host, emojis) - upInsert(nodeInfo.host, emojis, isReplace = true) + customEmojiDAO.deleteByHost(host) + upInsert(nodeInfo.host, emojis) } } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt index b9f052b8b3..5cecdc2eb8 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt @@ -1,6 +1,5 @@ package net.pantasystem.milktea.data.infrastructure.emoji.delegate -import androidx.room.Transaction import net.pantasystem.milktea.data.infrastructure.emoji.db.CustomEmojiAliasRecord import net.pantasystem.milktea.data.infrastructure.emoji.db.CustomEmojiDAO import net.pantasystem.milktea.data.infrastructure.emoji.db.toRecord @@ -11,24 +10,13 @@ internal class CustomEmojiUpInsertDelegate @Inject constructor( private val customEmojiDAO: CustomEmojiDAO, ) { - @Transaction - suspend operator fun invoke(host: String, emojis: List, isReplace: Boolean = false) { + suspend operator fun invoke(host: String, emojis: List) { val record = emojis.map { it.toRecord(host) } val insertResults = customEmojiDAO.insertAll(record) - if (isReplace) { - val exists = customEmojiDAO.findBy(host).associateBy { - it.emoji.name - } - val removedEmojis = emojis.mapNotNull { - exists[it.name]?.emoji - } - customEmojiDAO.deleteBy(removedEmojis) - } - val ids = insertResults.mapIndexed { index, id -> if (id == -1L) { customEmojiDAO.findBy(host, emojis[index].name).firstOrNull()?.emoji?.id ?: -1 diff --git a/modules/worker/src/main/java/net/pantasystem/milktea/worker/meta/SyncMetaWorker.kt b/modules/worker/src/main/java/net/pantasystem/milktea/worker/meta/SyncMetaWorker.kt index d4e47afe04..c60d73b6e4 100644 --- a/modules/worker/src/main/java/net/pantasystem/milktea/worker/meta/SyncMetaWorker.kt +++ b/modules/worker/src/main/java/net/pantasystem/milktea/worker/meta/SyncMetaWorker.kt @@ -45,14 +45,15 @@ class SyncMetaWorker @AssistedInject constructor( instanceInfoService.sync(it.normalizedInstanceUri) } }.awaitAll() - }.forEach { result -> + }.count { result -> result.onFailure { logger.error("Fetch instance info failed", it) - }.getOrThrow() - } + }.isSuccess + } == accounts.size }.fold( onSuccess = { - Result.success() + if (it) Result.success() else Result.failure() + }, onFailure = { Result.failure() From a54b59ed6678a6470f51ef1a586cdd081e4ffa11 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 12 May 2023 16:27:44 +0900 Subject: [PATCH 174/432] fix test --- .../emoji/delegate/CustomEmojiUpInsertDelegateTest.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegateTest.kt b/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegateTest.kt index 857bc61a52..86d6db6422 100644 --- a/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegateTest.kt +++ b/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegateTest.kt @@ -56,7 +56,8 @@ class CustomEmojiUpInsertDelegateTest { emoji.copy( aliases = emoji.aliases?.filterNot { it.isBlank() - } + }, + host = "misskey.pantasystem.com" ) } Assert.assertEquals( From c0a3c70d60c9b12642a0d5686fa88988f82fab2b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 13 May 2023 22:44:15 +0900 Subject: [PATCH 175/432] feat: chunk batch delete --- .../emoji/delegate/CustomEmojiUpInsertDelegate.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt index 5cecdc2eb8..3531ab96af 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/delegate/CustomEmojiUpInsertDelegate.kt @@ -25,7 +25,10 @@ internal class CustomEmojiUpInsertDelegate @Inject constructor( } } - customEmojiDAO.deleteAliasByEmojiIds(ids.filterNot { it == -1L }) + ids.filterNot { it == -1L }.chunked(500).map { + customEmojiDAO.deleteAliasByEmojiIds(it) + } + val aliasRecords = emojis.mapIndexedNotNull { index, emoji -> val id = ids[index] if (id == -1L) { From 7b27e19a18100fca785644fa2f3c739401e8b045 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 15 May 2023 12:13:53 +0900 Subject: [PATCH 176/432] =?UTF-8?q?feat:=20=E5=85=88=E9=A0=AD=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=A6=E3=81=84=E3=81=AA=E3=81=84?= =?UTF-8?q?=E6=99=82=E3=81=AF=E3=82=B9=E3=83=88=E3=83=AA=E3=83=BC=E3=83=9F?= =?UTF-8?q?=E3=83=B3=E3=82=B0=E3=82=92=E8=87=AA=E5=8B=95=E7=9A=84=E3=81=AB?= =?UTF-8?q?=E5=81=9C=E6=AD=A2=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/timeline/TimelineFragment.kt | 6 +----- .../note/timeline/viewmodel/TimelineViewModel.kt | 12 +++++++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt index 1b943d0d3e..d34a795d6e 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt @@ -275,11 +275,7 @@ class TimelineFragment : Fragment(R.layout.fragment_swipe_refresh_recycler_view) mViewModel.loadOld() } - if (lm.findFirstVisibleItemPosition() <= 3) { - mViewModel.onVisibleFirst() - } - - mViewModel.onPositionChanged(lm.findFirstVisibleItemPosition()) + mViewModel.onScrollPositionChanged(lm.findFirstVisibleItemPosition()) } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt index 518375baaf..db26905c53 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt @@ -215,13 +215,19 @@ class TimelineViewModel @AssistedInject constructor( } } - fun onPositionChanged(position: Int) { + fun onScrollPositionChanged(firstVisiblePosition: Int) { + if (firstVisiblePosition <= 3) { + onVisibleFirst() + } else { + // NOTE: 先頭を表示していない時はストリーミングを停止する + timelineStore.suspendStreaming() + } viewModelScope.launch { - timelineStore.releaseUnusedPages(position) + timelineStore.releaseUnusedPages(firstVisiblePosition) } } - fun onVisibleFirst() { + private fun onVisibleFirst() { viewModelScope.launch { if (this@TimelineViewModel.isActive && !timelineStore.isActiveStreaming From 6ccbf7f3d44d3f26ff383533eb62c204f70fcef3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 15 May 2023 12:35:13 +0900 Subject: [PATCH 177/432] =?UTF-8?q?feat:=20=E7=94=BB=E5=83=8F=E3=81=8C?= =?UTF-8?q?=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=81=BE=E3=82=8C=E3=82=8B=E3=82=B5?= =?UTF-8?q?=E3=82=A4=E3=82=BA=E3=82=92=E5=88=B6=E9=99=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/reaction/NoteReactionViewHelper.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 714fda1947..a90dd1da12 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -13,6 +13,8 @@ import net.pantasystem.milktea.common_android_ui.BindingProvider import net.pantasystem.milktea.model.notes.reaction.LegacyReaction import net.pantasystem.milktea.model.notes.reaction.Reaction import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData +import kotlin.math.max +import kotlin.math.min object NoteReactionViewHelper { @@ -39,6 +41,7 @@ object NoteReactionViewHelper { GlideApp.with(reactionImageTypeView.context) .load(emoji.url ?: emoji.uri) + .override(min(max(reactionImageTypeView.height, 20), 40)) // FIXME: webpの場合エラーが発生してうまく表示できなくなってしまう // .fitCenter() .into(reactionImageTypeView) From fb986e5d2dba0e60948448d760e017c598d6f85c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 15 May 2023 14:23:55 +0900 Subject: [PATCH 178/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=AB=E3=82=A6=E3=83=B3=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E3=81=AE=E7=94=BB=E5=83=8F=E3=81=AE=E6=AF=94=E7=8E=87?= =?UTF-8?q?=E3=82=92=E7=B6=AD=E6=8C=81=E3=81=97=E3=81=AA=E3=81=8C=E3=82=89?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/reaction/NoteReactionViewHelper.kt | 4 +--- modules/features/note/src/main/res/layout/item_reaction.xml | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index a90dd1da12..5fe654efb2 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -13,8 +13,6 @@ import net.pantasystem.milktea.common_android_ui.BindingProvider import net.pantasystem.milktea.model.notes.reaction.LegacyReaction import net.pantasystem.milktea.model.notes.reaction.Reaction import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData -import kotlin.math.max -import kotlin.math.min object NoteReactionViewHelper { @@ -41,7 +39,7 @@ object NoteReactionViewHelper { GlideApp.with(reactionImageTypeView.context) .load(emoji.url ?: emoji.uri) - .override(min(max(reactionImageTypeView.height, 20), 40)) +// .override(min(max(reactionImageTypeView.height, 20), 120)) // FIXME: webpの場合エラーが発生してうまく表示できなくなってしまう // .fitCenter() .into(reactionImageTypeView) diff --git a/modules/features/note/src/main/res/layout/item_reaction.xml b/modules/features/note/src/main/res/layout/item_reaction.xml index e9b93364c3..4be11450d4 100644 --- a/modules/features/note/src/main/res/layout/item_reaction.xml +++ b/modules/features/note/src/main/res/layout/item_reaction.xml @@ -18,10 +18,11 @@ > Date: Mon, 15 May 2023 14:59:06 +0900 Subject: [PATCH 179/432] fix --- modules/features/note/src/main/res/layout/item_reaction.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/features/note/src/main/res/layout/item_reaction.xml b/modules/features/note/src/main/res/layout/item_reaction.xml index 4be11450d4..1227a5edde 100644 --- a/modules/features/note/src/main/res/layout/item_reaction.xml +++ b/modules/features/note/src/main/res/layout/item_reaction.xml @@ -19,6 +19,7 @@ Date: Mon, 15 May 2023 17:07:25 +0900 Subject: [PATCH 180/432] =?UTF-8?q?feat:=20=E3=82=B5=E3=82=A4=E3=82=BA?= =?UTF-8?q?=E3=81=AE=E4=B8=8A=E9=99=90=E3=82=92=E6=8C=87=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/features/note/src/main/res/layout/item_reaction.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/features/note/src/main/res/layout/item_reaction.xml b/modules/features/note/src/main/res/layout/item_reaction.xml index 1227a5edde..5b39fa39a4 100644 --- a/modules/features/note/src/main/res/layout/item_reaction.xml +++ b/modules/features/note/src/main/res/layout/item_reaction.xml @@ -18,13 +18,14 @@ > + android:scaleType="fitCenter" + android:layout_weight="1" /> Date: Mon, 15 May 2023 21:28:16 +0900 Subject: [PATCH 181/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=81=AE=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=81=AE=E3=82=B5=E3=82=A4=E3=82=BA=E3=82=92=E3=83=A1=E3=83=A2?= =?UTF-8?q?=E3=83=AA=E4=B8=8A=E3=81=AB=E3=82=AD=E3=83=A3=E3=83=83=E3=82=B7?= =?UTF-8?q?=E3=83=A5=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/reaction/ImageAspectCache.kt | 18 +++++++ .../note/reaction/NoteReactionViewHelper.kt | 47 +++++++++++++++++-- 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectCache.kt diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectCache.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectCache.kt new file mode 100644 index 0000000000..9442753452 --- /dev/null +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectCache.kt @@ -0,0 +1,18 @@ +package net.pantasystem.milktea.note.reaction + +object ImageAspectCache { + + private var cache = mutableMapOf() + + fun put(url: String?, aspect: Float) { + url?: return + synchronized(this) { + cache[url] = aspect + } + } + + fun get(url: String?): Float? { + url ?: return null + return cache[url] + } +} \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 5fe654efb2..a2c4e04f73 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -1,11 +1,16 @@ package net.pantasystem.milktea.note.reaction import android.content.Context +import android.graphics.drawable.Drawable import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.databinding.BindingAdapter +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target import dagger.hilt.android.EntryPointAccessors import net.pantasystem.milktea.common.glide.GlideApp import net.pantasystem.milktea.common_android.ui.VisibilityHelper.setMemoVisibility @@ -14,6 +19,7 @@ import net.pantasystem.milktea.model.notes.reaction.LegacyReaction import net.pantasystem.milktea.model.notes.reaction.Reaction import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData + object NoteReactionViewHelper { @JvmStatic @@ -39,9 +45,40 @@ object NoteReactionViewHelper { GlideApp.with(reactionImageTypeView.context) .load(emoji.url ?: emoji.uri) + .let { + val imageAspectRatio = ImageAspectCache.get(emoji.url ?: emoji.uri) + if (imageAspectRatio == null) { + it + } else { + val metrics = context.resources.displayMetrics + val imageViewHeightPx = 20 * metrics.density + it.override((imageViewHeightPx * imageAspectRatio).toInt()) + } + } // .override(min(max(reactionImageTypeView.height, 20), 120)) - // FIXME: webpの場合エラーが発生してうまく表示できなくなってしまう -// .fitCenter() + .addListener(object : RequestListener { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean, + ): Boolean { + return false + } + + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean, + ): Boolean { + resource ?: return false + val imageAspectRatio: Float = resource.intrinsicWidth.toFloat() / resource.intrinsicHeight + ImageAspectCache.put(emoji.url ?: emoji.uri, imageAspectRatio) + return false + } + }) .into(reactionImageTypeView) } } @@ -53,7 +90,7 @@ object NoteReactionViewHelper { reactionTextTypeView: TextView, reactionImageTypeView: ImageView, reaction: String, - note: PlaneNoteViewData + note: PlaneNoteViewData, ) { val entryPoint = EntryPointAccessors.fromApplication( context.applicationContext, @@ -79,10 +116,10 @@ object NoteReactionViewHelper { GlideApp.with(reactionImageTypeView.context) .load(emoji.url ?: emoji.uri) - // FIXME: webpの場合エラーが発生してうまく表示できなくなってしまう -// .fitCenter() .into(reactionImageTypeView) } } + + } \ No newline at end of file From 11c26c4baeeff1290f950331abef981b7ca53946 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 15 May 2023 22:03:35 +0900 Subject: [PATCH 182/432] =?UTF-8?q?feat:=20=E5=90=8D=E7=A7=B0=E3=82=92?= =?UTF-8?q?=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{ImageAspectCache.kt => ImageAspectRatioCache.kt} | 2 +- .../milktea/note/reaction/NoteReactionViewHelper.kt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/{ImageAspectCache.kt => ImageAspectRatioCache.kt} (91%) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectCache.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectRatioCache.kt similarity index 91% rename from modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectCache.kt rename to modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectRatioCache.kt index 9442753452..04666c58db 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectCache.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectRatioCache.kt @@ -1,6 +1,6 @@ package net.pantasystem.milktea.note.reaction -object ImageAspectCache { +object ImageAspectRatioCache { private var cache = mutableMapOf() diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index a2c4e04f73..c61e20ede6 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -46,7 +46,7 @@ object NoteReactionViewHelper { GlideApp.with(reactionImageTypeView.context) .load(emoji.url ?: emoji.uri) .let { - val imageAspectRatio = ImageAspectCache.get(emoji.url ?: emoji.uri) + val imageAspectRatio = ImageAspectRatioCache.get(emoji.url ?: emoji.uri) if (imageAspectRatio == null) { it } else { @@ -75,7 +75,7 @@ object NoteReactionViewHelper { ): Boolean { resource ?: return false val imageAspectRatio: Float = resource.intrinsicWidth.toFloat() / resource.intrinsicHeight - ImageAspectCache.put(emoji.url ?: emoji.uri, imageAspectRatio) + ImageAspectRatioCache.put(emoji.url ?: emoji.uri, imageAspectRatio) return false } }) From 046d37a8ce30c6ca2e22d29180d11a6ce36fa738 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 15 May 2023 22:05:32 +0900 Subject: [PATCH 183/432] =?UTF-8?q?feat:=20=E5=85=B1=E9=80=9A=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/reaction/NoteReactionViewHelper.kt | 29 +--------------- .../SaveImageAspectRequestListener.kt | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 28 deletions(-) create mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index c61e20ede6..1dc1384d67 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -1,16 +1,11 @@ package net.pantasystem.milktea.note.reaction import android.content.Context -import android.graphics.drawable.Drawable import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView import androidx.databinding.BindingAdapter -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.Target import dagger.hilt.android.EntryPointAccessors import net.pantasystem.milktea.common.glide.GlideApp import net.pantasystem.milktea.common_android.ui.VisibilityHelper.setMemoVisibility @@ -56,29 +51,7 @@ object NoteReactionViewHelper { } } // .override(min(max(reactionImageTypeView.height, 20), 120)) - .addListener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean, - ): Boolean { - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean, - ): Boolean { - resource ?: return false - val imageAspectRatio: Float = resource.intrinsicWidth.toFloat() / resource.intrinsicHeight - ImageAspectRatioCache.put(emoji.url ?: emoji.uri, imageAspectRatio) - return false - } - }) + .addListener(SaveImageAspectRequestListener(emoji.url ?: emoji.uri)) .into(reactionImageTypeView) } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt new file mode 100644 index 0000000000..28b8f42fef --- /dev/null +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt @@ -0,0 +1,33 @@ +package net.pantasystem.milktea.note.reaction + +import android.graphics.drawable.Drawable +import com.bumptech.glide.load.DataSource +import com.bumptech.glide.load.engine.GlideException +import com.bumptech.glide.request.RequestListener +import com.bumptech.glide.request.target.Target + +class SaveImageAspectRequestListener( + val url: String? +) : RequestListener { + override fun onLoadFailed( + e: GlideException?, + model: Any?, + target: Target?, + isFirstResource: Boolean, + ): Boolean { + return false + } + + override fun onResourceReady( + resource: Drawable?, + model: Any?, + target: Target?, + dataSource: DataSource?, + isFirstResource: Boolean, + ): Boolean { + resource ?: return false + val imageAspectRatio: Float = resource.intrinsicWidth.toFloat() / resource.intrinsicHeight + ImageAspectRatioCache.put(url, imageAspectRatio) + return false + } +} \ No newline at end of file From a50526e59b08ca1408e2e169ed769152be40244d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 15 May 2023 22:06:24 +0900 Subject: [PATCH 184/432] =?UTF-8?q?feat:=20=E3=83=91=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=BC=E3=83=9E=E3=83=B3=E3=82=B9=E6=94=B9=E5=96=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/reaction/ImageAspectRatioCache.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectRatioCache.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectRatioCache.kt index 04666c58db..d49d273ece 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectRatioCache.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ImageAspectRatioCache.kt @@ -1,18 +1,20 @@ package net.pantasystem.milktea.note.reaction +import android.util.SparseArray + object ImageAspectRatioCache { - private var cache = mutableMapOf() + private var cache = SparseArray() fun put(url: String?, aspect: Float) { url?: return synchronized(this) { - cache[url] = aspect + cache[url.hashCode()] = aspect } } fun get(url: String?): Float? { url ?: return null - return cache[url] + return cache[url.hashCode()] } } \ No newline at end of file From cfd80903e13e6165eb5587ac4dfaa4f71f67aa96 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 16 May 2023 22:12:14 +0900 Subject: [PATCH 185/432] feat: calckey v14 --- .../milktea/api/misskey/MisskeyAPIServiceBuilder.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPIServiceBuilder.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPIServiceBuilder.kt index de04dfe15d..dc9e7822db 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPIServiceBuilder.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPIServiceBuilder.kt @@ -85,10 +85,10 @@ class MisskeyAPIServiceBuilder @Inject constructor( val diff = retrofit.create(MisskeyAPIV10Diff::class.java) return MisskeyAPIV10(build(baseUrl), diff) } - version.isInRange(Version.Major.V_11) || version.isInRange(Version.Major.V_12) || version.isInRange(Version.Major.V_13) ->{ + version >= Version("11") ->{ val baseAPI = build(baseUrl) val misskeyAPIV11Diff = retrofit.create(MisskeyAPIV11Diff::class.java) - if(version.isInRange(Version.Major.V_12) || version.isInRange(Version.Major.V_13)){ + if(version >= Version("12")){ val misskeyAPI12DiffImpl = retrofit.create(MisskeyAPIV12Diff::class.java) if(version >= Version("12.75.0")) { val misskeyAPIV1275Diff = retrofit.create(MisskeyAPIV1275Diff::class.java) From 307d76d452e54b6e3fbb3e36cd059bdc498dc5a1 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 09:25:05 +0900 Subject: [PATCH 186/432] fix --- .../note/emojis/EmojiPickerFragment.kt | 56 ++++++++++--------- 1 file changed, 29 insertions(+), 27 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt index 1e46cce196..c1b46a4be4 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt @@ -9,10 +9,7 @@ import android.widget.EditText import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleOwner -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle +import androidx.lifecycle.* import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView import com.ahmadhamwi.tabsync.TabbedListMediator @@ -22,7 +19,10 @@ import com.google.android.material.tabs.TabLayout import com.wada811.databinding.dataBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.filterNot +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import net.pantasystem.milktea.common_android.resource.convertDp2Px import net.pantasystem.milktea.model.notes.reaction.LegacyReaction @@ -200,31 +200,33 @@ class EmojiSelectionBinder( } var tabbedListMediator: TabbedListMediator? = null - scope.launch { - lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { - emojiPickerViewModel.uiState.filterNot { - it.tabHeaderLabels.isEmpty() - }.collect { - tabLayout.removeAllTabs() - val labels = it.tabHeaderLabels - labels.map { - val tab = tabLayout.newTab().apply { - text = it.getString(context) - } - tabLayout.addTab(tab) - } - tabbedListMediator?.detach() - tabbedListMediator = - TabbedListMediator(recyclerView, tabLayout, it.emojiListItems.mapIndexedNotNull { index, emojiListItemType -> - when(emojiListItemType) { - is EmojiListItemType.EmojiItem -> null - is EmojiListItemType.Header -> index - } - }) - tabbedListMediator?.attach() + emojiPickerViewModel.uiState.filterNot { + it.tabHeaderLabels.isEmpty() + }.distinctUntilChangedBy { + it.emojiListItems + }.onEach { + tabLayout.removeAllTabs() + val labels = it.tabHeaderLabels + labels.forEach { + val tab = tabLayout.newTab().apply { + text = it.getString(context) } + tabLayout.addTab(tab) } - } + tabbedListMediator?.detach() + tabbedListMediator = TabbedListMediator( + recyclerView, + tabLayout, + it.emojiListItems.mapIndexedNotNull { index, emojiListItemType -> + when(emojiListItemType) { + is EmojiListItemType.EmojiItem -> null + is EmojiListItemType.Header -> index + } + } + ) + tabbedListMediator?.attach() + recyclerView.scrollToPosition(0) + }.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.RESUMED).launchIn(scope) searchWordTextField.setOnEditorActionListener { _, actionId, _ -> From 7106c63fd8a18129136eab59ca9e167035618e92 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 13:16:14 +0900 Subject: [PATCH 187/432] =?UTF-8?q?feat:=20api=E7=B5=8C=E7=94=B1=E3=81=A7?= =?UTF-8?q?=E3=82=AA=E3=83=B3=E3=83=A9=E3=82=A4=E3=83=B3=E3=83=A6=E3=83=BC?= =?UTF-8?q?=E3=82=B6=E3=83=BC=E6=95=B0=E3=82=92=E5=8F=96=E5=BE=97=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/api/misskey/MisskeyAPI.kt | 4 ++++ .../milktea/api/misskey/online/user/OnlineUserCount.kt | 8 ++++++++ 2 files changed, 12 insertions(+) create mode 100644 modules/api/src/main/java/net/pantasystem/milktea/api/misskey/online/user/OnlineUserCount.kt diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt index 4f5f43a155..3abd6dc14a 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt @@ -22,6 +22,7 @@ import net.pantasystem.milktea.api.misskey.notes.translation.Translate import net.pantasystem.milktea.api.misskey.notes.translation.TranslationResult import net.pantasystem.milktea.api.misskey.notification.NotificationDTO import net.pantasystem.milktea.api.misskey.notification.NotificationRequest +import net.pantasystem.milktea.api.misskey.online.user.OnlineUserCount import net.pantasystem.milktea.api.misskey.register.Subscription import net.pantasystem.milktea.api.misskey.register.UnSubscription import net.pantasystem.milktea.api.misskey.register.WebClientBaseRequest @@ -318,4 +319,7 @@ interface MisskeyAPI { @POST("api/hashtags/trend") suspend fun getTrendingHashtags(@Body body: EmptyRequest): Response> + + @POST("api/get-online-users-count") + suspend fun getOnlineUsersCount(@Body body: EmptyRequest): Response } \ No newline at end of file diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/online/user/OnlineUserCount.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/online/user/OnlineUserCount.kt new file mode 100644 index 0000000000..83b541a0a4 --- /dev/null +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/online/user/OnlineUserCount.kt @@ -0,0 +1,8 @@ +package net.pantasystem.milktea.api.misskey.online.user + +import kotlinx.serialization.SerialName + +@kotlinx.serialization.Serializable +data class OnlineUserCount( + @SerialName("count") val count: Int +) \ No newline at end of file From 45e90be6b6483be6824064792c8b68b8316b3506 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 13:18:13 +0900 Subject: [PATCH 188/432] =?UTF-8?q?feat:=20interface=E3=82=92=E4=BD=9C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/user/online/OnlineUserCountRepository.kt | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/user/online/OnlineUserCountRepository.kt diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/user/online/OnlineUserCountRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/user/online/OnlineUserCountRepository.kt new file mode 100644 index 0000000000..5efeeb6e7a --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/user/online/OnlineUserCountRepository.kt @@ -0,0 +1,10 @@ +package net.pantasystem.milktea.model.user.online + +interface OnlineUserCountRepository { + suspend fun find(accountId: Long): Result +} + +sealed interface OnlineUserCountResult { + object Unknown : OnlineUserCountResult + data class Success(val count: Int) : OnlineUserCountResult +} \ No newline at end of file From 3a3dda2e8422143a7970d8c26e26cbe6923b50bd Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 13:23:10 +0900 Subject: [PATCH 189/432] =?UTF-8?q?feat:=20=E5=AE=9F=E8=A3=85=E3=82=AF?= =?UTF-8?q?=E3=83=A9=E3=82=B9=E3=82=92=E4=BD=9C=E6=88=90=E3=81=97=E9=96=A2?= =?UTF-8?q?=E4=BF=82=E3=82=92Module=E3=81=AB=E7=99=BB=E9=8C=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/di/module/InstanceInfoModule.kt | 6 ++++ .../count/OnlineUserCountRepositoryImpl.kt | 30 +++++++++++++++++++ .../user/count}/OnlineUserCountRepository.kt | 2 +- 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/online/user/count/OnlineUserCountRepositoryImpl.kt rename modules/model/src/main/java/net/pantasystem/milktea/model/{user/online => instance/online/user/count}/OnlineUserCountRepository.kt (79%) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/InstanceInfoModule.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/InstanceInfoModule.kt index c65e4615b4..f187d5e8fb 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/InstanceInfoModule.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/InstanceInfoModule.kt @@ -6,8 +6,10 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import net.pantasystem.milktea.data.infrastructure.instance.FeatureEnablesImpl import net.pantasystem.milktea.data.infrastructure.instance.InstanceInfoRepositoryImpl +import net.pantasystem.milktea.data.infrastructure.instance.online.user.count.OnlineUserCountRepositoryImpl import net.pantasystem.milktea.model.instance.FeatureEnables import net.pantasystem.milktea.model.instance.InstanceInfoRepository +import net.pantasystem.milktea.model.instance.online.user.count.OnlineUserCountRepository import javax.inject.Singleton @InstallIn(SingletonComponent::class) @@ -21,4 +23,8 @@ abstract class InstanceInfoBindModule { @Binds @Singleton abstract fun bindFeatureEnables(impl: FeatureEnablesImpl): FeatureEnables + + @Binds + @Singleton + abstract fun bindOnlineUserCountRepository(impl: OnlineUserCountRepositoryImpl): OnlineUserCountRepository } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/online/user/count/OnlineUserCountRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/online/user/count/OnlineUserCountRepositoryImpl.kt new file mode 100644 index 0000000000..0b81ad99c9 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/online/user/count/OnlineUserCountRepositoryImpl.kt @@ -0,0 +1,30 @@ +package net.pantasystem.milktea.data.infrastructure.instance.online.user.count + +import net.pantasystem.milktea.api.misskey.EmptyRequest +import net.pantasystem.milktea.common.runCancellableCatching +import net.pantasystem.milktea.common.throwIfHasError +import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.instance.online.user.count.OnlineUserCountRepository +import net.pantasystem.milktea.model.instance.online.user.count.OnlineUserCountResult +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class OnlineUserCountRepositoryImpl @Inject constructor( + val accountRepository: AccountRepository, + val misskeyAPIProvider: MisskeyAPIProvider, +): OnlineUserCountRepository { + override suspend fun find(accountId: Long): Result = runCancellableCatching { + val account = accountRepository.get(accountId).getOrThrow() + when(account.instanceType) { + Account.InstanceType.MISSKEY -> { + val res = misskeyAPIProvider.get(account).getOnlineUsersCount(EmptyRequest) + .throwIfHasError().body() + OnlineUserCountResult.Success(requireNotNull(res?.count)) + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> OnlineUserCountResult.Unknown + } + } +} \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/user/online/OnlineUserCountRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/online/user/count/OnlineUserCountRepository.kt similarity index 79% rename from modules/model/src/main/java/net/pantasystem/milktea/model/user/online/OnlineUserCountRepository.kt rename to modules/model/src/main/java/net/pantasystem/milktea/model/instance/online/user/count/OnlineUserCountRepository.kt index 5efeeb6e7a..2485ed5254 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/user/online/OnlineUserCountRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/online/user/count/OnlineUserCountRepository.kt @@ -1,4 +1,4 @@ -package net.pantasystem.milktea.model.user.online +package net.pantasystem.milktea.model.instance.online.user.count interface OnlineUserCountRepository { suspend fun find(accountId: Long): Result From bd1971d1e015df78c8e7a3807e778e6d72f89d73 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 13:28:37 +0900 Subject: [PATCH 190/432] =?UTF-8?q?feat:=20=E3=82=A4=E3=83=B3=E3=82=B9?= =?UTF-8?q?=E3=82=BF=E3=83=B3=E3=82=B9=E3=81=AE=E6=83=85=E5=A0=B1=E3=82=84?= =?UTF-8?q?=E3=82=AA=E3=83=B3=E3=83=A9=E3=82=A4=E3=83=B3=E6=95=B0=E3=82=92?= =?UTF-8?q?=E5=8F=96=E5=BE=97=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/account/AccountScreenViewModel.kt | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountScreenViewModel.kt b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountScreenViewModel.kt index 78e85a4043..4ce1d6bd50 100644 --- a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountScreenViewModel.kt +++ b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountScreenViewModel.kt @@ -8,6 +8,10 @@ import kotlinx.coroutines.flow.* import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.instance.InstanceInfo +import net.pantasystem.milktea.model.instance.InstanceInfoRepository +import net.pantasystem.milktea.model.instance.online.user.count.OnlineUserCountRepository +import net.pantasystem.milktea.model.instance.online.user.count.OnlineUserCountResult import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserDataSource import net.pantasystem.milktea.model.user.UserRepository @@ -18,6 +22,8 @@ class AccountScreenViewModel @Inject constructor( accountStore: AccountStore, private val userRepository: UserRepository, private val userDataSource: UserDataSource, + private val onlineUserCountRepository: OnlineUserCountRepository, + private val instanceInfoRepository: InstanceInfoRepository, private val loggerFactory: Logger.Factory, ) : ViewModel() { @@ -40,8 +46,22 @@ class AccountScreenViewModel @Inject constructor( null ) - val uiState = combine(currentAccount, user) { a, u -> - AccountUiState(a, u) + @OptIn(ExperimentalCoroutinesApi::class) + private val instanceInfo = accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { + instanceInfoRepository.observeByHost(it.getHost()) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + private val onlineUserCount = accountStore.observeCurrentAccount.filterNotNull().map { + onlineUserCountRepository.find(it.accountId).getOrNull() + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + val uiState = combine(currentAccount, user, instanceInfo, onlineUserCount) { a, u, info, count -> + AccountUiState( + currentAccount = a, + userInfo = u, + instanceInfo = info, + onlineUserCount = count, + ) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), AccountUiState()) init { @@ -56,4 +76,6 @@ class AccountScreenViewModel @Inject constructor( data class AccountUiState( val currentAccount: Account? = null, val userInfo: User? = null, + val instanceInfo: InstanceInfo? = null, + val onlineUserCount: OnlineUserCountResult? = null, ) \ No newline at end of file From 46997ba1c5dd6222f053ebc1468309a1079af9bd Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 14:35:15 +0900 Subject: [PATCH 191/432] =?UTF-8?q?feat:=20iconUrl=E3=81=8Cnull=E3=81=AE?= =?UTF-8?q?=E6=99=82=E3=81=AFfavicon=E3=82=92=E5=8F=96=E3=81=A3=E3=81=A6?= =?UTF-8?q?=E3=81=8F=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/model/instance/InstanceInfoType.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt index 894e0ea116..e0168a81ab 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/instance/InstanceInfoType.kt @@ -10,7 +10,7 @@ sealed interface InstanceInfoType { return when(this) { is Mastodon -> "https://${info.uri}/favicon.ico" is Pleroma -> "https://${info.uri}/favicon.ico" - is Misskey -> meta.iconUrl + is Misskey -> meta.iconUrl ?: "${meta.uri}/favicon.ico" } } @@ -51,6 +51,14 @@ sealed interface InstanceInfoType { } } + val name: String get() { + return when(this) { + is Mastodon -> info.title + is Misskey -> meta.name ?: "Misskey" + is Pleroma -> info.title + } + } + val canMultipleReaction: Boolean get() { return maxReactionsPerAccount > 1 } From 6695be72188f6f64defe3f6280279adff9701d4e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 14:35:29 +0900 Subject: [PATCH 192/432] fix --- .../milktea/account/AccountScreenViewModel.kt | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountScreenViewModel.kt b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountScreenViewModel.kt index 4ce1d6bd50..d2c9474e8b 100644 --- a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountScreenViewModel.kt +++ b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountScreenViewModel.kt @@ -8,8 +8,8 @@ import kotlinx.coroutines.flow.* import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.model.account.Account -import net.pantasystem.milktea.model.instance.InstanceInfo -import net.pantasystem.milktea.model.instance.InstanceInfoRepository +import net.pantasystem.milktea.model.instance.InstanceInfoService +import net.pantasystem.milktea.model.instance.InstanceInfoType import net.pantasystem.milktea.model.instance.online.user.count.OnlineUserCountRepository import net.pantasystem.milktea.model.instance.online.user.count.OnlineUserCountResult import net.pantasystem.milktea.model.user.User @@ -23,7 +23,7 @@ class AccountScreenViewModel @Inject constructor( private val userRepository: UserRepository, private val userDataSource: UserDataSource, private val onlineUserCountRepository: OnlineUserCountRepository, - private val instanceInfoRepository: InstanceInfoRepository, + private val instanceInfoService: InstanceInfoService, private val loggerFactory: Logger.Factory, ) : ViewModel() { @@ -46,13 +46,16 @@ class AccountScreenViewModel @Inject constructor( null ) - @OptIn(ExperimentalCoroutinesApi::class) - private val instanceInfo = accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { - instanceInfoRepository.observeByHost(it.getHost()) + private val instanceInfo = accountStore.observeCurrentAccount.filterNotNull().map { + instanceInfoService.find(it.normalizedInstanceUri).getOrThrow() + }.catch { + logger.error("インスタンス情報の取得に失敗", it) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) private val onlineUserCount = accountStore.observeCurrentAccount.filterNotNull().map { - onlineUserCountRepository.find(it.accountId).getOrNull() + onlineUserCountRepository.find(it.accountId).onFailure { e -> + logger.error("オンラインユーザー数の取得に失敗", e) + }.getOrNull() }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) val uiState = combine(currentAccount, user, instanceInfo, onlineUserCount) { a, u, info, count -> @@ -76,6 +79,6 @@ class AccountScreenViewModel @Inject constructor( data class AccountUiState( val currentAccount: Account? = null, val userInfo: User? = null, - val instanceInfo: InstanceInfo? = null, + val instanceInfo: InstanceInfoType? = null, val onlineUserCount: OnlineUserCountResult? = null, ) \ No newline at end of file From a39b40145dd8ac0497513673fa39eb833567982b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 14:41:25 +0900 Subject: [PATCH 193/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_compose/src/main/res/values-ja-rJP/strings.xml | 2 ++ modules/common_compose/src/main/res/values-zh/strings.xml | 2 ++ modules/common_compose/src/main/res/values/strings.xml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/modules/common_compose/src/main/res/values-ja-rJP/strings.xml b/modules/common_compose/src/main/res/values-ja-rJP/strings.xml index 45f1e7dce2..ff2a96983f 100644 --- a/modules/common_compose/src/main/res/values-ja-rJP/strings.xml +++ b/modules/common_compose/src/main/res/values-ja-rJP/strings.xml @@ -15,4 +15,6 @@ 秒前 ファイル名を変更 キャプションを編集 + インスタンス + %d人がオンライン \ No newline at end of file diff --git a/modules/common_compose/src/main/res/values-zh/strings.xml b/modules/common_compose/src/main/res/values-zh/strings.xml index f7be2b65d8..70e668a0bc 100644 --- a/modules/common_compose/src/main/res/values-zh/strings.xml +++ b/modules/common_compose/src/main/res/values-zh/strings.xml @@ -15,4 +15,6 @@ 重新命名文件 编辑标题 + Instance + %d poeple online \ No newline at end of file diff --git a/modules/common_compose/src/main/res/values/strings.xml b/modules/common_compose/src/main/res/values/strings.xml index 1d6e3fb0d4..e5b8a0e579 100644 --- a/modules/common_compose/src/main/res/values/strings.xml +++ b/modules/common_compose/src/main/res/values/strings.xml @@ -16,4 +16,6 @@ S Edit name Edit caption + Instance + %d poeple online \ No newline at end of file From f5705ed4d86cd42b9a78c9ca70d44ae78dbd701a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 18 May 2023 14:42:47 +0900 Subject: [PATCH 194/432] =?UTF-8?q?feat:=20=E7=B8=81=E4=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/account/AccountFragment.kt | 137 ++++++++++++++---- 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountFragment.kt b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountFragment.kt index b9c7f5bfc6..079c8250c9 100644 --- a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountFragment.kt +++ b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountFragment.kt @@ -4,12 +4,13 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.Image +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.MaterialTheme import androidx.compose.material.Scaffold import androidx.compose.material.Text import androidx.compose.runtime.collectAsState @@ -17,17 +18,23 @@ import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.rememberNestedScrollInteropConnection import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import coil.compose.rememberAsyncImagePainter import com.google.android.material.composethemeadapter.MdcTheme import dagger.hilt.android.AndroidEntryPoint import net.pantasystem.milktea.common_viewmodel.CurrentPageType import net.pantasystem.milktea.common_viewmodel.CurrentPageableTimelineViewModel +import net.pantasystem.milktea.model.instance.online.user.count.OnlineUserCountResult import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.user.activity.FollowFollowerActivity @@ -43,7 +50,7 @@ class AccountFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View { return ComposeView(requireContext()).apply { setContent { @@ -58,36 +65,57 @@ class AccountFragment : Fragment() { .nestedScroll(rememberNestedScrollInteropConnection()) ) { item { - when(val account = uiState.currentAccount) { + when (val account = uiState.currentAccount) { null -> { Text(stringResource(id = R.string.unauthorized_error)) } else -> { - when(val user = uiState.userInfo) { + when (val user = uiState.userInfo) { is User.Detail -> { - AccountInfoLayout( - isUserNameMain = false, - userDetail = user, - account = account, - onFollowerCountButtonClicked = { - requireActivity().startActivity( - FollowFollowerActivity.newIntent( - requireContext(), - user.id, - isFollowing = false - ) + Column( + Modifier + .fillMaxWidth() + .padding( + horizontal = 14.dp, + vertical = 8.dp ) - }, - onFollowingCountButtonClicked = { - requireActivity().startActivity( - FollowFollowerActivity.newIntent( - requireContext(), - user.id, - isFollowing = true + ) { + Text(stringResource(id = R.string.account)) + Box( + Modifier + + .border( + 1.dp, + MaterialTheme.colors.primary, + RoundedCornerShape(8.dp) ) + ) { + AccountInfoLayout( + isUserNameMain = false, + userDetail = user, + account = account, + onFollowerCountButtonClicked = { + requireActivity().startActivity( + FollowFollowerActivity.newIntent( + requireContext(), + user.id, + isFollowing = false + ) + ) + }, + onFollowingCountButtonClicked = { + requireActivity().startActivity( + FollowFollowerActivity.newIntent( + requireContext(), + user.id, + isFollowing = true + ) + ) + } ) } - ) + } + } else -> { Box( @@ -100,6 +128,63 @@ class AccountFragment : Fragment() { } } } + + } + + item { + + when (val info = uiState.instanceInfo) { + null -> Unit + else -> { + Column( + Modifier + .fillMaxWidth() + .padding(horizontal = 14.dp, vertical = 8.dp) + ) { + Text(stringResource(id = R.string.instance)) + Box( + Modifier + .border( + 1.dp, + MaterialTheme.colors.primary, + RoundedCornerShape(8.dp) + ) + ) { + Column( + Modifier + .fillMaxWidth() + .padding(8.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Image( + rememberAsyncImagePainter(info.iconUrl), + contentDescription = null, + modifier = Modifier.size(52.dp).clip( + RoundedCornerShape(8.dp) + ) + ) + Text( + info.name, + fontWeight = FontWeight.Bold, + fontSize = 20.sp + ) + when (val count = uiState.onlineUserCount) { + is OnlineUserCountResult.Success -> { + Text( + stringResource( + id = R.string.online_user_count_message, + count.count + ) + ) + } + else -> Unit + } + } + } + } + } + } + } } } From 1741fd946b701368c414c41a001436c502cffe0c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 11:03:18 +0900 Subject: [PATCH 195/432] =?UTF-8?q?feat:=20aspect=20ratio=E3=82=92?= =?UTF-8?q?=E4=BF=9D=E5=AD=98=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AE?= =?UTF-8?q?=E6=A9=9F=E6=A7=8B=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../di/module/ObjectBoxModule.kt | 2 +- .../ui/text/CustomEmojiDecorator.kt | 7 +- .../milktea/common_android_ui/MFMDecorator.kt | 2 +- modules/data/objectbox-models/default.json | 30 +++++++- .../data/objectbox-models/default.json.bak | 49 ++++++++++++- .../data/di/module/CustomEmojiModule.kt | 6 ++ .../CustomEmojiAspectRatioDataSourceImpl.kt | 73 +++++++++++++++++++ .../emoji/CustomEmojiAspectRatioRecord.kt | 24 ++++++ .../note/reaction/NoteReactionViewHelper.kt | 4 +- .../emoji/CustomEmojiAspectRatioDataSource.kt | 17 +++++ .../pantasystem/milktea/model/emoji/Emoji.kt | 5 +- 11 files changed, 205 insertions(+), 14 deletions(-) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioRecord.kt create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioDataSource.kt diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt b/app/src/main/java/jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt index be47f19170..3cc65476a8 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/di/module/ObjectBoxModule.kt @@ -7,7 +7,7 @@ import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import io.objectbox.BoxStore -import net.pantasystem.milktea.data.infrastructure.notes.MyObjectBox +import net.pantasystem.milktea.data.infrastructure.MyObjectBox import javax.inject.Singleton @Module diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt index 528d6d124f..3d5378fa0a 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt @@ -9,7 +9,6 @@ import net.pantasystem.milktea.model.emoji.CustomEmojiParser import net.pantasystem.milktea.model.emoji.Emoji import net.pantasystem.milktea.model.emoji.EmojiResolvedType import net.pantasystem.milktea.model.instance.HostWithVersion -import kotlin.math.min class CustomEmojiDecorator { @@ -37,7 +36,7 @@ class CustomEmojiDecorator { GlideApp.with(view) .asDrawable() .load(it.result.getUrl(accountHost)) - .override(min(view.textSize.toInt(), 20)) + .override(view.textSize.toInt()) .into(span.target) builder.setSpan(span, it.start, it.end, 0) } @@ -57,7 +56,7 @@ class CustomEmojiDecorator { val span = DrawableEmojiSpan(emojiAdapter, it.result.getUrl(accountHost)) GlideApp.with(view) .asDrawable() - .override(min(view.textSize.toInt(), 20)) + .override(view.textSize.toInt()) .load(it.result.getUrl(accountHost)) .into(span.target) builder.setSpan(span, it.start, it.end, 0) @@ -79,7 +78,7 @@ class CustomEmojiDecorator { GlideApp.with(view) .asDrawable() .load(it.result.getUrl(accountHost)) - .override(min(view.textSize.toInt(), 20)) + .override(view.textSize.toInt()) .into(span.target) builder.setSpan(span, it.start, it.end, 0) } diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt index ccbdc43b39..e36993cddb 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt @@ -313,7 +313,7 @@ object MFMDecorator { spannableString.setSpan(emojiSpan, skippedEmoji.start, skippedEmoji.end, 0) GlideApp.with(textView) .load(emojiElement.emoji.url) - .override(min(max(textView.textSize.toInt(), 10), 20)) + .override(min(max(textView.textSize.toInt(), 10), 128)) .addListener(object : RequestListener { override fun onLoadFailed( e: GlideException?, diff --git a/modules/data/objectbox-models/default.json b/modules/data/objectbox-models/default.json index e9d3071665..0dc0e9d71c 100644 --- a/modules/data/objectbox-models/default.json +++ b/modules/data/objectbox-models/default.json @@ -361,10 +361,36 @@ } ], "relations": [] + }, + { + "id": "4:5892387857597582401", + "lastPropertyId": "3:5250934048025398662", + "name": "CustomEmojiAspectRatioRecord", + "properties": [ + { + "id": "1:27795029384995044", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:6142530143329153194", + "name": "uri", + "indexId": "9:4038966684149214552", + "type": 9, + "flags": 2080 + }, + { + "id": "3:5250934048025398662", + "name": "aspectRatio", + "type": 7 + } + ], + "relations": [] } ], - "lastEntityId": "3:1672123969377209864", - "lastIndexId": "8:2235427975397612884", + "lastEntityId": "4:5892387857597582401", + "lastIndexId": "9:4038966684149214552", "lastRelationId": "2:5971800600708341253", "lastSequenceId": "0:0", "modelVersion": 5, diff --git a/modules/data/objectbox-models/default.json.bak b/modules/data/objectbox-models/default.json.bak index ff836a5648..e9d3071665 100644 --- a/modules/data/objectbox-models/default.json.bak +++ b/modules/data/objectbox-models/default.json.bak @@ -316,10 +316,55 @@ "targetId": "1:4355718382021751829" } ] + }, + { + "id": "3:1672123969377209864", + "lastPropertyId": "6:7634221281760726222", + "name": "ReactionUsersRecord", + "properties": [ + { + "id": "1:676934433640553584", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:8038701529227730420", + "name": "accountId", + "indexId": "6:6489830746707304657", + "type": 6, + "flags": 8 + }, + { + "id": "3:2163943456333549192", + "name": "noteId", + "indexId": "7:7445768282858324655", + "type": 9, + "flags": 2048 + }, + { + "id": "4:2143810341282238420", + "name": "accountIdAndNoteIdAndReaction", + "indexId": "8:2235427975397612884", + "type": 9, + "flags": 2080 + }, + { + "id": "5:1292188942965914799", + "name": "reaction", + "type": 9 + }, + { + "id": "6:7634221281760726222", + "name": "accountIds", + "type": 30 + } + ], + "relations": [] } ], - "lastEntityId": "2:2221534449032746185", - "lastIndexId": "5:7310482946990627063", + "lastEntityId": "3:1672123969377209864", + "lastIndexId": "8:2235427975397612884", "lastRelationId": "2:5971800600708341253", "lastSequenceId": "0:0", "modelVersion": 5, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/CustomEmojiModule.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/CustomEmojiModule.kt index d24771d86c..e112265cf7 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/CustomEmojiModule.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/CustomEmojiModule.kt @@ -6,7 +6,9 @@ import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent import net.pantasystem.milktea.data.infrastructure.emoji.CustomEmojiApiAdapter import net.pantasystem.milktea.data.infrastructure.emoji.CustomEmojiApiAdapterImpl +import net.pantasystem.milktea.data.infrastructure.emoji.CustomEmojiAspectRatioDataSourceImpl import net.pantasystem.milktea.data.infrastructure.emoji.CustomEmojiRepositoryImpl +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatioDataSource import net.pantasystem.milktea.model.emoji.CustomEmojiRepository import javax.inject.Singleton @@ -20,4 +22,8 @@ abstract class CustomEmojiModule { @Singleton @Binds internal abstract fun bindCustomEmojiRepository(impl: CustomEmojiRepositoryImpl): CustomEmojiRepository + + @Singleton + @Binds + internal abstract fun bindAspectRatioDataSource(impl: CustomEmojiAspectRatioDataSourceImpl): CustomEmojiAspectRatioDataSource } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt new file mode 100644 index 0000000000..8ade670149 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt @@ -0,0 +1,73 @@ +package net.pantasystem.milktea.data.infrastructure.emoji + +import io.objectbox.Box +import io.objectbox.BoxStore +import io.objectbox.kotlin.awaitCallInTx +import io.objectbox.kotlin.boxFor +import io.objectbox.kotlin.inValues +import io.objectbox.query.QueryBuilder +import kotlinx.coroutines.CoroutineDispatcher +import net.pantasystem.milktea.common.runCancellableCatching +import net.pantasystem.milktea.common_android.hilt.IODispatcher +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatio +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatioDataSource +import javax.inject.Inject + +class CustomEmojiAspectRatioDataSourceImpl @Inject constructor( + private val boxStore: BoxStore, + @IODispatcher val coroutineDispatcher: CoroutineDispatcher, +) : CustomEmojiAspectRatioDataSource { + private val aspectBox: Box by lazy { + boxStore.boxFor() + } + + override suspend fun findIn(uris: List): Result> = + runCancellableCatching { + aspectBox.query().inValues( + CustomEmojiAspectRatioRecord_.uri, + uris.toTypedArray(), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().find().map { + CustomEmojiAspectRatio( + uri = it.uri, + aspectRatio = it.aspectRatio + ) + } + } + + override suspend fun findOne(uri: String): Result = runCancellableCatching { + aspectBox.query().equal( + CustomEmojiAspectRatioRecord_.uri, + uri, + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().findFirst()?.let { + CustomEmojiAspectRatio( + uri = it.uri, + aspectRatio = it.aspectRatio + ) + } ?: throw NoSuchElementException() + } + + override suspend fun save(ratio: CustomEmojiAspectRatio): Result = runCancellableCatching { + boxStore.awaitCallInTx { + val exists = aspectBox.query().equal( + CustomEmojiAspectRatioRecord_.uri, + ratio.uri, + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().findFirst() + if (exists == null) { + aspectBox.put(CustomEmojiAspectRatioRecord.from(ratio)) + } else { + aspectBox.put(exists.copy(aspectRatio = ratio.aspectRatio)) + } + } + } + + override suspend fun delete(ratio: CustomEmojiAspectRatio): Result = runCancellableCatching { + aspectBox.query().equal( + CustomEmojiAspectRatioRecord_.uri, + ratio.uri, + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().remove() + } +} \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioRecord.kt new file mode 100644 index 0000000000..870db85e60 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioRecord.kt @@ -0,0 +1,24 @@ +package net.pantasystem.milktea.data.infrastructure.emoji + +import io.objectbox.annotation.Entity +import io.objectbox.annotation.Id +import io.objectbox.annotation.Unique +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatio + +@Entity +data class CustomEmojiAspectRatioRecord ( + @Id var id: Long = 0, + @Unique + var uri: String = "", + var aspectRatio: Float = 1f, +) { + + companion object { + fun from(model: CustomEmojiAspectRatio): CustomEmojiAspectRatioRecord { + return CustomEmojiAspectRatioRecord( + uri = model.uri, + aspectRatio = model.aspectRatio + ) + } + } +} \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 1dc1384d67..283da49757 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -41,12 +41,12 @@ object NoteReactionViewHelper { GlideApp.with(reactionImageTypeView.context) .load(emoji.url ?: emoji.uri) .let { + val metrics = context.resources.displayMetrics + val imageViewHeightPx = 20 * metrics.density val imageAspectRatio = ImageAspectRatioCache.get(emoji.url ?: emoji.uri) if (imageAspectRatio == null) { it } else { - val metrics = context.resources.displayMetrics - val imageViewHeightPx = 20 * metrics.density it.override((imageViewHeightPx * imageAspectRatio).toInt()) } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioDataSource.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioDataSource.kt new file mode 100644 index 0000000000..e680f39985 --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioDataSource.kt @@ -0,0 +1,17 @@ +package net.pantasystem.milktea.model.emoji + +interface CustomEmojiAspectRatioDataSource { + suspend fun save(ratio: CustomEmojiAspectRatio): Result + + suspend fun findIn(uris: List): Result> + + suspend fun findOne(uri: String): Result + + suspend fun delete(ratio: CustomEmojiAspectRatio): Result +} + + +data class CustomEmojiAspectRatio( + val uri: String, + val aspectRatio: Float +) \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/Emoji.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/Emoji.kt index 829594260d..fdc73678b5 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/Emoji.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/Emoji.kt @@ -12,8 +12,9 @@ data class Emoji( @SerialName("uri") val uri: String? = null, @SerialName("type") val type: String? = null, @SerialName("category") val category: String? = null, - @SerialName("aliases") val aliases: List? = null + @SerialName("aliases") val aliases: List? = null, + @kotlinx.serialization.Transient val aspectRatio: Float? = null, ): Serializable{ - constructor(name: String) : this(null, name, null, null, null, null, null) + constructor(name: String) : this(null, name, null, null, null, null, null, null) } \ No newline at end of file From e7c4406ae36371ee66d0176ff0f15075f18ca30b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 11:53:40 +0900 Subject: [PATCH 196/432] feat: save aspect ratio --- .../api/mastodon/emojis/TootEmojiDTO.kt | 9 +++++- .../api/mastodon/status/TootStatusDTO.kt | 11 +++++++ .../common_android_ui/BindingProvider.kt | 3 ++ modules/data/objectbox-models/default.json | 7 ++++- .../data/objectbox-models/default.json.bak | 30 +++++++++++++++++-- .../data/converters/NoteDTOEntityConverter.kt | 29 ++++++++++++++---- .../notes/NoteDataSourceAdder.kt | 8 ++--- .../notes/impl/db/NoteRecord.kt | 16 +++++++++- .../note/reaction/NoteReactionViewHelper.kt | 4 +-- .../SaveImageAspectRequestListener.kt | 20 +++++++++++-- .../emoji/CustomEmojiAspectRatioStore.kt | 26 ++++++++++++++++ 11 files changed, 145 insertions(+), 18 deletions(-) create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/emojis/TootEmojiDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/emojis/TootEmojiDTO.kt index ce47c2a05b..659239c6f5 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/emojis/TootEmojiDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/emojis/TootEmojiDTO.kt @@ -19,7 +19,13 @@ data class TootEmojiDTO( val category: String? = null, @SerialName("visible_in_picker") - val visibleInPicker: Boolean = true + val visibleInPicker: Boolean = true, + + @SerialName("width") + val width: Int? = null, + + @SerialName("height") + val height: Int? = null, ) { fun toEmoji(): Emoji { @@ -27,6 +33,7 @@ data class TootEmojiDTO( name = shortcode, url = url, category = category, + aspectRatio = if (width == null || height == null) null else (width.toFloat() / height) ) } } \ No newline at end of file diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/TootStatusDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/TootStatusDTO.kt index 6ea7badc8e..c75e8c6bb2 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/TootStatusDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/mastodon/status/TootStatusDTO.kt @@ -178,6 +178,12 @@ data class TootStatusDTO( @SerialName("static_url") val staticUrl: String? = null, + + @SerialName("width") + val width: Float? = null, + + @SerialName("height") + val height: Float? = null, ) { val isCustomEmoji = url != null || staticUrl != null @@ -203,6 +209,11 @@ data class TootStatusDTO( }, url = url, host = domain, + aspectRatio = if (width == null || height == null) { + null + } else { + width / height + } ) } diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/BindingProvider.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/BindingProvider.kt index a944299219..a22785597b 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/BindingProvider.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/BindingProvider.kt @@ -9,6 +9,7 @@ import net.pantasystem.milktea.app_store.setting.SettingStore import net.pantasystem.milktea.common_navigation.MediaNavigation import net.pantasystem.milktea.common_navigation.SearchNavigation import net.pantasystem.milktea.common_navigation.UserDetailNavigation +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatioStore import net.pantasystem.milktea.model.emoji.CustomEmojiRepository import net.pantasystem.milktea.model.instance.MetaRepository import net.pantasystem.milktea.model.setting.ColorSettingStore @@ -32,4 +33,6 @@ interface BindingProvider { fun customEmojiRepository(): CustomEmojiRepository fun colorSettingStore(): ColorSettingStore + + fun customEmojiAspectRatioStore(): CustomEmojiAspectRatioStore } \ No newline at end of file diff --git a/modules/data/objectbox-models/default.json b/modules/data/objectbox-models/default.json index 0dc0e9d71c..74d253b847 100644 --- a/modules/data/objectbox-models/default.json +++ b/modules/data/objectbox-models/default.json @@ -5,7 +5,7 @@ "entities": [ { "id": "1:4355718382021751829", - "lastPropertyId": "51:1047146758580551890", + "lastPropertyId": "52:3207491205296200689", "name": "NoteRecord", "properties": [ { @@ -271,6 +271,11 @@ "id": "51:1047146758580551890", "name": "maxReactionsPerAccount", "type": 5 + }, + { + "id": "52:3207491205296200689", + "name": "customEmojiAspectRatioMap", + "type": 13 } ], "relations": [] diff --git a/modules/data/objectbox-models/default.json.bak b/modules/data/objectbox-models/default.json.bak index e9d3071665..0dc0e9d71c 100644 --- a/modules/data/objectbox-models/default.json.bak +++ b/modules/data/objectbox-models/default.json.bak @@ -361,10 +361,36 @@ } ], "relations": [] + }, + { + "id": "4:5892387857597582401", + "lastPropertyId": "3:5250934048025398662", + "name": "CustomEmojiAspectRatioRecord", + "properties": [ + { + "id": "1:27795029384995044", + "name": "id", + "type": 6, + "flags": 1 + }, + { + "id": "2:6142530143329153194", + "name": "uri", + "indexId": "9:4038966684149214552", + "type": 9, + "flags": 2080 + }, + { + "id": "3:5250934048025398662", + "name": "aspectRatio", + "type": 7 + } + ], + "relations": [] } ], - "lastEntityId": "3:1672123969377209864", - "lastIndexId": "8:2235427975397612884", + "lastEntityId": "4:5892387857597582401", + "lastIndexId": "9:4038966684149214552", "lastRelationId": "2:5971800600708341253", "lastSequenceId": "0:0", "modelVersion": 5, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverter.kt index f15c1f6fc9..a130135e42 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverter.kt @@ -7,6 +7,7 @@ import net.pantasystem.milktea.api.misskey.notes.ReactionAcceptanceType import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.channel.Channel import net.pantasystem.milktea.model.drive.FileProperty +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatioDataSource import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.notes.Visibility import net.pantasystem.milktea.model.notes.poll.Poll @@ -16,9 +17,19 @@ import javax.inject.Inject import javax.inject.Singleton @Singleton -class NoteDTOEntityConverter @Inject constructor() { +class NoteDTOEntityConverter @Inject constructor( + private val customEmojiAspectRatioDataSource: CustomEmojiAspectRatioDataSource, +) { suspend fun convert(noteDTO: NoteDTO, account: Account): Note { + val emojis = (noteDTO.emojiList + (noteDTO.reactionEmojiList)) + val aspects = customEmojiAspectRatioDataSource.findIn(emojis.mapNotNull { + it.url ?: it.uri + }).getOrElse { + emptyList() + }.associate { + it.uri to it.aspectRatio + } val visibility = Visibility( noteDTO.visibility ?: NoteVisibilityType.Public, isLocalOnly = noteDTO.localOnly ?: false, @@ -37,7 +48,11 @@ class NoteDTOEntityConverter @Inject constructor() { viaMobile = noteDTO.viaMobile, visibility = visibility, localOnly = noteDTO.localOnly, - emojis = noteDTO.emojiList + (noteDTO.reactionEmojiList), + emojis = (noteDTO.emojiList + (noteDTO.reactionEmojiList)).map { + it.copy( + aspectRatio = aspects[it.url ?: it.uri] + ) + }, app = null, fileIds = noteDTO.fileIds?.map { FileProperty.Id(account.accountId, it) }, poll = noteDTO.poll?.toPoll(), @@ -66,7 +81,7 @@ class NoteDTOEntityConverter @Inject constructor() { name = it.name ) }, - isAcceptingOnlyLikeReaction = when(noteDTO.reactionAcceptance){ + isAcceptingOnlyLikeReaction = when (noteDTO.reactionAcceptance) { ReactionAcceptanceType.LikeOnly4Remote -> noteDTO.uri != null ReactionAcceptanceType.LikeOnly -> true null -> false @@ -96,8 +111,12 @@ fun PollDTO?.toPoll(): Poll? { @Throws(IllegalArgumentException::class) -fun Visibility(type: NoteVisibilityType, isLocalOnly: Boolean, visibleUserIds: List? = null): Visibility { - return when(type){ +fun Visibility( + type: NoteVisibilityType, + isLocalOnly: Boolean, + visibleUserIds: List? = null, +): Visibility { + return when (type) { NoteVisibilityType.Public -> Visibility.Public(isLocalOnly) NoteVisibilityType.Followers -> Visibility.Followers(isLocalOnly) NoteVisibilityType.Home -> Visibility.Home(isLocalOnly) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteDataSourceAdder.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteDataSourceAdder.kt index 2d62e1ccb0..826e346f2d 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteDataSourceAdder.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteDataSourceAdder.kt @@ -165,7 +165,7 @@ suspend fun NoteDTO.toEntities( files, userDTOEntityConverter, noteDTOEntityConverter, - filePropertyDTOEntityConverter + filePropertyDTOEntityConverter, ) return NoteRelationEntities( note = note, @@ -183,7 +183,7 @@ private suspend fun NoteDTO.pickEntities( userDTOEntityConverter: UserDTOEntityConverter, noteDTOEntityConverter: NoteDTOEntityConverter, filePropertyDTOEntityConverter: FilePropertyDTOEntityConverter, -) { + ) { val (note, user) = this.toNoteAndUser( account, userDTOEntityConverter, @@ -204,7 +204,7 @@ private suspend fun NoteDTO.pickEntities( files, userDTOEntityConverter, noteDTOEntityConverter, - filePropertyDTOEntityConverter + filePropertyDTOEntityConverter, ) } @@ -216,7 +216,7 @@ private suspend fun NoteDTO.pickEntities( files, userDTOEntityConverter, noteDTOEntityConverter, - filePropertyDTOEntityConverter + filePropertyDTOEntityConverter, ) } } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecord.kt index 4dd3b19bc2..71d86c84c0 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecord.kt @@ -90,6 +90,7 @@ data class NoteRecord( var myReactions: MutableList? = null, var maxReactionsPerAccount: Int = 0, + var customEmojiAspectRatioMap: MutableMap? = null, ) { companion object { @@ -170,6 +171,15 @@ data class NoteRecord( misskeyIsAcceptingOnlyLikeReaction = t.isAcceptingOnlyLikeReaction } } + customEmojiAspectRatioMap = model.emojis?.mapNotNull { emoji -> + val aspectRatio = emoji.aspectRatio + val uri = emoji.url ?: emoji.uri + if (aspectRatio == null || uri == null) { + null + } else { + uri to aspectRatio.toString() + } + }?.toMap()?.toMutableMap() } fun toModel(): Note { @@ -197,7 +207,11 @@ data class NoteRecord( it == entry.key } ?: false ) }, - emojis = emojis?.map { Emoji(name = it.key, url = it.value) }, + emojis = emojis?.map { Emoji( + name = it.key, + url = it.value, + aspectRatio = customEmojiAspectRatioMap?.get(it.value)?.toFloatOrNull() + ) }, repliesCount = repliesCount, fileIds = fileIds?.map { FileProperty.Id(accountId, it) }, poll = getPoll(), diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 283da49757..3a59906c51 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -43,7 +43,7 @@ object NoteReactionViewHelper { .let { val metrics = context.resources.displayMetrics val imageViewHeightPx = 20 * metrics.density - val imageAspectRatio = ImageAspectRatioCache.get(emoji.url ?: emoji.uri) + val imageAspectRatio = emoji.aspectRatio ?: ImageAspectRatioCache.get(emoji.url ?: emoji.uri) if (imageAspectRatio == null) { it } else { @@ -51,7 +51,7 @@ object NoteReactionViewHelper { } } // .override(min(max(reactionImageTypeView.height, 20), 120)) - .addListener(SaveImageAspectRequestListener(emoji.url ?: emoji.uri)) + .addListener(SaveImageAspectRequestListener(emoji, context)) .into(reactionImageTypeView) } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt index 28b8f42fef..31e2aa1d8f 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt @@ -1,13 +1,18 @@ package net.pantasystem.milktea.note.reaction +import android.content.Context import android.graphics.drawable.Drawable import com.bumptech.glide.load.DataSource import com.bumptech.glide.load.engine.GlideException import com.bumptech.glide.request.RequestListener import com.bumptech.glide.request.target.Target +import dagger.hilt.android.EntryPointAccessors +import net.pantasystem.milktea.common_android_ui.BindingProvider +import net.pantasystem.milktea.model.emoji.Emoji class SaveImageAspectRequestListener( - val url: String? + val emoji: Emoji, + val context: Context, ) : RequestListener { override fun onLoadFailed( e: GlideException?, @@ -27,7 +32,18 @@ class SaveImageAspectRequestListener( ): Boolean { resource ?: return false val imageAspectRatio: Float = resource.intrinsicWidth.toFloat() / resource.intrinsicHeight - ImageAspectRatioCache.put(url, imageAspectRatio) + val navigationEntryPoint = EntryPointAccessors.fromApplication( + context, + BindingProvider::class.java + ) + (emoji.url ?: emoji.uri)?.let { + navigationEntryPoint.customEmojiAspectRatioStore().save( + emoji, imageAspectRatio + ) + + } + + ImageAspectRatioCache.put(emoji.url ?: emoji.uri, imageAspectRatio) return false } } \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt new file mode 100644 index 0000000000..480818d76d --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt @@ -0,0 +1,26 @@ +package net.pantasystem.milktea.model.emoji + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class CustomEmojiAspectRatioStore @Inject constructor( + private val coroutineScope: CoroutineScope, + private val customEmojiAspectRatioDataSource: CustomEmojiAspectRatioDataSource, +){ + + fun save(emoji: Emoji, aspectRatio: Float) { + val url = emoji.url ?: emoji.uri ?: return + if (aspectRatio <= 0f) { + return + } + coroutineScope.launch { + customEmojiAspectRatioDataSource.save(CustomEmojiAspectRatio( + uri = url, + aspectRatio = aspectRatio, + )) + } + } +} \ No newline at end of file From 8f9e341ee9c1255f1579b147718ae5c0b30587a4 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 12:05:59 +0900 Subject: [PATCH 197/432] feat: save size --- .../notes/impl/ObjectBoxNoteDataSource.kt | 2 +- .../milktea/model/emoji/CustomEmojiAspectRatioStore.kt | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt index 186597f2a8..3e92b76daa 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt @@ -207,7 +207,7 @@ class ObjectBoxNoteDataSource @Inject constructor( override suspend fun clear(): Result = runCancellableCatching { withContext(coroutineDispatcher) { - boxStore.removeAllObjects() + } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt index 480818d76d..0bda0544a7 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt @@ -2,6 +2,7 @@ package net.pantasystem.milktea.model.emoji import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.launch +import net.pantasystem.milktea.common.Logger import javax.inject.Inject import javax.inject.Singleton @@ -9,8 +10,13 @@ import javax.inject.Singleton class CustomEmojiAspectRatioStore @Inject constructor( private val coroutineScope: CoroutineScope, private val customEmojiAspectRatioDataSource: CustomEmojiAspectRatioDataSource, + loggerFactory: Logger.Factory, ){ + private val logger by lazy { + loggerFactory.create("CEARStore") + } + fun save(emoji: Emoji, aspectRatio: Float) { val url = emoji.url ?: emoji.uri ?: return if (aspectRatio <= 0f) { @@ -20,7 +26,9 @@ class CustomEmojiAspectRatioStore @Inject constructor( customEmojiAspectRatioDataSource.save(CustomEmojiAspectRatio( uri = url, aspectRatio = aspectRatio, - )) + )).onFailure { + logger.error("save failure", it) + } } } } \ No newline at end of file From 0c702512ddcca34a2ed20b835f4e6e77aa6d9c2e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 12:46:32 +0900 Subject: [PATCH 198/432] feat: save size --- .../CustomEmojiAspectRatioDataSourceImpl.kt | 3 +- .../emoji/CustomEmojiRepositoryImpl.kt | 47 +++++++++++++++++-- .../emoji/db/CustomEmojiRecord.kt | 3 +- .../note/reaction/NoteReactionViewHelper.kt | 2 +- .../SaveImageAspectRequestListener.kt | 10 ++-- .../reaction/choices/EmojiListItemsAdapter.kt | 7 +++ .../emoji/CustomEmojiAspectRatioDataSource.kt | 2 +- .../emoji/CustomEmojiAspectRatioStore.kt | 15 +++++- 8 files changed, 72 insertions(+), 17 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt index 8ade670149..2cc37ade26 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt @@ -48,7 +48,7 @@ class CustomEmojiAspectRatioDataSourceImpl @Inject constructor( } ?: throw NoSuchElementException() } - override suspend fun save(ratio: CustomEmojiAspectRatio): Result = runCancellableCatching { + override suspend fun save(ratio: CustomEmojiAspectRatio): Result = runCancellableCatching { boxStore.awaitCallInTx { val exists = aspectBox.query().equal( CustomEmojiAspectRatioRecord_.uri, @@ -61,6 +61,7 @@ class CustomEmojiAspectRatioDataSourceImpl @Inject constructor( aspectBox.put(exists.copy(aspectRatio = ratio.aspectRatio)) } } + findOne(ratio.uri).getOrThrow() } override suspend fun delete(ratio: CustomEmojiAspectRatio): Result = runCancellableCatching { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt index a76285e702..c694775065 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiRepositoryImpl.kt @@ -9,6 +9,7 @@ import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.data.infrastructure.emoji.db.CustomEmojiDAO import net.pantasystem.milktea.data.infrastructure.emoji.delegate.CustomEmojiUpInsertDelegate +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatioDataSource import net.pantasystem.milktea.model.emoji.CustomEmojiRepository import net.pantasystem.milktea.model.emoji.Emoji import net.pantasystem.milktea.model.nodeinfo.NodeInfo @@ -21,6 +22,7 @@ internal class CustomEmojiRepositoryImpl @Inject constructor( private val customEmojiApiAdapter: CustomEmojiApiAdapter, private val customEmojiCache: CustomEmojiCache, private val upInsert: CustomEmojiUpInsertDelegate, + private val aspectRatioDataSource: CustomEmojiAspectRatioDataSource, @IODispatcher private val ioDispatcher: CoroutineDispatcher, ) : CustomEmojiRepository { @@ -34,10 +36,21 @@ internal class CustomEmojiRepositoryImpl @Inject constructor( emojis = customEmojiDAO.findBy(host).map { it.toModel() } + if (emojis.isEmpty()) { emojis = fetch(nodeInfo).getOrThrow() upInsert(nodeInfo.host, emojis) } + val aspects = aspectRatioDataSource.findIn(emojis.mapNotNull { + it.url ?: it.uri + }).getOrElse { emptyList() }.associateBy { + it.uri + } + emojis = emojis.map { + it.copy( + aspectRatio = aspects[it.url ?: it.uri]?.aspectRatio + ) + } customEmojiCache.put(host, emojis) emojis } @@ -45,8 +58,19 @@ internal class CustomEmojiRepositoryImpl @Inject constructor( override suspend fun findByName(host: String, name: String): Result> = runCancellableCatching { withContext(ioDispatcher) { - customEmojiDAO.findBy(host, name).map { - it.toModel() + val dtoList = customEmojiDAO.findBy(host, name) + val aspects = aspectRatioDataSource.findIn( + dtoList.mapNotNull { + it.emoji.url ?: it.emoji.uri + } + ).getOrElse { emptyList() }.associateBy { + it.uri + } + dtoList.map { + it.toModel( + aspects[it.emoji.url ?: it.emoji.uri]?.aspectRatio + ) + } } } @@ -54,7 +78,15 @@ internal class CustomEmojiRepositoryImpl @Inject constructor( override suspend fun sync(host: String): Result = runCancellableCatching { withContext(ioDispatcher) { val nodeInfo = nodeInfoRepository.find(host).getOrThrow() - val emojis = fetch(nodeInfo).getOrThrow() + var emojis = fetch(nodeInfo).getOrThrow() + val aspects = aspectRatioDataSource.findIn(emojis.mapNotNull { + it.url ?: it.uri + }).getOrElse { emptyList() }.associateBy { + it.uri + } + emojis = emojis.map { + it.copy(aspectRatio = aspects[it.url ?: it.uri]?.aspectRatio) + } customEmojiCache.put(host, emojis) customEmojiDAO.deleteByHost(host) @@ -69,8 +101,15 @@ internal class CustomEmojiRepositoryImpl @Inject constructor( nodeInfoRepository.find(host).getOrThrow() }.asFlow().flatMapLatest { customEmojiDAO.observeBy(host).map { list -> + val aspects = aspectRatioDataSource.findIn( + list.mapNotNull { + it.emoji.url ?: it.emoji.uri + } + ).getOrElse { emptyList() }.associateBy { + it.uri + } list.map { - it.toModel() + it.toModel(aspects[it.emoji.url ?: it.emoji.uri]?.aspectRatio) } } }.onEach { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt index cc3fb26426..65ba0e8807 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/db/CustomEmojiRecord.kt @@ -73,7 +73,7 @@ data class CustomEmojiRelated( ) { @Ignore - fun toModel(): Emoji { + fun toModel(aspectRatio: Float? = null): Emoji { return Emoji( id = emoji.serverId, name = emoji.name, @@ -85,6 +85,7 @@ data class CustomEmojiRelated( aliases = aliases.map { it.value }, + aspectRatio = aspectRatio ) } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 3a59906c51..b0762ddda8 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -43,7 +43,7 @@ object NoteReactionViewHelper { .let { val metrics = context.resources.displayMetrics val imageViewHeightPx = 20 * metrics.density - val imageAspectRatio = emoji.aspectRatio ?: ImageAspectRatioCache.get(emoji.url ?: emoji.uri) + val imageAspectRatio = emoji.aspectRatio if (imageAspectRatio == null) { it } else { diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt index 31e2aa1d8f..506894774a 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt @@ -36,14 +36,10 @@ class SaveImageAspectRequestListener( context, BindingProvider::class.java ) - (emoji.url ?: emoji.uri)?.let { - navigationEntryPoint.customEmojiAspectRatioStore().save( - emoji, imageAspectRatio - ) - - } + navigationEntryPoint.customEmojiAspectRatioStore().save( + emoji, imageAspectRatio + ) - ImageAspectRatioCache.put(emoji.url ?: emoji.uri, imageAspectRatio) return false } } \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt index 9c5edbbced..8e20f800f7 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt @@ -15,6 +15,7 @@ import net.pantasystem.milktea.note.EmojiType import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.ItemEmojiChoiceBinding import net.pantasystem.milktea.note.databinding.ItemEmojiListItemHeaderBinding +import net.pantasystem.milktea.note.reaction.SaveImageAspectRequestListener class EmojiListItemsAdapter( private val onEmojiSelected: (EmojiType) -> Unit, @@ -60,6 +61,12 @@ class EmojiListItemsAdapter( .load(item.emoji.url ?: item.emoji.uri) // FIXME: webpの場合うまく表示できなくなる // .centerCrop() + .addListener( + SaveImageAspectRequestListener( + item.emoji, + binding.root.context + ) + ) .into(binding.reactionImagePreview) binding.reactionStringPreview.setMemoVisibility(View.GONE) binding.reactionImagePreview.setMemoVisibility(View.VISIBLE) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioDataSource.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioDataSource.kt index e680f39985..3e1ed2038b 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioDataSource.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioDataSource.kt @@ -1,7 +1,7 @@ package net.pantasystem.milktea.model.emoji interface CustomEmojiAspectRatioDataSource { - suspend fun save(ratio: CustomEmojiAspectRatio): Result + suspend fun save(ratio: CustomEmojiAspectRatio): Result suspend fun findIn(uris: List): Result> diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt index 0bda0544a7..95396d2aec 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/CustomEmojiAspectRatioStore.kt @@ -18,16 +18,27 @@ class CustomEmojiAspectRatioStore @Inject constructor( } fun save(emoji: Emoji, aspectRatio: Float) { - val url = emoji.url ?: emoji.uri ?: return - if (aspectRatio <= 0f) { + val url = (emoji.url ?: emoji.uri) + if (aspectRatio <= 0f || url == null) { + logger.debug { + "cancel save emoji aspect. emoji:$emoji, aspect:$aspectRatio" + } return } coroutineScope.launch { + val ratio = customEmojiAspectRatioDataSource.findOne(url).getOrNull() + if (ratio != null) { + return@launch + } customEmojiAspectRatioDataSource.save(CustomEmojiAspectRatio( uri = url, aspectRatio = aspectRatio, )).onFailure { logger.error("save failure", it) + }.onSuccess { + logger.debug { + "success save emoji aspect ratio:$emoji, $aspectRatio, $it" + } } } } From 52f68e317e1849d1c2e2fcfa5a48c227882bc42c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 12:56:00 +0900 Subject: [PATCH 199/432] feat: io dispatcher --- .../CustomEmojiAspectRatioDataSourceImpl.kt | 71 +++++++++++-------- 1 file changed, 40 insertions(+), 31 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt index 2cc37ade26..e53cd3c1cf 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiAspectRatioDataSourceImpl.kt @@ -7,6 +7,7 @@ import io.objectbox.kotlin.boxFor import io.objectbox.kotlin.inValues import io.objectbox.query.QueryBuilder import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.withContext import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatio @@ -23,52 +24,60 @@ class CustomEmojiAspectRatioDataSourceImpl @Inject constructor( override suspend fun findIn(uris: List): Result> = runCancellableCatching { - aspectBox.query().inValues( + withContext(coroutineDispatcher) { + aspectBox.query().inValues( + CustomEmojiAspectRatioRecord_.uri, + uris.toTypedArray(), + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().find().map { + CustomEmojiAspectRatio( + uri = it.uri, + aspectRatio = it.aspectRatio + ) + } + } + } + + override suspend fun findOne(uri: String): Result = runCancellableCatching { + withContext(coroutineDispatcher) { + aspectBox.query().equal( CustomEmojiAspectRatioRecord_.uri, - uris.toTypedArray(), + uri, QueryBuilder.StringOrder.CASE_SENSITIVE - ).build().find().map { + ).build().findFirst()?.let { CustomEmojiAspectRatio( uri = it.uri, aspectRatio = it.aspectRatio ) - } + } ?: throw NoSuchElementException() } - - override suspend fun findOne(uri: String): Result = runCancellableCatching { - aspectBox.query().equal( - CustomEmojiAspectRatioRecord_.uri, - uri, - QueryBuilder.StringOrder.CASE_SENSITIVE - ).build().findFirst()?.let { - CustomEmojiAspectRatio( - uri = it.uri, - aspectRatio = it.aspectRatio - ) - } ?: throw NoSuchElementException() } override suspend fun save(ratio: CustomEmojiAspectRatio): Result = runCancellableCatching { - boxStore.awaitCallInTx { - val exists = aspectBox.query().equal( - CustomEmojiAspectRatioRecord_.uri, - ratio.uri, - QueryBuilder.StringOrder.CASE_SENSITIVE - ).build().findFirst() - if (exists == null) { - aspectBox.put(CustomEmojiAspectRatioRecord.from(ratio)) - } else { - aspectBox.put(exists.copy(aspectRatio = ratio.aspectRatio)) + withContext(coroutineDispatcher) { + boxStore.awaitCallInTx { + val exists = aspectBox.query().equal( + CustomEmojiAspectRatioRecord_.uri, + ratio.uri, + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().findFirst() + if (exists == null) { + aspectBox.put(CustomEmojiAspectRatioRecord.from(ratio)) + } else { + aspectBox.put(exists.copy(aspectRatio = ratio.aspectRatio)) + } } } findOne(ratio.uri).getOrThrow() } override suspend fun delete(ratio: CustomEmojiAspectRatio): Result = runCancellableCatching { - aspectBox.query().equal( - CustomEmojiAspectRatioRecord_.uri, - ratio.uri, - QueryBuilder.StringOrder.CASE_SENSITIVE - ).build().remove() + withContext(coroutineDispatcher) { + aspectBox.query().equal( + CustomEmojiAspectRatioRecord_.uri, + ratio.uri, + QueryBuilder.StringOrder.CASE_SENSITIVE + ).build().remove() + } } } \ No newline at end of file From 555a56995c1076ce92a23a07e4769eb540e27f52 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 12:58:49 +0900 Subject: [PATCH 200/432] =?UTF-8?q?feat:=20retry=E3=81=97=E3=81=AA?= =?UTF-8?q?=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_android_ui/MFMDecorator.kt | 71 +++++++++---------- 1 file changed, 33 insertions(+), 38 deletions(-) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt index e36993cddb..80efb5a8ee 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt @@ -5,17 +5,12 @@ import android.app.SearchManager import android.content.Intent import android.graphics.Color import android.graphics.Typeface -import android.graphics.drawable.Drawable import android.net.Uri import android.text.* import android.text.style.* import android.util.Log import android.view.View import android.widget.TextView -import com.bumptech.glide.load.DataSource -import com.bumptech.glide.load.engine.GlideException -import com.bumptech.glide.request.RequestListener -import com.bumptech.glide.request.target.Target import dagger.hilt.android.EntryPointAccessors import dagger.hilt.android.internal.managers.FragmentComponentManager import jp.panta.misskeyandroidclient.mfm.* @@ -314,39 +309,39 @@ object MFMDecorator { GlideApp.with(textView) .load(emojiElement.emoji.url) .override(min(max(textView.textSize.toInt(), 10), 128)) - .addListener(object : RequestListener { - override fun onLoadFailed( - e: GlideException?, - model: Any?, - target: Target?, - isFirstResource: Boolean - ): Boolean { - val t = this@LazyEmojiDecorator.textView.get() - if (t != null && !skipEmojis.contains(emojiElement.emoji) && t.getTag(R.id.TEXT_VIEW_MFM_TAG_ID) == lazyDecorateResult.sourceText) { - if (retryCounter < 10) { - - t.text = decorate( - t, - lazyDecorateResult = lazyDecorateResult, - skipEmojis = skipEmojis.add(emojiElement.emoji), - retryCounter + 1 - ) - } - } - - return false - } - - override fun onResourceReady( - resource: Drawable?, - model: Any?, - target: Target?, - dataSource: DataSource?, - isFirstResource: Boolean - ): Boolean { - return false - } - }) +// .addListener(object : RequestListener { +// override fun onLoadFailed( +// e: GlideException?, +// model: Any?, +// target: Target?, +// isFirstResource: Boolean +// ): Boolean { +// val t = this@LazyEmojiDecorator.textView.get() +// if (t != null && !skipEmojis.contains(emojiElement.emoji) && t.getTag(R.id.TEXT_VIEW_MFM_TAG_ID) == lazyDecorateResult.sourceText) { +// if (retryCounter < 10) { +// +// t.text = decorate( +// t, +// lazyDecorateResult = lazyDecorateResult, +// skipEmojis = skipEmojis.add(emojiElement.emoji), +// retryCounter + 1 +// ) +// } +// } +// +// return false +// } +// +// override fun onResourceReady( +// resource: Drawable?, +// model: Any?, +// target: Target?, +// dataSource: DataSource?, +// isFirstResource: Boolean +// ): Boolean { +// return false +// } +// }) .into(emojiSpan.target) } } From cfcd4a71935f3a2564ab6cc568de92891724ce88 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 13:08:55 +0900 Subject: [PATCH 201/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E6=99=82=E3=81=AB=E3=83=9C=E3=83=A4?= =?UTF-8?q?=E3=81=91=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/di/module/SocketModule.kt | 3 ++ .../notes/NoteCaptureAPIAdapterImpl.kt | 37 ++++++++++++++----- .../infrastructure/notes/NoteEventReducer.kt | 5 ++- .../notes/NoteEventReducerKtTest.kt | 15 +++++--- 4 files changed, 44 insertions(+), 16 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/SocketModule.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/SocketModule.kt index 85ab2ff232..909a04b496 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/SocketModule.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/SocketModule.kt @@ -19,6 +19,7 @@ import net.pantasystem.milktea.data.streaming.SocketWithAccountProvider import net.pantasystem.milktea.data.streaming.StreamingAPIProvider import net.pantasystem.milktea.data.streaming.impl.SocketWithAccountProviderImpl import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatioDataSource import net.pantasystem.milktea.model.emoji.EmojiEventHandler import net.pantasystem.milktea.model.notes.NoteCaptureAPIAdapter import net.pantasystem.milktea.model.notes.NoteDataSource @@ -76,6 +77,7 @@ object SocketModule { noteDataSource: NoteDataSource, noteDataSourceAdder: NoteDataSourceAdder, streamingAPIProvider: StreamingAPIProvider, + customEmojiAspectRatioDataSource: CustomEmojiAspectRatioDataSource, ): NoteCaptureAPIAdapter { return NoteCaptureAPIAdapterImpl( accountRepository = accountRepository, @@ -86,6 +88,7 @@ object SocketModule { dispatcher = Dispatchers.IO, noteDataSourceAdder = noteDataSourceAdder, streamingAPIProvider = streamingAPIProvider, + customEmojiAspectRatioDataSource = customEmojiAspectRatioDataSource ) } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteCaptureAPIAdapterImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteCaptureAPIAdapterImpl.kt index 6f39ee9dac..33b70056ab 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteCaptureAPIAdapterImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteCaptureAPIAdapterImpl.kt @@ -11,6 +11,7 @@ import net.pantasystem.milktea.common.mapCancellableCatching import net.pantasystem.milktea.data.streaming.StreamingAPIProvider import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatioDataSource import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.notes.NoteCaptureAPIAdapter import net.pantasystem.milktea.model.notes.NoteDataSource @@ -26,6 +27,7 @@ class NoteCaptureAPIAdapterImpl( private val noteCaptureAPIWithAccountProvider: NoteCaptureAPIWithAccountProvider, private val streamingAPIProvider: StreamingAPIProvider, private val noteDataSourceAdder: NoteDataSourceAdder, + private val customEmojiAspectRatioDataSource: CustomEmojiAspectRatioDataSource, loggerFactory: Logger.Factory, cs: CoroutineScope, dispatcher: CoroutineDispatcher = Dispatchers.IO, @@ -48,13 +50,19 @@ class NoteCaptureAPIAdapterImpl( /** * mastodonのStreaming APIから流れてきたEventがここに入る */ - private val streamingEventDispatcher = MutableSharedFlow>(extraBufferCapacity = 1000, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val streamingEventDispatcher = MutableSharedFlow>( + extraBufferCapacity = 1000, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) /** * Noteのキャプチャーによって発生したイベントのQueue。 * ここから順番にイベントを取り出し、キャッシュに反映させるなどをしている。 */ - private val noteUpdatedDispatcher = MutableSharedFlow>(extraBufferCapacity = 1000, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val noteUpdatedDispatcher = MutableSharedFlow>( + extraBufferCapacity = 1000, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) // /** // * 使用されなくなったNoteのリソースが順番に入れられるQueue。 @@ -129,9 +137,10 @@ class NoteCaptureAPIAdapterImpl( noteIdWithJob[id] = job } Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { - val job = requireNotNull(streamingAPIProvider.get(account)).connectUser().catch { e -> - logger.error("ノート更新イベント受信中にエラー発生", e = e) - }.onEach { + val job = requireNotNull(streamingAPIProvider.get(account)).connectUser() + .catch { e -> + logger.error("ノート更新イベント受信中にエラー発生", e = e) + }.onEach { streamingEventDispatcher.tryEmit(account to it) }.launchIn(coroutineScope) noteIdWithJob[id] = job @@ -170,7 +179,7 @@ class NoteCaptureAPIAdapterImpl( */ private fun addRepositoryEventListener( noteId: Note.Id, - listener: (NoteDataSource.Event) -> Unit + listener: (NoteDataSource.Event) -> Unit, ): Boolean { synchronized(noteIdWithListeners) { val listeners = noteIdWithListeners[noteId] @@ -191,7 +200,7 @@ class NoteCaptureAPIAdapterImpl( */ private fun removeRepositoryEventListener( noteId: Note.Id, - listener: (NoteDataSource.Event) -> Unit + listener: (NoteDataSource.Event) -> Unit, ): Boolean { synchronized(noteIdWithListeners) { @@ -226,7 +235,17 @@ class NoteCaptureAPIAdapterImpl( noteDataSource.delete(noteId) } is NoteUpdated.Body.Reacted -> { - noteDataSource.add(note.onReacted(account, e)) + noteDataSource.add( + note.onReacted( + account, + e, + aspectRatio = (e.body.emoji?.url ?: e.body.emoji?.url)?.let { + customEmojiAspectRatioDataSource.findOne( + it + ).getOrNull() + } + ) + ) } is NoteUpdated.Body.Unreacted -> { noteDataSource.add(note.onUnReacted(account, e)) @@ -245,7 +264,7 @@ class NoteCaptureAPIAdapterImpl( private suspend fun handleMastodonRemoteEvent(account: Account, e: Event) { try { - when(e) { + when (e) { is Event.Delete -> { noteDataSource.remove(Note.Id(account.accountId, e.id)) } diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteEventReducer.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteEventReducer.kt index 15794130db..65396c06eb 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteEventReducer.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/NoteEventReducer.kt @@ -3,6 +3,7 @@ package net.pantasystem.milktea.data.infrastructure.notes import net.pantasystem.milktea.api_streaming.NoteUpdated import net.pantasystem.milktea.api_streaming.mastodon.EmojiReaction import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatio import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.notes.reaction.ReactionCount @@ -27,7 +28,7 @@ fun Note.onUnReacted(account: Account, e: NoteUpdated.Body.Unreacted): Note { ) } -fun Note.onReacted(account: Account, e: NoteUpdated.Body.Reacted): Note { +fun Note.onReacted(account: Account, e: NoteUpdated.Body.Reacted, aspectRatio: CustomEmojiAspectRatio?): Note { val hasItem = this.reactionCounts.any { count -> count.reaction == e.body.reaction } @@ -43,7 +44,7 @@ fun Note.onReacted(account: Account, e: NoteUpdated.Body.Reacted): Note { list = list + ReactionCount(reaction = e.body.reaction, count = 1, me = false) } - val emojis = when (val emoji = e.body.emoji) { + val emojis = when (val emoji = e.body.emoji?.copy(aspectRatio = aspectRatio?.aspectRatio)) { null -> this.emojis else -> (this.emojis ?: emptyList()) + emoji } diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/notes/NoteEventReducerKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/notes/NoteEventReducerKtTest.kt index f9abcda5b0..229f99ed55 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/notes/NoteEventReducerKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/notes/NoteEventReducerKtTest.kt @@ -31,7 +31,8 @@ class NoteEventReducerKtTest { reaction = ":kawaii:", userId = "other" ) - ) + ), + null ) Assertions.assertEquals(listOf(ReactionCount(":kawaii:", 1, false)), result.reactionCounts) @@ -54,7 +55,8 @@ class NoteEventReducerKtTest { reaction = ":kawaii:", userId = account.remoteId ) - ) + ), + null ) Assertions.assertEquals(listOf(ReactionCount(":kawaii:", 1, true)), result.reactionCounts) @@ -80,7 +82,8 @@ class NoteEventReducerKtTest { reaction = ":kawaii:", userId = account.remoteId ) - ) + ), + null ) Assertions.assertEquals( @@ -108,7 +111,8 @@ class NoteEventReducerKtTest { reaction = ":kawaii:", userId = account.remoteId ) - ) + ), + null ) Assertions.assertEquals( @@ -212,7 +216,8 @@ class NoteEventReducerKtTest { name = ":kawaii:" ) ) - ) + ), + null ) Assertions.assertEquals( listOf( From b26e16f43b35804bf77f4e9bc22f54b38e4958ad Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 13:32:00 +0900 Subject: [PATCH 202/432] fix --- .../data/converters/NoteDTOEntityConverterTest.kt | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverterTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverterTest.kt index a72016bbc9..6505458c72 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverterTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverterTest.kt @@ -11,6 +11,9 @@ import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.user.User import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock class NoteDTOEntityConverterTest { @@ -18,7 +21,13 @@ class NoteDTOEntityConverterTest { @Test fun convert() = runTest { - val converter = NoteDTOEntityConverter() + val converter = NoteDTOEntityConverter( + mock() { + onBlocking { + findIn(any()) + } doReturn Result.success(emptyList()) + } + ) val account = Account( remoteId = "test-id", From c9dbdf740de47739be11ed384756794fd3a80aeb Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 14:14:49 +0900 Subject: [PATCH 203/432] =?UTF-8?q?feat:=20=E3=83=95=E3=82=A3=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=83=89=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt | 2 ++ modules/data/objectbox-models/default.json | 7 ++++++- modules/data/objectbox-models/default.json.bak | 7 ++++++- .../milktea/data/converters/NoteDTOEntityConverter.kt | 7 +++++++ .../data/infrastructure/notes/impl/db/NoteRecord.kt | 5 ++++- .../main/java/net/pantasystem/milktea/model/notes/Note.kt | 1 + 6 files changed, 26 insertions(+), 3 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt index 730bd4c0bf..ea4648415d 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt @@ -155,4 +155,6 @@ object NoteVisibilityTypeSerializer : EnumIgnoreUnknownSerializer noteDTO.uri != null ReactionAcceptanceType.LikeOnly -> true + ReactionAcceptanceType.NonSensitiveOnly -> false + ReactionAcceptanceType.NonSensitiveOnly4LocalOnly4Remote -> false null -> false }, + isNotAcceptingSensitiveReaction = when (noteDTO.reactionAcceptance) { + ReactionAcceptanceType.NonSensitiveOnly -> true + ReactionAcceptanceType.NonSensitiveOnly4LocalOnly4Remote -> true + else -> false + }, ), maxReactionsPerAccount = 1 ) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecord.kt index 71d86c84c0..235513ac61 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecord.kt @@ -86,6 +86,7 @@ data class NoteRecord( var misskeyChannelId: String? = null, var misskeyChannelName: String? = null, var misskeyIsAcceptingOnlyLikeReaction: Boolean = false, + var misskeyIsNotAcceptingSensitiveReaction: Boolean = false, var myReactions: MutableList? = null, var maxReactionsPerAccount: Int = 0, @@ -169,6 +170,7 @@ data class NoteRecord( misskeyChannelId = t.channel?.id?.channelId misskeyChannelName = t.channel?.name misskeyIsAcceptingOnlyLikeReaction = t.isAcceptingOnlyLikeReaction + misskeyIsNotAcceptingSensitiveReaction = t.isNotAcceptingSensitiveReaction } } customEmojiAspectRatioMap = model.emojis?.mapNotNull { emoji -> @@ -227,7 +229,8 @@ data class NoteRecord( name = misskeyChannelName ?: "" ) }, - isAcceptingOnlyLikeReaction = misskeyIsAcceptingOnlyLikeReaction + isAcceptingOnlyLikeReaction = misskeyIsAcceptingOnlyLikeReaction, + isNotAcceptingSensitiveReaction = misskeyIsNotAcceptingSensitiveReaction, ) } "mastodon" -> { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/Note.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/Note.kt index c43300015e..3208247157 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/Note.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/Note.kt @@ -82,6 +82,7 @@ data class Note( data class Misskey( val channel: SimpleChannelInfo? = null, val isAcceptingOnlyLikeReaction: Boolean = false, + val isNotAcceptingSensitiveReaction: Boolean = false, ) : Type { data class SimpleChannelInfo(val id: Channel.Id, val name: String) From 96904a6c64d0d988844645b5ac166a1725a3dbb6 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 14:20:12 +0900 Subject: [PATCH 204/432] feat: test case --- .../data/infrastructure/notes/impl/db/NoteRecordTest.kt | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/test/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecordTest.kt b/app/src/test/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecordTest.kt index 72fcb8817a..cc8746f909 100644 --- a/app/src/test/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecordTest.kt +++ b/app/src/test/java/net/pantasystem/milktea/data/infrastructure/notes/impl/db/NoteRecordTest.kt @@ -294,7 +294,8 @@ internal class NoteRecordTest { id = Channel.Id(0L, "ch1"), name = "name1", ), - isAcceptingOnlyLikeReaction = false + isAcceptingOnlyLikeReaction = false, + isNotAcceptingSensitiveReaction = true, ) ) record.applyModel(note) @@ -310,6 +311,10 @@ internal class NoteRecordTest { "name1", record.misskeyChannelName ) + Assertions.assertEquals( + true, + record.misskeyIsNotAcceptingSensitiveReaction + ) } @Test @@ -361,7 +366,7 @@ internal class NoteRecordTest { mastodonPureText = "test note", mastodonIsReactionAvailable = true, myReactions = mutableListOf("like"), - maxReactionsPerAccount = 3 + maxReactionsPerAccount = 3, ) val expectedNote = Note( id = Note.Id(accountId = 1, noteId = "note-id"), From dfd4fb2b49828466a026114eb3661af271f463d6 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 16:58:28 +0900 Subject: [PATCH 205/432] =?UTF-8?q?feat:=20=E3=83=A1=E3=83=A2=E3=83=AA?= =?UTF-8?q?=E4=B8=8A=E3=81=AB=E3=82=82=E4=BF=9D=E6=8C=81=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt | 2 +- .../milktea/note/reaction/SaveImageAspectRequestListener.kt | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index b0762ddda8..9429bdbf09 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -43,7 +43,7 @@ object NoteReactionViewHelper { .let { val metrics = context.resources.displayMetrics val imageViewHeightPx = 20 * metrics.density - val imageAspectRatio = emoji.aspectRatio + val imageAspectRatio = ImageAspectRatioCache.get(emoji.url ?: emoji.uri) ?: emoji.aspectRatio if (imageAspectRatio == null) { it } else { diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt index 506894774a..edec3dbfb0 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/SaveImageAspectRequestListener.kt @@ -39,6 +39,7 @@ class SaveImageAspectRequestListener( navigationEntryPoint.customEmojiAspectRatioStore().save( emoji, imageAspectRatio ) + ImageAspectRatioCache.put(emoji.url ?: emoji.uri, imageAspectRatio) return false } From 365e15ec8f7ed0134e3549c179a264e11afdd085 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 18:04:37 +0900 Subject: [PATCH 206/432] fix --- .../data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt index 3e92b76daa..80141ba97a 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/impl/ObjectBoxNoteDataSource.kt @@ -207,7 +207,7 @@ class ObjectBoxNoteDataSource @Inject constructor( override suspend fun clear(): Result = runCancellableCatching { withContext(coroutineDispatcher) { - + noteBox.removeAll() } } From f1aed1c8c75aef21cb13665b477fdf50e643ab6b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 19 May 2023 20:42:52 +0900 Subject: [PATCH 207/432] =?UTF-8?q?feat:=20=E3=83=AC=E3=82=A4=E3=82=A2?= =?UTF-8?q?=E3=82=A6=E3=83=88=E3=82=B7=E3=83=95=E3=83=88=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=E3=82=92=E8=BB=BD=E6=B8=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/reaction/NoteReactionViewHelper.kt | 30 ++++++++++++------- .../src/main/res/layout/item_reaction.xml | 1 - 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 9429bdbf09..35e2dc3ba5 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -5,6 +5,7 @@ import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView +import androidx.core.view.updateLayoutParams import androidx.databinding.BindingAdapter import dagger.hilt.android.EntryPointAccessors import net.pantasystem.milktea.common.glide.GlideApp @@ -17,6 +18,8 @@ import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData object NoteReactionViewHelper { + const val REACTION_IMAGE_WIDTH_SIZE_DP = 20 + @JvmStatic @BindingAdapter("reactionTextTypeView", "reactionImageTypeView", "reaction") fun LinearLayout.bindReactionCount( @@ -37,20 +40,25 @@ object NoteReactionViewHelper { } else { reactionImageTypeView.setMemoVisibility(View.VISIBLE) reactionTextTypeView.setMemoVisibility(View.GONE) + val metrics = context.resources.displayMetrics + val imageViewHeightPx = REACTION_IMAGE_WIDTH_SIZE_DP * metrics.density + val imageAspectRatio = ImageAspectRatioCache.get(emoji.url ?: emoji.uri) ?: emoji.aspectRatio + val imageViewWidthPx = if (imageAspectRatio == null) { + imageViewHeightPx + } else { + (imageViewHeightPx * imageAspectRatio) + } + + reactionImageTypeView.updateLayoutParams { + this@updateLayoutParams.also { + it.height = imageViewHeightPx.toInt() + it.width = imageViewWidthPx.toInt() + } + } GlideApp.with(reactionImageTypeView.context) .load(emoji.url ?: emoji.uri) - .let { - val metrics = context.resources.displayMetrics - val imageViewHeightPx = 20 * metrics.density - val imageAspectRatio = ImageAspectRatioCache.get(emoji.url ?: emoji.uri) ?: emoji.aspectRatio - if (imageAspectRatio == null) { - it - } else { - it.override((imageViewHeightPx * imageAspectRatio).toInt()) - } - } -// .override(min(max(reactionImageTypeView.height, 20), 120)) + .override(imageViewWidthPx.toInt(), imageViewHeightPx.toInt()) .addListener(SaveImageAspectRequestListener(emoji, context)) .into(reactionImageTypeView) } diff --git a/modules/features/note/src/main/res/layout/item_reaction.xml b/modules/features/note/src/main/res/layout/item_reaction.xml index 5b39fa39a4..6a51445682 100644 --- a/modules/features/note/src/main/res/layout/item_reaction.xml +++ b/modules/features/note/src/main/res/layout/item_reaction.xml @@ -23,7 +23,6 @@ android:layout_height="20dp" android:layout_marginEnd="4dp" tools:ignore="ContentDescription" - android:adjustViewBounds="true" android:scaleType="fitCenter" android:layout_weight="1" /> Date: Sat, 20 May 2023 01:10:28 +0900 Subject: [PATCH 208/432] =?UTF-8?q?feat:=20CustomEmoji=E3=81=AEwidth,=20he?= =?UTF-8?q?ight=E3=83=95=E3=82=A3=E3=83=BC=E3=83=AB=E3=83=89=E3=82=92?= =?UTF-8?q?=E6=A7=8B=E9=80=A0=E4=BD=93=E3=81=AB=E5=90=AB=E3=82=81=E3=82=8B?= =?UTF-8?q?=E3=81=9F=E3=82=81=E3=81=ABNetworkDTO=E3=81=A8Model=E3=81=A7?= =?UTF-8?q?=E5=88=86=E9=9B=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/notes/NoteDTOTest.kt | 6 ++-- .../misskey/emoji/CustomEmojiNetworkDTO.kt | 32 +++++++++++++++++++ .../milktea/api/misskey/emoji/EmojisType.kt | 5 ++- .../milktea/api/misskey/notes/NoteDTO.kt | 8 ++--- .../milktea/api/misskey/users/UserDTO.kt | 6 ++-- .../milktea/api/misskey/v13/EmojisResponse.kt | 4 +-- .../data/converters/NoteDTOEntityConverter.kt | 4 +-- .../data/converters/UserDTOEntityConverter.kt | 6 ++-- .../emoji/CustomEmojiApiAdapter.kt | 2 ++ .../instance/MetaRepositoryImpl.kt | 4 ++- 10 files changed, 56 insertions(+), 21 deletions(-) create mode 100644 modules/api/src/main/java/net/pantasystem/milktea/api/misskey/emoji/CustomEmojiNetworkDTO.kt diff --git a/app/src/test/java/jp/panta/misskeyandroidclient/api/notes/NoteDTOTest.kt b/app/src/test/java/jp/panta/misskeyandroidclient/api/notes/NoteDTOTest.kt index 99939d8fda..0ee686862d 100644 --- a/app/src/test/java/jp/panta/misskeyandroidclient/api/notes/NoteDTOTest.kt +++ b/app/src/test/java/jp/panta/misskeyandroidclient/api/notes/NoteDTOTest.kt @@ -3,10 +3,10 @@ package jp.panta.misskeyandroidclient.api.notes import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json +import net.pantasystem.milktea.api.misskey.emoji.CustomEmojiNetworkDTO import net.pantasystem.milktea.api.misskey.emoji.EmojisType import net.pantasystem.milktea.api.misskey.emoji.TestNoteObject import net.pantasystem.milktea.api.misskey.notes.NoteDTO -import net.pantasystem.milktea.model.emoji.Emoji import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test @@ -869,7 +869,7 @@ class NoteDTOTest { [{"name": "hoge", "url": "https://example.com"}] """.trimIndent() val result = builder.decodeFromString(json1) - Assertions.assertEquals(EmojisType.TypeArray(listOf(Emoji(name = "hoge", url = "https://example.com"))), result) + Assertions.assertEquals(EmojisType.TypeArray(listOf(CustomEmojiNetworkDTO(name = "hoge", url = "https://example.com"))), result) } @@ -896,7 +896,7 @@ class NoteDTOTest { """.trimIndent() val result = builder.decodeFromString(json1) Assertions.assertEquals(TestNoteObject(EmojisType.TypeArray( - listOf(Emoji(name = "hoge", url = "https://example.com")) + listOf(CustomEmojiNetworkDTO(name = "hoge", url = "https://example.com")) )), result) } diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/emoji/CustomEmojiNetworkDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/emoji/CustomEmojiNetworkDTO.kt new file mode 100644 index 0000000000..63681d0a61 --- /dev/null +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/emoji/CustomEmojiNetworkDTO.kt @@ -0,0 +1,32 @@ +package net.pantasystem.milktea.api.misskey.emoji + +import kotlinx.serialization.SerialName +import net.pantasystem.milktea.model.emoji.Emoji + +@kotlinx.serialization.Serializable +data class CustomEmojiNetworkDTO( + @SerialName("id") val id: String? = null, + @SerialName("name") val name: String, + @SerialName("host") val host: String? = null, + @SerialName("url") val url: String? = null, + @SerialName("uri") val uri: String? = null, + @SerialName("type") val type: String? = null, + @SerialName("category") val category: String? = null, + @SerialName("aliases") val aliases: List? = null, + @SerialName("width") val width: Int? = null, + @SerialName("height") val height: Int? = null, +) { + fun toModel(aspectRatio: Float? = null): Emoji { + return Emoji( + id = id, + name = name, + host = host, + url = url, + uri = uri, + type = type, + category = category, + aliases = aliases, + aspectRatio = aspectRatio ?: if (width == null || height == null || height <= 0) null else width.toFloat() / height + ) + } +} \ No newline at end of file diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/emoji/EmojisType.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/emoji/EmojisType.kt index d48a324834..d76952c13b 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/emoji/EmojisType.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/emoji/EmojisType.kt @@ -12,13 +12,12 @@ import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonContentPolymorphicSerializer import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject -import net.pantasystem.milktea.model.emoji.Emoji @kotlinx.serialization.Serializable(with = CustomEmojisTypeSerializer::class) sealed interface EmojisType { @kotlinx.serialization.Serializable(with = TypeArraySerializer::class) - data class TypeArray(val emojis: List) : EmojisType + data class TypeArray(val emojis: List) : EmojisType @kotlinx.serialization.Serializable(with = TypeObjectSerializer::class) data class TypeObject(val emojis: Map) : EmojisType @@ -61,7 +60,7 @@ object TypeObjectSerializer : KSerializer { } class TypeArraySerializer : KSerializer { - private val listSerializer = ListSerializer(Emoji.serializer()) + private val listSerializer = ListSerializer(CustomEmojiNetworkDTO.serializer()) override val descriptor: SerialDescriptor = listSerializer.descriptor override fun deserialize(decoder: Decoder): EmojisType.TypeArray { diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt index ea4648415d..a7ae62dfea 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/notes/NoteDTO.kt @@ -6,11 +6,11 @@ import kotlinx.datetime.serializers.InstantIso8601Serializer import kotlinx.serialization.SerialName import net.pantasystem.milktea.api.misskey.auth.App import net.pantasystem.milktea.api.misskey.drive.FilePropertyDTO +import net.pantasystem.milktea.api.misskey.emoji.CustomEmojiNetworkDTO import net.pantasystem.milktea.api.misskey.emoji.CustomEmojisTypeSerializer import net.pantasystem.milktea.api.misskey.emoji.EmojisType import net.pantasystem.milktea.api.misskey.users.UserDTO import net.pantasystem.milktea.common.serializations.EnumIgnoreUnknownSerializer -import net.pantasystem.milktea.model.emoji.Emoji import java.io.Serializable @kotlinx.serialization.Serializable @@ -126,15 +126,15 @@ data class NoteDTO( EmojisType.None -> emptyList() is EmojisType.TypeArray -> emojis.emojis is EmojisType.TypeObject -> emojis.emojis.map { - Emoji(name = it.key, url = it.value) + CustomEmojiNetworkDTO(name = it.key, url = it.value) } null -> emptyList() } - val emojiList: List = when(rawEmojis) { + val emojiList: List = when(rawEmojis) { EmojisType.None -> emptyList() is EmojisType.TypeArray -> rawEmojis.emojis is EmojisType.TypeObject -> (rawEmojis.emojis).map { - Emoji(name = it.key, url = it.value, uri = it.value) + CustomEmojiNetworkDTO(name = it.key, url = it.value, uri = it.value) } null -> emptyList() } + reactionEmojiList diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/users/UserDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/users/UserDTO.kt index 739aaecc76..68518962c9 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/users/UserDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/users/UserDTO.kt @@ -4,10 +4,10 @@ package net.pantasystem.milktea.api.misskey.users import kotlinx.datetime.Instant import kotlinx.datetime.LocalDate import kotlinx.serialization.SerialName +import net.pantasystem.milktea.api.misskey.emoji.CustomEmojiNetworkDTO import net.pantasystem.milktea.api.misskey.emoji.CustomEmojisTypeSerializer import net.pantasystem.milktea.api.misskey.emoji.EmojisType import net.pantasystem.milktea.api.misskey.notes.NoteDTO -import net.pantasystem.milktea.model.emoji.Emoji import java.io.Serializable /** @@ -141,11 +141,11 @@ data class UserDTO( val themeColor: String? = null, ) - val emojiList: List? = when(rawEmojis) { + val emojiList: List? = when(rawEmojis) { EmojisType.None -> null is EmojisType.TypeArray -> rawEmojis.emojis is EmojisType.TypeObject -> rawEmojis.emojis.map { - Emoji(name = it.key, url = it.value, uri = it.value) + CustomEmojiNetworkDTO(name = it.key, url = it.value, uri = it.value) } null -> null } diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/v13/EmojisResponse.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/v13/EmojisResponse.kt index b121eacb45..e4bdb3813e 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/v13/EmojisResponse.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/v13/EmojisResponse.kt @@ -1,10 +1,10 @@ package net.pantasystem.milktea.api.misskey.v13 import kotlinx.serialization.SerialName -import net.pantasystem.milktea.model.emoji.Emoji +import net.pantasystem.milktea.api.misskey.emoji.CustomEmojiNetworkDTO @kotlinx.serialization.Serializable data class EmojisResponse( @SerialName("emojis") - val emojis: List + val emojis: List ) \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverter.kt index b33316c914..0a47991c54 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/NoteDTOEntityConverter.kt @@ -49,9 +49,7 @@ class NoteDTOEntityConverter @Inject constructor( visibility = visibility, localOnly = noteDTO.localOnly, emojis = (noteDTO.emojiList + (noteDTO.reactionEmojiList)).map { - it.copy( - aspectRatio = aspects[it.url ?: it.uri] - ) + it.toModel(aspects[it.url ?: it.uri]) }, app = null, fileIds = noteDTO.fileIds?.map { FileProperty.Id(account.accountId, it) }, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt index 8f351987c7..fabfe5cccb 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt @@ -25,7 +25,9 @@ class UserDTOEntityConverter @Inject constructor() { return User.Detail( id = User.Id(account.accountId, userDTO.id), avatarUrl = userDTO.avatarUrl, - emojis = userDTO.emojiList ?: emptyList(), + emojis = userDTO.emojiList?.map { + it.toModel() + } ?: emptyList(), isBot = userDTO.isBot, isCat = userDTO.isCat, name = userDTO.name, @@ -68,7 +70,7 @@ class UserDTOEntityConverter @Inject constructor() { return User.Simple( id = User.Id(account.accountId, userDTO.id), avatarUrl = userDTO.avatarUrl, - emojis = userDTO.emojiList ?: emptyList(), + emojis = userDTO.emojiList?.map { it.toModel() } ?: emptyList(), isBot = userDTO.isBot, isCat = userDTO.isCat, name = userDTO.name, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt index 98e2f69977..3036dc52a7 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/emoji/CustomEmojiApiAdapter.kt @@ -50,6 +50,8 @@ internal class CustomEmojiApiAdapterImpl @Inject constructor( .throwIfHasError() .body() emojis?.emojis?.map { + it.toModel() + }?.map { it.copy( url = if (it.url == null) V13EmojiUrlResolver.resolve(it, "https://${nodeInfo.host}") else it.url, uri = if (it.uri == null) V13EmojiUrlResolver.resolve(it, "https://${nodeInfo.host}") else it.uri, diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MetaRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MetaRepositoryImpl.kt index 4c42cfc2a8..36458a07b6 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MetaRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/instance/MetaRepositoryImpl.kt @@ -88,6 +88,8 @@ class MetaRepositoryImpl @Inject constructor( } private suspend fun fetchEmojis(instanceDomain: String): List? { - return misskeyAPIProvider.get(instanceDomain).getEmojis(EmptyRequest).throwIfHasError().body()?.emojis + return misskeyAPIProvider.get(instanceDomain).getEmojis(EmptyRequest).throwIfHasError().body()?.emojis?.map { + it.toModel() + } } } \ No newline at end of file From aebb3204869f905880522334d2a8f1467618378b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 20 May 2023 01:25:17 +0900 Subject: [PATCH 209/432] =?UTF-8?q?feat:=20EmojiSpan=E3=81=ABaspectRatio?= =?UTF-8?q?=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/text/DrawableEmojiSpan.kt | 2 +- .../common_android/ui/text/EmojiSpan.kt | 21 ++++++++++++++----- .../milktea/common_android_ui/MFMDecorator.kt | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DrawableEmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DrawableEmojiSpan.kt index a646bd97b3..7fe5764f8d 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DrawableEmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DrawableEmojiSpan.kt @@ -9,7 +9,7 @@ import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.github.penfeizhou.animation.apng.APNGDrawable -class DrawableEmojiSpan(var adapter: EmojiAdapter?, k: Any?) : EmojiSpan(k){ +class DrawableEmojiSpan(var adapter: EmojiAdapter?, k: Any?, aspectRatio: Float? = null) : EmojiSpan(k, aspectRatio = aspectRatio){ //val weakReference: WeakReference = WeakReference(view) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt index a2b274d3e6..58703e9a6c 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt @@ -7,7 +7,7 @@ import android.text.TextPaint import android.text.style.ReplacementSpan import kotlin.math.min -abstract class EmojiSpan(val key: T) : ReplacementSpan(){ +abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : ReplacementSpan(){ companion object { private val drawableSizeCache = mutableMapOf() @@ -37,6 +37,8 @@ abstract class EmojiSpan(val key: T) : ReplacementSpan(){ end: Int, fm: Paint.FontMetricsInt? ): Int { + val textHeight = paint.textSize + val drawable = imageDrawable val size = key?.let { drawableSizeCache[key] @@ -45,6 +47,11 @@ abstract class EmojiSpan(val key: T) : ReplacementSpan(){ intrinsicHeight = it.intrinsicHeight, intrinsicWidth = it.intrinsicWidth ) + } ?: aspectRatio?.let { + EmojiSizeCache( + intrinsicHeight = textHeight.toInt(), + intrinsicWidth = (textHeight * aspectRatio).toInt() + ) } key?.run { drawableSizeCache[key] ?: drawable?.let { @@ -72,7 +79,6 @@ abstract class EmojiSpan(val key: T) : ReplacementSpan(){ beforeTextSize = 0 - val textHeight = paint.textSize val imageWidth = size.intrinsicWidth val imageHeight = size.intrinsicHeight @@ -114,6 +120,7 @@ abstract class EmojiSpan(val key: T) : ReplacementSpan(){ private fun updateImageDrawableSize(paint: Paint) { + val emojiHeight = min((paint.textSize).toInt(), 128) val drawable = imageDrawable val size = key?.let { drawableSizeCache[key] @@ -122,20 +129,24 @@ abstract class EmojiSpan(val key: T) : ReplacementSpan(){ intrinsicWidth = it.intrinsicWidth, intrinsicHeight = it.intrinsicHeight ) + } ?: aspectRatio?.let { + EmojiSizeCache( + intrinsicHeight = emojiHeight, + intrinsicWidth = (emojiHeight.toFloat() * aspectRatio).toInt() + ) } ?: return key?.run { drawableSizeCache[key] = size } val imageWidth = size.intrinsicWidth val imageHeight = size.intrinsicHeight - val emojiHeight = min((paint.textSize).toInt(), 640) val unknownEmojiSize = imageWidth <= 0 || imageHeight <= 0 if (beforeTextSize != 0 && beforeTextSize != emojiHeight || unknownEmojiSize) { if (!isSizeComputed) { beforeTextSize = emojiHeight imageDrawable?.setBounds(0, 0, emojiHeight, emojiHeight) - isSizeComputed = true + isSizeComputed = imageDrawable != null } return } @@ -147,7 +158,7 @@ abstract class EmojiSpan(val key: T) : ReplacementSpan(){ if (!isSizeComputed) { textHeight = emojiHeight textWidth = scaledImageWidth - isSizeComputed = true + isSizeComputed = imageDrawable != null imageDrawable?.setBounds(0, 0, scaledImageWidth, emojiHeight) } } diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt index 80efb5a8ee..714c63dd74 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt @@ -304,7 +304,7 @@ object MFMDecorator { return } textView.get()?.let { textView -> - val emojiSpan = DrawableEmojiSpan(emojiAdapter, emojiElement.emoji.url) + val emojiSpan = DrawableEmojiSpan(emojiAdapter, emojiElement.emoji.url, emojiElement.emoji.aspectRatio) spannableString.setSpan(emojiSpan, skippedEmoji.start, skippedEmoji.end, 0) GlideApp.with(textView) .load(emojiElement.emoji.url) From a44f86da230afef3e7b1291be88c900e5f6f6399 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 20 May 2023 01:54:51 +0900 Subject: [PATCH 210/432] =?UTF-8?q?feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=81=AE=E8=A7=A3=E6=B1=BA=E3=82=92Convert=E6=99=82=E3=81=AB?= =?UTF-8?q?=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/text/CustomEmojiDecorator.kt | 32 ++++++++++++----- .../data/converters/UserDTOEntityConverter.kt | 36 ++++++++++++++++--- .../model/emoji/SimpleCustomEmojiParser.kt | 4 +-- 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt index 3d5378fa0a..fe219e3c49 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt @@ -8,7 +8,6 @@ import net.pantasystem.milktea.model.emoji.CustomEmojiParsedResult import net.pantasystem.milktea.model.emoji.CustomEmojiParser import net.pantasystem.milktea.model.emoji.Emoji import net.pantasystem.milktea.model.emoji.EmojiResolvedType -import net.pantasystem.milktea.model.instance.HostWithVersion class CustomEmojiDecorator { @@ -30,9 +29,13 @@ class CustomEmojiDecorator { text, ) result.emojis.filter { - HostWithVersion.isOverV13(accountHost) || it.result is EmojiResolvedType.Resolved + it.result is EmojiResolvedType.Resolved }.map { - val span = DrawableEmojiSpan(emojiAdapter, it.result.getUrl(accountHost)) + val span = DrawableEmojiSpan( + emojiAdapter, + it.result.getUrl(accountHost), + (it.result as? EmojiResolvedType.Resolved)?.emoji?.aspectRatio + ) GlideApp.with(view) .asDrawable() .load(it.result.getUrl(accountHost)) @@ -51,9 +54,13 @@ class CustomEmojiDecorator { val builder = SpannableStringBuilder(result.text) result.emojis.filter { - HostWithVersion.isOverV13(accountHost) || it.result is EmojiResolvedType.Resolved + it.result is EmojiResolvedType.Resolved }.map { - val span = DrawableEmojiSpan(emojiAdapter, it.result.getUrl(accountHost)) + val span = DrawableEmojiSpan( + emojiAdapter, + it.result.getUrl(accountHost), + (it.result as? EmojiResolvedType.Resolved)?.emoji?.aspectRatio + ) GlideApp.with(view) .asDrawable() .override(view.textSize.toInt()) @@ -66,15 +73,24 @@ class CustomEmojiDecorator { return builder } - fun decorate(spanned: Spanned, accountHost: String?, result: CustomEmojiParsedResult, view: TextView): Spanned { + fun decorate( + spanned: Spanned, + accountHost: String?, + result: CustomEmojiParsedResult, + view: TextView, + ): Spanned { val emojiAdapter = EmojiAdapter(view) val builder = SpannableStringBuilder(spanned) result.emojis.filter { - HostWithVersion.isOverV13(accountHost) || it.result is EmojiResolvedType.Resolved + it.result is EmojiResolvedType.Resolved }.map { - val span = DrawableEmojiSpan(emojiAdapter, it.result.getUrl(accountHost)) + val span = DrawableEmojiSpan( + emojiAdapter, + it.result.getUrl(accountHost), + (it.result as? EmojiResolvedType.Resolved)?.emoji?.aspectRatio + ) GlideApp.with(view) .asDrawable() .load(it.result.getUrl(accountHost)) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt index fabfe5cccb..6bfddc1592 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt @@ -2,13 +2,20 @@ package net.pantasystem.milktea.data.converters import net.pantasystem.milktea.api.misskey.users.UserDTO import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.emoji.CustomEmojiAspectRatioDataSource +import net.pantasystem.milktea.model.emoji.CustomEmojiParser +import net.pantasystem.milktea.model.emoji.CustomEmojiRepository +import net.pantasystem.milktea.model.emoji.EmojiResolvedType import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.user.User import javax.inject.Inject import javax.inject.Singleton @Singleton -class UserDTOEntityConverter @Inject constructor() { +class UserDTOEntityConverter @Inject constructor( + private val customEmojiRepository: CustomEmojiRepository, + private val customEmojiAspectRatioDataSource: CustomEmojiAspectRatioDataSource, +) { suspend fun convert(account: Account, userDTO: UserDTO, isDetail: Boolean = false): User { val instanceInfo = userDTO.instance?.let { @@ -21,13 +28,32 @@ class UserDTOEntityConverter @Inject constructor() { themeColor = it.themeColor ) } + val aspects = customEmojiAspectRatioDataSource.findIn( + userDTO.emojiList?.mapNotNull { + it.url ?: it.uri + } ?: emptyList() + ).getOrNull()?.associateBy { + it.uri + } + var emojis = userDTO.emojiList?.map { + it.toModel(aspects?.get(it.url ?: it.uri)?.aspectRatio) + } ?: emptyList() + emojis = (emojis + CustomEmojiParser.parse( + userDTO.host ?: account.getHost(), + emojis, + userDTO.name ?: userDTO.userName, + customEmojiRepository.getAndConvertToMap(account.getHost()), + ).emojis.mapNotNull { + (it.result as? EmojiResolvedType.Resolved)?.emoji + }).distinctBy { + it.name to it.host to it.url to it.uri + } + if (isDetail) { return User.Detail( id = User.Id(account.accountId, userDTO.id), avatarUrl = userDTO.avatarUrl, - emojis = userDTO.emojiList?.map { - it.toModel() - } ?: emptyList(), + emojis = emojis, isBot = userDTO.isBot, isCat = userDTO.isCat, name = userDTO.name, @@ -70,7 +96,7 @@ class UserDTOEntityConverter @Inject constructor() { return User.Simple( id = User.Id(account.accountId, userDTO.id), avatarUrl = userDTO.avatarUrl, - emojis = userDTO.emojiList?.map { it.toModel() } ?: emptyList(), + emojis = emojis, isBot = userDTO.isBot, isCat = userDTO.isCat, name = userDTO.name, diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/SimpleCustomEmojiParser.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/SimpleCustomEmojiParser.kt index d86412b2fd..1b503dff02 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/SimpleCustomEmojiParser.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/emoji/SimpleCustomEmojiParser.kt @@ -3,7 +3,7 @@ package net.pantasystem.milktea.model.emoji object CustomEmojiParser { - fun parse(sourceHost: String?, emojis: List?, text: String): CustomEmojiParsedResult { + fun parse(sourceHost: String?, emojis: List?, text: String, instanceEmojis: Map? = null): CustomEmojiParsedResult { val emojiMap = emojis?.associateBy { it.name } @@ -21,7 +21,7 @@ object CustomEmojiParser { } else { null } - var emoji: EmojiResolvedType? = emojiMap?.get(tag)?.let { + var emoji: EmojiResolvedType? = (emojiMap?.get(tag) ?: instanceEmojis?.get(tag))?.let { if (sourceHost == null) { null } else { From 32cf59fdbdfd333b4aefb12496689384f4ef8de8 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 20 May 2023 01:59:00 +0900 Subject: [PATCH 211/432] =?UTF-8?q?feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=81=AE=E8=A7=A3=E6=B1=BA=E3=82=92Convert=E6=99=82=E3=81=AB?= =?UTF-8?q?=E8=A1=8C=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/converters/UserDTOEntityConverter.kt | 11 ++-------- .../converters/UserDTOEntityConverterTest.kt | 20 +++++++++++++++++-- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt index 6bfddc1592..65fdb5f2ed 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverter.kt @@ -14,7 +14,6 @@ import javax.inject.Singleton @Singleton class UserDTOEntityConverter @Inject constructor( private val customEmojiRepository: CustomEmojiRepository, - private val customEmojiAspectRatioDataSource: CustomEmojiAspectRatioDataSource, ) { suspend fun convert(account: Account, userDTO: UserDTO, isDetail: Boolean = false): User { @@ -28,15 +27,9 @@ class UserDTOEntityConverter @Inject constructor( themeColor = it.themeColor ) } - val aspects = customEmojiAspectRatioDataSource.findIn( - userDTO.emojiList?.mapNotNull { - it.url ?: it.uri - } ?: emptyList() - ).getOrNull()?.associateBy { - it.uri - } + var emojis = userDTO.emojiList?.map { - it.toModel(aspects?.get(it.url ?: it.uri)?.aspectRatio) + it.toModel() } ?: emptyList() emojis = (emojis + CustomEmojiParser.parse( userDTO.host ?: account.getHost(), diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverterTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverterTest.kt index 7f62ab3dfe..932395687b 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverterTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/converters/UserDTOEntityConverterTest.kt @@ -10,6 +10,9 @@ import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.user.User import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Test +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock import kotlin.time.Duration.Companion.days class UserDTOEntityConverterTest { @@ -17,7 +20,14 @@ class UserDTOEntityConverterTest { @OptIn(ExperimentalCoroutinesApi::class) @Test fun converter_GiveDetailedData() = runTest { - val converter = UserDTOEntityConverter() + val converter = UserDTOEntityConverter( + mock() { + onBlocking { + getAndConvertToMap(any()) + } doReturn mapOf() + }, + + ) val userDTO = UserDTO( id = "test-id", @@ -89,7 +99,13 @@ class UserDTOEntityConverterTest { @Test @OptIn(ExperimentalCoroutinesApi::class) fun convert_GiveSimpleUser() = runTest { - val converter = UserDTOEntityConverter() + val converter = UserDTOEntityConverter( + mock() { + onBlocking { + getAndConvertToMap(any()) + } doReturn mapOf() + }, + ) val userDTO = UserDTO( id = "test-id", From 9aa6ba218884445db3273db87829a2d139b69ef3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 21 May 2023 15:10:35 +0900 Subject: [PATCH 212/432] fix: initial tab scroll position --- .../net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt index c1b46a4be4..f52ee888d8 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt @@ -211,7 +211,7 @@ class EmojiSelectionBinder( val tab = tabLayout.newTab().apply { text = it.getString(context) } - tabLayout.addTab(tab) + tabLayout.addTab(tab, false) } tabbedListMediator?.detach() tabbedListMediator = TabbedListMediator( @@ -225,7 +225,6 @@ class EmojiSelectionBinder( } ) tabbedListMediator?.attach() - recyclerView.scrollToPosition(0) }.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.RESUMED).launchIn(scope) From 90b1e0823f15094ad789ab89f9773a427f0e471e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 21 May 2023 15:41:36 +0900 Subject: [PATCH 213/432] =?UTF-8?q?feat:=20=E4=B8=8B=E6=9B=B8=E3=81=8D?= =?UTF-8?q?=E4=B8=80=E8=A6=A7=E3=83=9C=E3=82=BF=E3=83=B3=E3=82=92=E8=BF=BD?= =?UTF-8?q?=E5=8A=A0=E3=81=97=E9=81=B8=E6=8A=9E=E3=81=97=E3=81=9F=E3=82=89?= =?UTF-8?q?=E4=B8=8B=E6=9B=B8=E3=81=8D=E4=B8=80=E8=A6=A7=E3=82=92=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/editor/NoteEditorFragment.kt | 7 ++++++- .../note/editor/NoteEditorUserActionMenuLayout.kt | 12 ++++++++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index b4b0e939d4..0030c0827c 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -55,6 +55,7 @@ import net.pantasystem.milktea.model.instance.FeatureType import net.pantasystem.milktea.model.instance.MetaRepository import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.user.User +import net.pantasystem.milktea.note.DraftNotesActivity import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentNoteEditorBinding import net.pantasystem.milktea.note.databinding.ViewNoteEditorToolbarBinding @@ -347,7 +348,11 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti }, onToggleCwButtonClicked = { noteEditorViewModel.changeCwEnabled() - }) + }, + onSelectDraftNoteButtonClicked = { + startActivity(Intent(requireActivity(), DraftNotesActivity::class.java)) + } + ) } } viewLifecycleOwner.lifecycleScope.launch { diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorUserActionMenuLayout.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorUserActionMenuLayout.kt index 30c227db06..b4e38e17b0 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorUserActionMenuLayout.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorUserActionMenuLayout.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.EditNote import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -31,7 +32,8 @@ fun NoteEditorUserActionMenuLayout( onTogglePollButtonClicked: () -> Unit, onSelectMentionUsersButtonClicked: () -> Unit, onSelectEmojiButtonClicked: () -> Unit, - onToggleCwButtonClicked: () -> Unit + onToggleCwButtonClicked: () -> Unit, + onSelectDraftNoteButtonClicked: () -> Unit ) { var isShowFilePickerDropDownMenu: Boolean by remember { mutableStateOf(false) @@ -133,6 +135,11 @@ fun NoteEditorUserActionMenuLayout( ) } } + MenuItemLayout() { + IconButton(onClick = onSelectDraftNoteButtonClicked) { + Icon(Icons.Outlined.EditNote, contentDescription = null, tint = iconColor) + } + } } } @@ -165,7 +172,8 @@ fun Preview_NoteEditorUserActionMenuLayout() { onTogglePollButtonClicked = {}, onSelectMentionUsersButtonClicked = {}, onSelectEmojiButtonClicked = {}, - onToggleCwButtonClicked = {} + onToggleCwButtonClicked = {}, + onSelectDraftNoteButtonClicked = {} ) } } From 9eb6ba520650d5d0938c550e642ba5e9bdccb6d9 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 21 May 2023 15:51:31 +0900 Subject: [PATCH 214/432] =?UTF-8?q?feat:=20=E9=81=B8=E6=8A=9E=E3=83=A2?= =?UTF-8?q?=E3=83=BC=E3=83=89=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/draft/DraftNoteCard.kt | 48 +++++++++++-------- .../milktea/note/draft/DraftNotesFragment.kt | 35 +++++++++++--- .../milktea/note/draft/DraftNotesScreen.kt | 8 +++- .../milktea/note/editor/NoteEditorFragment.kt | 4 +- 4 files changed, 67 insertions(+), 28 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNoteCard.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNoteCard.kt index 5125b57fd3..fa31be830e 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNoteCard.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNoteCard.kt @@ -18,14 +18,17 @@ import net.pantasystem.milktea.model.notes.draft.DraftNoteFile import net.pantasystem.milktea.note.R +@OptIn(ExperimentalMaterialApi::class) @Composable fun DraftNoteCard( draftNote: DraftNote, isVisibleContent: Boolean, + isPickMode: Boolean, onAction: (DraftNoteCardAction) -> Unit, onDetach: (DraftNoteFile) -> Unit, onShow: (DraftNoteFile) -> Unit, onToggleSensitive: (DraftNoteFile) -> Unit, + onSelect: (DraftNote) -> Unit, ) { var confirmDeleteDraftNoteId: Long? by remember { @@ -53,6 +56,11 @@ fun DraftNoteCard( .padding(8.dp) .fillMaxWidth(), shape = RoundedCornerShape(8.dp), + onClick = { + if (isPickMode) { + onSelect(draftNote) + } + } ) { Column( @@ -95,25 +103,27 @@ fun DraftNoteCard( ) } - Row( - horizontalArrangement = Arrangement.End, - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - IconButton(onClick = { confirmDeleteDraftNoteId = draftNote.draftNoteId }) { - Icon( - Icons.Default.Delete, - contentDescription = stringResource(id = R.string.delete_draft_note) - ) - } - Spacer(modifier = Modifier.width(4.dp)) - IconButton(onClick = { - onAction(DraftNoteCardAction.Edit(draftNote)) - }) { - Icon( - Icons.Default.Edit, - contentDescription = stringResource(id = R.string.edit) - ) + if (!isPickMode) { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth() + ) { + IconButton(onClick = { confirmDeleteDraftNoteId = draftNote.draftNoteId }) { + Icon( + Icons.Default.Delete, + contentDescription = stringResource(id = R.string.delete_draft_note) + ) + } + Spacer(modifier = Modifier.width(4.dp)) + IconButton(onClick = { + onAction(DraftNoteCardAction.Edit(draftNote)) + }) { + Icon( + Icons.Default.Edit, + contentDescription = stringResource(id = R.string.edit) + ) + } } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesFragment.kt index 1b4745ce64..84fd900ca0 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesFragment.kt @@ -1,5 +1,7 @@ package net.pantasystem.milktea.note.draft +import android.app.Activity.RESULT_OK +import android.content.Intent import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -34,33 +36,54 @@ class DraftNotesFragment : Fragment() { override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? + savedInstanceState: Bundle?, ): View { + val action = activity?.intent?.action + val isSelectMode = action == Intent.ACTION_PICK return ComposeView(requireContext()).apply { setContent { MdcTheme { DraftNotesScreen( + isPickMode = isSelectMode, viewModel = viewModel, onNavigateUp = { - requireActivity().finish() + requireActivity().finish() }, onEdit = { val intent = NoteEditorActivity.newBundle( requireContext(), draftNoteId = it.draftNoteId ) - requireActivity().startActivityFromFragment(this@DraftNotesFragment, intent, 300) + requireActivity().startActivityFromFragment( + this@DraftNotesFragment, + intent, + 300 + ) }, onShowFile = { val intent = mediaNavigation.newIntent( MediaNavigationArgs.AFile( - when(it) { - is DraftNoteFile.Local -> FilePreviewSource.Local(AppFile.from(it) as AppFile.Local) - is DraftNoteFile.Remote -> FilePreviewSource.Remote(AppFile.Remote(it.fileProperty.id), it.fileProperty) + when (it) { + is DraftNoteFile.Local -> FilePreviewSource.Local( + AppFile.from( + it + ) as AppFile.Local + ) + is DraftNoteFile.Remote -> FilePreviewSource.Remote( + AppFile.Remote( + it.fileProperty.id + ), it.fileProperty + ) } ) ) startActivity(intent) + }, + onSelect = { + val intent = Intent() + intent.putExtra("DRAFT_NOTE_ID", it.draftNoteId) + requireActivity().setResult(RESULT_OK, intent) + requireActivity().finish() } ) } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesScreen.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesScreen.kt index 671fd29ebc..eddb713f58 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesScreen.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesScreen.kt @@ -23,10 +23,12 @@ import net.pantasystem.milktea.note.draft.viewmodel.DraftNotesViewModel @Composable fun DraftNotesScreen( + isPickMode: Boolean, viewModel: DraftNotesViewModel, onShowFile: (DraftNoteFile) -> Unit, onNavigateUp: () -> Unit, - onEdit: (DraftNote) -> Unit + onEdit: (DraftNote) -> Unit, + onSelect: (DraftNote) -> Unit, ) { val state by viewModel.uiState.collectAsState() @@ -64,6 +66,7 @@ fun DraftNotesScreen( DraftNoteCard( draftNote = item.draftNote, isVisibleContent = item.isVisibleContent, + isPickMode = isPickMode, onAction = { action -> when (action) { is DraftNoteCardAction.DeleteDraftNote -> { @@ -81,7 +84,8 @@ fun DraftNotesScreen( }, onToggleSensitive = { e -> viewModel.toggleSensitive(e) - } + }, + onSelect = onSelect ) } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index 0030c0827c..38bea24deb 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -350,7 +350,9 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti noteEditorViewModel.changeCwEnabled() }, onSelectDraftNoteButtonClicked = { - startActivity(Intent(requireActivity(), DraftNotesActivity::class.java)) + startActivity(Intent(requireActivity(), DraftNotesActivity::class.java).also { + it.action = Intent.ACTION_PICK + }) } ) } From ffe862645d535fb5fe95b7c390cd5e7017ee561b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 21 May 2023 15:55:45 +0900 Subject: [PATCH 215/432] =?UTF-8?q?feat:=20=E9=81=B8=E6=8A=9E=E3=81=97?= =?UTF-8?q?=E3=81=9F=E4=B8=8B=E6=9B=B8=E3=81=8D=E3=82=92=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=E3=81=AB=E5=8F=8D=E6=98=A0=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/DraftNotesActivity.kt | 4 ++ .../milktea/note/draft/DraftNotesFragment.kt | 3 +- .../milktea/note/editor/NoteEditorFragment.kt | 62 ++++++++++++------- 3 files changed, 46 insertions(+), 23 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/DraftNotesActivity.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/DraftNotesActivity.kt index 1c7e2ccd6e..163fa1ffd3 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/DraftNotesActivity.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/DraftNotesActivity.kt @@ -11,6 +11,10 @@ import javax.inject.Inject @AndroidEntryPoint class DraftNotesActivity : AppCompatActivity() { + companion object { + const val EXTRA_DRAFT_NOTE_ID = "DraftNotesActivity.EXTRA_DRAFT_NOTE_ID" + } + @Inject lateinit var applyTheme: ApplyTheme diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesFragment.kt index 84fd900ca0..0cca3ce5ac 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesFragment.kt @@ -17,6 +17,7 @@ import net.pantasystem.milktea.model.file.AppFile import net.pantasystem.milktea.model.file.FilePreviewSource import net.pantasystem.milktea.model.file.from import net.pantasystem.milktea.model.notes.draft.DraftNoteFile +import net.pantasystem.milktea.note.DraftNotesActivity import net.pantasystem.milktea.note.NoteEditorActivity import net.pantasystem.milktea.note.draft.viewmodel.DraftNotesViewModel import javax.inject.Inject @@ -81,7 +82,7 @@ class DraftNotesFragment : Fragment() { }, onSelect = { val intent = Intent() - intent.putExtra("DRAFT_NOTE_ID", it.draftNoteId) + intent.putExtra(DraftNotesActivity.EXTRA_DRAFT_NOTE_ID, it.draftNoteId) requireActivity().setResult(RESULT_OK, intent) requireActivity().finish() } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index 38bea24deb..1537f78270 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -350,9 +350,14 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti noteEditorViewModel.changeCwEnabled() }, onSelectDraftNoteButtonClicked = { - startActivity(Intent(requireActivity(), DraftNotesActivity::class.java).also { - it.action = Intent.ACTION_PICK - }) + pickDraftNoteActivityResult.launch( + Intent( + requireActivity(), + DraftNotesActivity::class.java + ).also { + it.action = Intent.ACTION_PICK + }, + ) } ) } @@ -377,7 +382,8 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti binding.inputMain.addTextChangedListener( onTextChanged = { text, start, _, count -> - val inputText = text?.substring(start, start + count)?: return@addTextChangedListener + val inputText = + text?.substring(start, start + count) ?: return@addTextChangedListener if (UrlPatternChecker.isMatch(inputText)) { lifecycleScope.launch { if (noteEditorViewModel.canQuote()) { @@ -536,7 +542,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti override fun onSelect(emoji: Emoji) { - when(noteEditorViewModel.focusType) { + when (noteEditorViewModel.focusType) { NoteEditorFocusEditTextType.Cw -> { val pos = binding.cw.selectionEnd noteEditorViewModel.addEmoji(emoji, pos).let { newPos -> @@ -556,7 +562,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti } override fun onSelect(emoji: String) { - when(noteEditorViewModel.focusType) { + when (noteEditorViewModel.focusType) { NoteEditorFocusEditTextType.Cw -> { val pos = binding.cw.selectionEnd noteEditorViewModel.addEmoji(emoji, pos).let { newPos -> @@ -754,6 +760,18 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti } } + private val pickDraftNoteActivityResult = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result -> + val draftNoteId = + result.data?.getLongExtra(DraftNotesActivity.EXTRA_DRAFT_NOTE_ID, -1)?.takeIf { + it > 0L + } + if (draftNoteId != null) { + noteEditorViewModel.setDraftNoteId(draftNoteId) + } + } + private val openLocalStorageResult = registerForActivityResult(ActivityResultContracts.OpenMultipleDocuments()) { uris -> uris?.map { uri -> @@ -790,39 +808,39 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti private val selectUserResult = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> - if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) { - val changed = - result.data?.getSerializableExtra(SearchAndSelectUserNavigation.EXTRA_SELECTED_USER_CHANGED_DIFF) as? ChangedDiffResult - if (changed != null) { - noteEditorViewModel.setAddress(changed.added, changed.removed) - } + if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) { + val changed = + result.data?.getSerializableExtra(SearchAndSelectUserNavigation.EXTRA_SELECTED_USER_CHANGED_DIFF) as? ChangedDiffResult + if (changed != null) { + noteEditorViewModel.setAddress(changed.added, changed.removed) } } + } @Suppress("DEPRECATION") private val selectMentionToUserResult = registerForActivityResult( ActivityResultContracts.StartActivityForResult() ) { result -> - if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) { - val changed = - result.data?.getSerializableExtra(SearchAndSelectUserNavigation.EXTRA_SELECTED_USER_CHANGED_DIFF) as? ChangedDiffResult - - if (changed != null) { - addMentionUserNames(changed.selectedUserNames) - } + if (result.resultCode == AppCompatActivity.RESULT_OK && result.data != null) { + val changed = + result.data?.getSerializableExtra(SearchAndSelectUserNavigation.EXTRA_SELECTED_USER_CHANGED_DIFF) as? ChangedDiffResult + if (changed != null) { + addMentionUserNames(changed.selectedUserNames) } + } + } private val pickMultipleMedia = registerForActivityResult( ActivityResultContracts.PickMultipleVisualMedia() ) { uris -> - uris?.map { - appendFile(it) - } + uris?.map { + appendFile(it) } + } private fun appendFile(uri: Uri) { // NOTE: 選択したファイルに対して永続的なアクセス権を得るようにしている From e7adf71b2dc7dfe003ebad548877a72b68c97029 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 21 May 2023 15:57:05 +0900 Subject: [PATCH 216/432] =?UTF-8?q?feat:=20=E8=A1=A8=E7=A4=BA=E9=A0=86?= =?UTF-8?q?=E5=BA=8F=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/NoteEditorUserActionMenuLayout.kt | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorUserActionMenuLayout.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorUserActionMenuLayout.kt index b4e38e17b0..76dd5a8f0f 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorUserActionMenuLayout.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorUserActionMenuLayout.kt @@ -33,7 +33,7 @@ fun NoteEditorUserActionMenuLayout( onSelectMentionUsersButtonClicked: () -> Unit, onSelectEmojiButtonClicked: () -> Unit, onToggleCwButtonClicked: () -> Unit, - onSelectDraftNoteButtonClicked: () -> Unit + onSelectDraftNoteButtonClicked: () -> Unit, ) { var isShowFilePickerDropDownMenu: Boolean by remember { mutableStateOf(false) @@ -126,20 +126,27 @@ fun NoteEditorUserActionMenuLayout( ) } } + MenuItemLayout { - IconButton(onClick = onSelectEmojiButtonClicked) { + IconButton(onClick = onSelectDraftNoteButtonClicked) { Icon( - Icons.Default.EmojiEmotions, + Icons.Outlined.EditNote, contentDescription = null, tint = iconColor ) } } - MenuItemLayout() { - IconButton(onClick = onSelectDraftNoteButtonClicked) { - Icon(Icons.Outlined.EditNote, contentDescription = null, tint = iconColor) + + MenuItemLayout { + IconButton(onClick = onSelectEmojiButtonClicked) { + Icon( + Icons.Default.EmojiEmotions, + contentDescription = null, + tint = iconColor + ) } } + } } From 4401258b91fb25e5dc724d1b9fd9b8bccaf8bb50 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 21 May 2023 16:00:48 +0900 Subject: [PATCH 217/432] =?UTF-8?q?feat:=20=E7=8A=B6=E6=85=8B=E3=81=AB?= =?UTF-8?q?=E5=BF=9C=E3=81=98=E3=81=A6=E3=82=BF=E3=82=A4=E3=83=88=E3=83=AB?= =?UTF-8?q?=E3=82=92=E5=A4=89=E3=81=88=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 1 + .../net/pantasystem/milktea/note/draft/DraftNotesScreen.kt | 6 +++++- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 3b05a2b160..2bb8cf6cd4 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -595,6 +595,7 @@ おすすめユーザ %d人が投稿 引用として添付しますか? + 下書き投稿を選択 diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 0fd426aba1..0c874f7fe1 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -589,6 +589,7 @@ Suggestions %d people posting Attach as a quote post? + Select draft post \ No newline at end of file diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index da273eab79..8c1ba14d0f 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -594,4 +594,5 @@ %d people posting Attach as a quote post? + Select draft post diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesScreen.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesScreen.kt index eddb713f58..8807e52d15 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesScreen.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/draft/DraftNotesScreen.kt @@ -41,7 +41,11 @@ fun DraftNotesScreen( } }, title = { - Text(text = stringResource(id = net.pantasystem.milktea.common_resource.R.string.draft_notes)) + if (isPickMode) { + Text(text = stringResource(id = net.pantasystem.milktea.common_resource.R.string.select_draft_post)) + } else { + Text(text = stringResource(id = net.pantasystem.milktea.common_resource.R.string.draft_notes)) + } }, backgroundColor = MaterialTheme.colors.surface, elevation = 0.dp From 3020956bbc22d016d31953ec320aeadeed1c413f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 21 May 2023 16:11:49 +0900 Subject: [PATCH 218/432] =?UTF-8?q?feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E9=81=B8=E6=8A=9E=E6=99=82=E3=81=AB=E3=83=80=E3=82=A4=E3=82=A2?= =?UTF-8?q?=E3=83=AD=E3=82=B0=E3=82=92=E9=96=89=E3=81=98=E3=81=AA=E3=81=84?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt index 952b56d0da..f43a810271 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt @@ -53,7 +53,6 @@ class CustomEmojiPickerDialog : BottomSheetDialogFragment(), EmojiPickerFragment }else{ mSelectionViewModel?.onSelect(emoji) } - dismiss() } } \ No newline at end of file From ddd6f5f7fb887962e779e6867a194f9719ac4734 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 25 May 2023 11:44:48 +0900 Subject: [PATCH 219/432] =?UTF-8?q?feat:=20=E7=A9=BA=E3=81=AEid=E3=82=92?= =?UTF-8?q?=E4=B8=8E=E3=81=88=E3=82=8B=E3=81=A8Flow=E3=81=AE=E5=80=A4?= =?UTF-8?q?=E3=81=8C=E4=BD=95=E3=82=82=E8=BF=94=E3=81=A3=E3=81=A6=E3=81=93?= =?UTF-8?q?=E3=81=AA=E3=81=8F=E3=81=AA=E3=82=8B=E4=B8=8D=E5=85=B7=E5=90=88?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/user/MediatorUserDataSource.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt index b17b191521..b029cdf3da 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt @@ -242,6 +242,9 @@ class MediatorUserDataSource @Inject constructor( override fun observeIn(accountId: Long, serverIds: List): Flow> { + if (serverIds.isEmpty()) { + return flowOf(emptyList()) + } return serverIds.distinct().chunked(50).map { userDao.observeInServerIds(accountId, serverIds).distinctUntilChanged().map { list -> list.map { From 2ef101761ab56d634d0dd32e0e261a8394442f41 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 25 May 2023 12:11:55 +0900 Subject: [PATCH 220/432] =?UTF-8?q?feat:=20name=E3=81=AE=E3=82=AB=E3=82=B9?= =?UTF-8?q?=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE=E6=AF=94?= =?UTF-8?q?=E7=8E=87=E3=82=92DB=E3=81=AB=E4=BF=9D=E6=8C=81=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../46.json | 3878 +++++++++++++++++ .../infrastructure/DatabaseMigrationTest.kt | 8 + .../milktea/data/infrastructure/DataBase.kt | 3 +- .../user/MediatorUserDataSource.kt | 1 + .../data/infrastructure/user/db/UserRecord.kt | 4 + 5 files changed, 3893 insertions(+), 1 deletion(-) create mode 100644 modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/46.json diff --git a/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/46.json b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/46.json new file mode 100644 index 0000000000..1ca2f02793 --- /dev/null +++ b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/46.json @@ -0,0 +1,3878 @@ +{ + "formatVersion": 1, + "database": { + "version": 46, + "identityHash": "f18c4b30fd0c11030b4bf1914d5a046f", + "entities": [ + { + "tableName": "connection_information", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT NOT NULL, `instanceBaseUrl` TEXT NOT NULL, `encryptedI` TEXT NOT NULL, `viaName` TEXT, `createdAt` TEXT NOT NULL, `isDirect` INTEGER NOT NULL, `updatedAt` TEXT NOT NULL, PRIMARY KEY(`accountId`, `encryptedI`, `instanceBaseUrl`), FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceBaseUrl", + "columnName": "instanceBaseUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedI", + "columnName": "encryptedI", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "viaName", + "columnName": "viaName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirect", + "columnName": "isDirect", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "encryptedI", + "instanceBaseUrl" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "reaction_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `accountId` INTEGER, `target_post_id` TEXT, `target_user_id` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetPostId", + "columnName": "target_post_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetUserId", + "columnName": "target_user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "reaction_user_setting", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`reaction`, `instance_domain`))", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "reaction", + "instance_domain" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "page", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT, `title` TEXT NOT NULL, `pageNumber` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `global_timeline_with_files` INTEGER, `global_timeline_type` TEXT, `local_timeline_with_files` INTEGER, `local_timeline_exclude_nsfw` INTEGER, `local_timeline_type` TEXT, `hybrid_timeline_withFiles` INTEGER, `hybrid_timeline_includeLocalRenotes` INTEGER, `hybrid_timeline_includeMyRenotes` INTEGER, `hybrid_timeline_includeRenotedMyRenotes` INTEGER, `hybrid_timeline_type` TEXT, `home_timeline_withFiles` INTEGER, `home_timeline_includeLocalRenotes` INTEGER, `home_timeline_includeMyRenotes` INTEGER, `home_timeline_includeRenotedMyRenotes` INTEGER, `home_timeline_type` TEXT, `user_list_timeline_listId` TEXT, `user_list_timeline_withFiles` INTEGER, `user_list_timeline_includeLocalRenotes` INTEGER, `user_list_timeline_includeMyRenotes` INTEGER, `user_list_timeline_includeRenotedMyRenotes` INTEGER, `user_list_timeline_type` TEXT, `mention_following` INTEGER, `mention_visibility` TEXT, `mention_type` TEXT, `show_noteId` TEXT, `show_type` TEXT, `tag_tag` TEXT, `tag_reply` INTEGER, `tag_renote` INTEGER, `tag_withFiles` INTEGER, `tag_poll` INTEGER, `tag_type` TEXT, `featured_offset` INTEGER, `featured_type` TEXT, `notification_following` INTEGER, `notification_markAsRead` INTEGER, `notification_type` TEXT, `user_userId` TEXT, `user_includeReplies` INTEGER, `user_includeMyRenotes` INTEGER, `user_withFiles` INTEGER, `user_type` TEXT, `search_query` TEXT, `search_host` TEXT, `search_userId` TEXT, `search_type` TEXT, `favorite_type` TEXT, `antenna_antennaId` TEXT, `antenna_type` TEXT, FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageNumber", + "columnName": "pageNumber", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.withFiles", + "columnName": "global_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.type", + "columnName": "global_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localTimeline.withFiles", + "columnName": "local_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.excludeNsfw", + "columnName": "local_timeline_exclude_nsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.type", + "columnName": "local_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.withFiles", + "columnName": "hybrid_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeLocalRenotes", + "columnName": "hybrid_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeMyRenotes", + "columnName": "hybrid_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeRenotedMyRenotes", + "columnName": "hybrid_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.type", + "columnName": "hybrid_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "homeTimeline.withFiles", + "columnName": "home_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeLocalRenotes", + "columnName": "home_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeMyRenotes", + "columnName": "home_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeRenotedMyRenotes", + "columnName": "home_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.type", + "columnName": "home_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.listId", + "columnName": "user_list_timeline_listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.withFiles", + "columnName": "user_list_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeLocalRenotes", + "columnName": "user_list_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeMyRenotes", + "columnName": "user_list_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeRenotedMyRenotes", + "columnName": "user_list_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.type", + "columnName": "user_list_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.following", + "columnName": "mention_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mention.visibility", + "columnName": "mention_visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.type", + "columnName": "mention_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.noteId", + "columnName": "show_noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.type", + "columnName": "show_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.tag", + "columnName": "tag_tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.reply", + "columnName": "tag_reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.renote", + "columnName": "tag_renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.withFiles", + "columnName": "tag_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.poll", + "columnName": "tag_poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.type", + "columnName": "tag_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "featured.offset", + "columnName": "featured_offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "featured.type", + "columnName": "featured_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notification.following", + "columnName": "notification_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.markAsRead", + "columnName": "notification_markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.type", + "columnName": "notification_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.userId", + "columnName": "user_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeReplies", + "columnName": "user_includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeMyRenotes", + "columnName": "user_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.withFiles", + "columnName": "user_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.type", + "columnName": "user_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.query", + "columnName": "search_query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.host", + "columnName": "search_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.userId", + "columnName": "search_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.type", + "columnName": "search_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "favorite.type", + "columnName": "favorite_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.antennaId", + "columnName": "antenna_antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.type", + "columnName": "antenna_type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_page_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "poll_choice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`choice` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`choice`, `weight`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "choice", + "columnName": "choice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "choice", + "weight", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_poll_choice_table_draft_note_id_choice", + "unique": false, + "columnNames": [ + "draft_note_id", + "choice" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_poll_choice_table_draft_note_id_choice` ON `${TABLE_NAME}` (`draft_note_id`, `choice`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "user_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, PRIMARY KEY(`userId`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_user_id_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_id_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_file_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL DEFAULT 'name none', `remote_file_id` TEXT, `file_path` TEXT, `is_sensitive` INTEGER, `type` TEXT, `thumbnailUrl` TEXT, `draft_note_id` INTEGER NOT NULL, `folder_id` TEXT, `file_id` INTEGER PRIMARY KEY AUTOINCREMENT, FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'name none'" + }, + { + "fieldPath": "remoteFileId", + "columnName": "remote_file_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileId", + "columnName": "file_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "file_id" + ] + }, + "indices": [ + { + "name": "index_draft_file_table_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_table_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_note_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `visibility` TEXT NOT NULL, `text` TEXT, `cw` TEXT, `viaMobile` INTEGER, `localOnly` INTEGER, `noExtractMentions` INTEGER, `noExtractHashtags` INTEGER, `noExtractEmojis` INTEGER, `replyId` TEXT, `renoteId` TEXT, `channelId` TEXT, `scheduleWillPostAt` TEXT, `draft_note_id` INTEGER PRIMARY KEY AUTOINCREMENT, `isSensitive` INTEGER, `multiple` INTEGER, `expiresAt` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cw", + "columnName": "cw", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "viaMobile", + "columnName": "viaMobile", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localOnly", + "columnName": "localOnly", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractMentions", + "columnName": "noExtractMentions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractHashtags", + "columnName": "noExtractHashtags", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractEmojis", + "columnName": "noExtractEmojis", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "replyId", + "columnName": "replyId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "renoteId", + "columnName": "renoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "scheduleWillPostAt", + "columnName": "scheduleWillPostAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.multiple", + "columnName": "multiple", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.expiresAt", + "columnName": "expiresAt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_draft_note_table_accountId_text", + "unique": false, + "columnNames": [ + "accountId", + "text" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_note_table_accountId_text` ON `${TABLE_NAME}` (`accountId`, `text`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "url_preview", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `icon` TEXT, `description` TEXT, `thumbnail` TEXT, `siteName` TEXT, PRIMARY KEY(`url`))", + "fields": [ + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnail", + "columnName": "thumbnail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "siteName", + "columnName": "siteName", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "url" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "account_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remoteId` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `userName` TEXT NOT NULL, `encryptedToken` TEXT NOT NULL, `instanceType` TEXT NOT NULL DEFAULT 'misskey', `accountId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedToken", + "columnName": "encryptedToken", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceType", + "columnName": "instanceType", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'misskey'" + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "accountId" + ] + }, + "indices": [ + { + "name": "index_account_table_remoteId", + "unique": false, + "columnNames": [ + "remoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_remoteId` ON `${TABLE_NAME}` (`remoteId`)" + }, + { + "name": "index_account_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_account_table_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_userName` ON `${TABLE_NAME}` (`userName`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "page_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `title` TEXT NOT NULL, `weight` INTEGER NOT NULL, `pageId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `withFiles` INTEGER, `excludeNsfw` INTEGER, `includeLocalRenotes` INTEGER, `includeMyRenotes` INTEGER, `includeRenotedMyRenotes` INTEGER, `listId` TEXT, `following` INTEGER, `visibility` TEXT, `noteId` TEXT, `tag` TEXT, `reply` INTEGER, `renote` INTEGER, `poll` INTEGER, `offset` INTEGER, `markAsRead` INTEGER, `userId` TEXT, `includeReplies` INTEGER, `query` TEXT, `host` TEXT, `antennaId` TEXT, `channelId` TEXT, `clipId` TEXT)", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageParams.type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageParams.withFiles", + "columnName": "withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.excludeNsfw", + "columnName": "excludeNsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeLocalRenotes", + "columnName": "includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeMyRenotes", + "columnName": "includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeRenotedMyRenotes", + "columnName": "includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.listId", + "columnName": "listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.following", + "columnName": "following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.tag", + "columnName": "tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.reply", + "columnName": "reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.renote", + "columnName": "renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.poll", + "columnName": "poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.offset", + "columnName": "offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.markAsRead", + "columnName": "markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.includeReplies", + "columnName": "includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.query", + "columnName": "query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.antennaId", + "columnName": "antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.clipId", + "columnName": "clipId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "pageId" + ] + }, + "indices": [ + { + "name": "index_page_table_weight", + "unique": false, + "columnNames": [ + "weight" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_weight` ON `${TABLE_NAME}` (`weight`)" + }, + { + "name": "index_page_table_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "meta_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `bannerUrl` TEXT, `cacheRemoteFiles` INTEGER, `description` TEXT, `disableGlobalTimeline` INTEGER, `disableLocalTimeline` INTEGER, `disableRegistration` INTEGER, `driveCapacityPerLocalUserMb` INTEGER, `driveCapacityPerRemoteUserMb` INTEGER, `enableDiscordIntegration` INTEGER, `enableEmail` INTEGER, `enableEmojiReaction` INTEGER, `enableGithubIntegration` INTEGER, `enableRecaptcha` INTEGER, `enableServiceWorker` INTEGER, `enableTwitterIntegration` INTEGER, `errorImageUrl` TEXT, `feedbackUrl` TEXT, `iconUrl` TEXT, `maintainerEmail` TEXT, `maintainerName` TEXT, `mascotImageUrl` TEXT, `maxNoteTextLength` INTEGER, `name` TEXT, `recaptchaSiteKey` TEXT, `secure` INTEGER, `swPublicKey` TEXT, `toSUrl` TEXT, `version` TEXT NOT NULL, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cacheRemoteFiles", + "columnName": "cacheRemoteFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "disableGlobalTimeline", + "columnName": "disableGlobalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableLocalTimeline", + "columnName": "disableLocalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableRegistration", + "columnName": "disableRegistration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerLocalUserMb", + "columnName": "driveCapacityPerLocalUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerRemoteUserMb", + "columnName": "driveCapacityPerRemoteUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableDiscordIntegration", + "columnName": "enableDiscordIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmail", + "columnName": "enableEmail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmojiReaction", + "columnName": "enableEmojiReaction", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableGithubIntegration", + "columnName": "enableGithubIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableRecaptcha", + "columnName": "enableRecaptcha", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableServiceWorker", + "columnName": "enableServiceWorker", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableTwitterIntegration", + "columnName": "enableTwitterIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "errorImageUrl", + "columnName": "errorImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "feedbackUrl", + "columnName": "feedbackUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerEmail", + "columnName": "maintainerEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerName", + "columnName": "maintainerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mascotImageUrl", + "columnName": "mascotImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maxNoteTextLength", + "columnName": "maxNoteTextLength", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recaptchaSiteKey", + "columnName": "recaptchaSiteKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secure", + "columnName": "secure", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "swPublicKey", + "columnName": "swPublicKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toSUrl", + "columnName": "toSUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "emoji_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `host` TEXT, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` TEXT, PRIMARY KEY(`name`, `instanceDomain`), FOREIGN KEY(`instanceDomain`) REFERENCES `meta_table`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_emoji_table_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [ + { + "table": "meta_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "instanceDomain" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "emoji_alias_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`alias` TEXT NOT NULL, `name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, PRIMARY KEY(`alias`, `name`, `instanceDomain`), FOREIGN KEY(`name`, `instanceDomain`) REFERENCES `emoji_table`(`name`, `instanceDomain`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "alias", + "columnName": "alias", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "alias", + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_alias_table_name_instanceDomain", + "unique": false, + "columnNames": [ + "name", + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_alias_table_name_instanceDomain` ON `${TABLE_NAME}` (`name`, `instanceDomain`)" + } + ], + "foreignKeys": [ + { + "table": "emoji_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "name", + "instanceDomain" + ], + "referencedColumns": [ + "name", + "instanceDomain" + ] + } + ] + }, + { + "tableName": "unread_notifications_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, PRIMARY KEY(`accountId`, `notificationId`), FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "nicknames", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`nickname` TEXT NOT NULL, `username` TEXT NOT NULL, `host` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "nickname", + "columnName": "nickname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_nicknames_username_host", + "unique": true, + "columnNames": [ + "username", + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_nicknames_username_host` ON `${TABLE_NAME}` (`username`, `host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "utf8_emojis_by_amio", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`codes` TEXT NOT NULL, `name` TEXT NOT NULL, `char` TEXT NOT NULL, PRIMARY KEY(`codes`))", + "fields": [ + { + "fieldPath": "codes", + "columnName": "codes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "charCode", + "columnName": "char", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "codes" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "drive_file_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `relatedAccountId` INTEGER NOT NULL, `createdAt` TEXT, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `md5` TEXT, `size` INTEGER, `url` TEXT NOT NULL, `isSensitive` INTEGER NOT NULL, `thumbnailUrl` TEXT, `folderId` TEXT, `userId` TEXT, `comment` TEXT, `blurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`relatedAccountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "relatedAccountId", + "columnName": "relatedAccountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "md5", + "columnName": "md5", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folderId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "blurhash", + "columnName": "blurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_drive_file_v1_serverId_relatedAccountId", + "unique": true, + "columnNames": [ + "serverId", + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_drive_file_v1_serverId_relatedAccountId` ON `${TABLE_NAME}` (`serverId`, `relatedAccountId`)" + }, + { + "name": "index_drive_file_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_drive_file_v1_relatedAccountId", + "unique": false, + "columnNames": [ + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_relatedAccountId` ON `${TABLE_NAME}` (`relatedAccountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "relatedAccountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "draft_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`draftNoteId` INTEGER NOT NULL, `filePropertyId` INTEGER, `localFileId` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`filePropertyId`) REFERENCES `drive_file_v1`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`localFileId`) REFERENCES `draft_local_file_v2_table`(`localFileId`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "draftNoteId", + "columnName": "draftNoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filePropertyId", + "columnName": "filePropertyId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId", + "unique": true, + "columnNames": [ + "draftNoteId", + "filePropertyId", + "localFileId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId` ON `${TABLE_NAME}` (`draftNoteId`, `filePropertyId`, `localFileId`)" + }, + { + "name": "index_draft_file_v2_table_draftNoteId", + "unique": false, + "columnNames": [ + "draftNoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId` ON `${TABLE_NAME}` (`draftNoteId`)" + }, + { + "name": "index_draft_file_v2_table_localFileId", + "unique": false, + "columnNames": [ + "localFileId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_localFileId` ON `${TABLE_NAME}` (`localFileId`)" + }, + { + "name": "index_draft_file_v2_table_filePropertyId", + "unique": false, + "columnNames": [ + "filePropertyId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_filePropertyId` ON `${TABLE_NAME}` (`filePropertyId`)" + } + ], + "foreignKeys": [ + { + "table": "drive_file_v1", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "filePropertyId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "draft_local_file_v2_table", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "localFileId" + ], + "referencedColumns": [ + "localFileId" + ] + } + ] + }, + { + "tableName": "draft_local_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `file_path` TEXT NOT NULL, `is_sensitive` INTEGER, `type` TEXT NOT NULL, `thumbnailUrl` TEXT, `folder_id` TEXT, `file_size` INTEGER, `comment` TEXT, `localFileId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "localFileId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "group_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `ownerId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "ownerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_v1_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_group_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_group_v1_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_v1_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "group_member_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`groupId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`groupId`) REFERENCES `group_v1`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_member_v1_groupId", + "unique": false, + "columnNames": [ + "groupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_member_v1_groupId` ON `${TABLE_NAME}` (`groupId`)" + }, + { + "name": "index_group_member_v1_groupId_userId", + "unique": true, + "columnNames": [ + "groupId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_member_v1_groupId_userId` ON `${TABLE_NAME}` (`groupId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "group_v1", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "groupId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `userName` TEXT NOT NULL, `name` TEXT, `avatarUrl` TEXT, `isCat` INTEGER, `isBot` INTEGER, `host` TEXT NOT NULL, `isSameHost` INTEGER NOT NULL, `avatarBlurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatarUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isCat", + "columnName": "isCat", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isBot", + "columnName": "isBot", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSameHost", + "columnName": "isSameHost", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "avatarBlurhash", + "columnName": "avatarBlurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_serverId_accountId", + "unique": true, + "columnNames": [ + "serverId", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_serverId_accountId` ON `${TABLE_NAME}` (`serverId`, `accountId`)" + }, + { + "name": "index_user_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_userName` ON `${TABLE_NAME}` (`userName`)" + }, + { + "name": "index_user_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_host", + "unique": false, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_detailed_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_detailed_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_detailed_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_emoji", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `userId` INTEGER NOT NULL, `aspectRatio` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "aspectRatio", + "columnName": "aspectRatio", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_emoji_name_userId", + "unique": true, + "columnNames": [ + "name", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_emoji_name_userId` ON `${TABLE_NAME}` (`name`, `userId`)" + }, + { + "name": "index_user_emoji_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_emoji_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "pinned_note_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_pinned_note_id_noteId_userId", + "unique": true, + "columnNames": [ + "noteId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_pinned_note_id_noteId_userId` ON `${TABLE_NAME}` (`noteId`, `userId`)" + }, + { + "name": "index_pinned_note_id_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pinned_note_id_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`faviconUrl` TEXT, `iconUrl` TEXT, `name` TEXT, `softwareName` TEXT, `softwareVersion` TEXT, `themeColor` TEXT, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "faviconUrl", + "columnName": "faviconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareName", + "columnName": "softwareName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareVersion", + "columnName": "softwareVersion", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_instance_info_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_instance_info_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_profile_field", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `value` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_profile_field_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_profile_field_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "word_filter_regex_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pattern` TEXT NOT NULL, `parentId` INTEGER NOT NULL, PRIMARY KEY(`parentId`), FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "pattern", + "columnName": "pattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentId" + ] + }, + "indices": [ + { + "name": "index_word_filter_regex_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_regex_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_word_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`word` TEXT NOT NULL, `parentId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "word", + "columnName": "word", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_word_filter_word_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_word_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_list", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_list_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_user_list_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_list_member", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userListId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userListId`) REFERENCES `user_list`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userListId", + "columnName": "userListId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_member_userListId", + "unique": false, + "columnNames": [ + "userListId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_member_userListId` ON `${TABLE_NAME}` (`userListId`)" + }, + { + "name": "index_user_list_member_userListId_userId", + "unique": true, + "columnNames": [ + "userListId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_member_userListId_userId` ON `${TABLE_NAME}` (`userListId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "user_list", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userListId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "instance_info_v1_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `host` TEXT NOT NULL, `name` TEXT, `description` TEXT, `clientMaxBodyByteSize` INTEGER, `iconUrl` TEXT, `themeColor` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "clientMaxBodyByteSize", + "columnName": "clientMaxBodyByteSize", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_instance_info_v1_table_host", + "unique": true, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_instance_info_v1_table_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "search_histories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `keyword` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keyword", + "columnName": "keyword", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_search_histories_keyword_accountId", + "unique": true, + "columnNames": [ + "keyword", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_search_histories_keyword_accountId` ON `${TABLE_NAME}` (`keyword`, `accountId`)" + }, + { + "name": "index_search_histories_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_search_histories_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_info_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_info_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_info_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_related_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_related_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_related_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nodeinfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `nodeInfoVersion` TEXT NOT NULL, `name` TEXT NOT NULL, `version` TEXT NOT NULL, PRIMARY KEY(`host`))", + "fields": [ + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nodeInfoVersion", + "columnName": "nodeInfoVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "host" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `title` TEXT NOT NULL, `description` TEXT NOT NULL, `email` TEXT NOT NULL, `version` TEXT NOT NULL, `urls_streamingApi` TEXT, `configuration_statuses_maxCharacters` INTEGER, `configuration_statuses_maxMediaAttachments` INTEGER, `configuration_polls_maxOptions` INTEGER, `configuration_polls_maxCharactersPerOption` INTEGER, `configuration_polls_minExpiration` INTEGER, `configuration_polls_maxExpiration` INTEGER, `configuration_emoji_reactions_myReactions` INTEGER, `configuration_emoji_reactions_maxReactionsPerAccount` INTEGER, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "urls.streamingApi", + "columnName": "urls_streamingApi", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxCharacters", + "columnName": "configuration_statuses_maxCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxMediaAttachments", + "columnName": "configuration_statuses_maxMediaAttachments", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxOptions", + "columnName": "configuration_polls_maxOptions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxCharactersPerOption", + "columnName": "configuration_polls_maxCharactersPerOption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.minExpiration", + "columnName": "configuration_polls_minExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxExpiration", + "columnName": "configuration_polls_maxExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactions", + "columnName": "configuration_emoji_reactions_myReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactionsPerAccount", + "columnName": "configuration_emoji_reactions_maxReactionsPerAccount", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "custom_emojis", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT, `name` TEXT NOT NULL, `emojiHost` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "emojiHost", + "columnName": "emojiHost", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_custom_emojis_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_custom_emojis_emojiHost", + "unique": false, + "columnNames": [ + "emojiHost" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost` ON `${TABLE_NAME}` (`emojiHost`)" + }, + { + "name": "index_custom_emojis_category", + "unique": false, + "columnNames": [ + "category" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_category` ON `${TABLE_NAME}` (`category`)" + }, + { + "name": "index_custom_emojis_emojiHost_name", + "unique": true, + "columnNames": [ + "emojiHost", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost_name` ON `${TABLE_NAME}` (`emojiHost`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "custom_emoji_aliases", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`emojiId` INTEGER NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`emojiId`, `value`), FOREIGN KEY(`emojiId`) REFERENCES `custom_emojis`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "emojiId", + "columnName": "emojiId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "emojiId", + "value" + ] + }, + "indices": [ + { + "name": "index_custom_emoji_aliases_emojiId_value", + "unique": false, + "columnNames": [ + "emojiId", + "value" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emoji_aliases_emojiId_value` ON `${TABLE_NAME}` (`emojiId`, `value`)" + } + ], + "foreignKeys": [ + { + "table": "custom_emojis", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "emojiId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "notification_json_cache_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, `json` TEXT NOT NULL, `key` TEXT, `weight` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `notificationId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "json", + "columnName": "json", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [ + { + "name": "index_notification_json_cache_v1_key", + "unique": false, + "columnNames": [ + "key" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notification_json_cache_v1_key` ON `${TABLE_NAME}` (`key`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_word_filters_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `filterId` TEXT NOT NULL, `phrase` TEXT NOT NULL, `wholeWord` INTEGER NOT NULL, `expiresAt` TEXT, `irreversible` INTEGER NOT NULL, `isContextHome` INTEGER NOT NULL, `isContextNotifications` INTEGER NOT NULL, `isContextPublic` INTEGER NOT NULL, `isContextThread` INTEGER NOT NULL, `isContextAccount` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `filterId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filterId", + "columnName": "filterId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phrase", + "columnName": "phrase", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "wholeWord", + "columnName": "wholeWord", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expiresAt", + "columnName": "expiresAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "irreversible", + "columnName": "irreversible", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextHome", + "columnName": "isContextHome", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextNotifications", + "columnName": "isContextNotifications", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextPublic", + "columnName": "isContextPublic", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextThread", + "columnName": "isContextThread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextAccount", + "columnName": "isContextAccount", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "filterId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "renote_mute_users", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `postedAt` TEXT, PRIMARY KEY(`userId`, `accountId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "postedAt", + "columnName": "postedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "accountId" + ] + }, + "indices": [ + { + "name": "index_renote_mute_users_postedAt", + "unique": false, + "columnNames": [ + "postedAt" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_postedAt` ON `${TABLE_NAME}` (`postedAt`)" + }, + { + "name": "index_renote_mute_users_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_fedibird_capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_mastodon_instance_fedibird_capabilities_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mastodon_instance_fedibird_capabilities_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "pleroma_metadata_features", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_pleroma_metadata_features_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pleroma_metadata_features_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + } + ], + "views": [ + { + "viewName": "user_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select user.*, nicknames.nickname from user left join nicknames on user.userName = nicknames.username and user.host = nicknames.host" + }, + { + "viewName": "group_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.groupId, u.id as userId, u.avatarUrl, u.serverId from group_member_v1 as m \n inner join group_v1 as g\n inner join user as u\n on m.groupId = g.id\n and m.userId = u.serverId\n and g.accountId = u.accountId" + }, + { + "viewName": "user_list_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.userListId, u.id as userId, u.avatarUrl, u.serverId from user_list_member as m \n inner join user_list as ul\n inner join user as u\n on m.userListId = ul.id\n and m.userId = u.serverId\n and ul.accountId = u.accountId" + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'f18c4b30fd0c11030b4bf1914d5a046f')" + ] + } +} \ No newline at end of file diff --git a/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/DatabaseMigrationTest.kt b/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/DatabaseMigrationTest.kt index d4462fab66..40d2dc27b6 100644 --- a/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/DatabaseMigrationTest.kt +++ b/modules/data/src/androidTest/java/net/pantasystem/milktea/data/infrastructure/DatabaseMigrationTest.kt @@ -229,4 +229,12 @@ class DatabaseMigrationTest { helper.createDatabase(testDb, 44) helper.runMigrationsAndValidate(testDb, 45, true) } + + @Test + @Throws(IOException::class) + fun migrate45To46() { + helper.createDatabase(testDb, 45) + helper.runMigrationsAndValidate(testDb, 46, true) + + } } \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt index 7de0802c7c..cb2de0a328 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt @@ -117,7 +117,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut PleromaMetadataFeatures::class, ], - version = 45, + version = 46, exportSchema = true, autoMigrations = [ AutoMigration(from = 11, to = 12), @@ -154,6 +154,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut AutoMigration(from = 42, to = 43), AutoMigration(from = 43, to = 44), AutoMigration(from = 44, to = 45), + AutoMigration(from = 45, to = 46), ], views = [UserView::class, GroupMemberView::class, UserListMemberView::class] ) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt index b029cdf3da..6b8694655b 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt @@ -132,6 +132,7 @@ class MediatorUserDataSource @Inject constructor( name = it.name, uri = it.uri, url = it.url, + aspectRatio = it.aspectRatio, ) } ) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt index 1d44c01dd6..38e67ddbf6 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt @@ -266,6 +266,9 @@ data class UserEmojiRecord( @ColumnInfo(name = "userId") val userId: Long, + @ColumnInfo(name = "aspectRatio") + val aspectRatio: Float? = null, + @ColumnInfo(name = "id") @PrimaryKey(autoGenerate = true) val id: Long = 0L, ) { @@ -274,6 +277,7 @@ data class UserEmojiRecord( name = name, url = url, uri = uri, + aspectRatio = aspectRatio, ) } } From c0f70a67889b0705ffaf57b485ee4231efd526af Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 26 May 2023 12:37:42 +0900 Subject: [PATCH 221/432] =?UTF-8?q?refactor:=20=E4=B8=80=E9=83=A8=E5=87=A6?= =?UTF-8?q?=E7=90=86=E3=82=92=E3=83=A1=E3=82=BD=E3=83=83=E3=83=89=E3=81=AB?= =?UTF-8?q?=E5=88=87=E3=82=8A=E5=87=BA=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CustomEmojiImageViewSizeHelper.kt | 29 +++++++++++++++++ .../note/reaction/NoteReactionViewHelper.kt | 32 +++++++++---------- 2 files changed, 44 insertions(+), 17 deletions(-) create mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt new file mode 100644 index 0000000000..4729741de9 --- /dev/null +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt @@ -0,0 +1,29 @@ +@file:Suppress("UNCHECKED_CAST") + +package net.pantasystem.milktea.note.reaction + +import android.content.Context +import android.view.ViewGroup +import android.widget.ImageView + +object CustomEmojiImageViewSizeHelper { + + fun ImageView.applySizeByAspectRatio(baseHeightDp: Int, aspectRatio: Float?) { + val (imageViewWidthPx, imageViewHeightPx) = context.calculateImageWidthAndHeightSize(baseHeightDp, aspectRatio) + val params = layoutParams as T + params.height = imageViewHeightPx.toInt() + params.width = imageViewWidthPx.toInt() + layoutParams = params + } + + fun Context.calculateImageWidthAndHeightSize(baseHeightDp: Int, aspectRatio: Float?): Pair { + val metrics = resources.displayMetrics + val heightPx = baseHeightDp * metrics.density + val imageViewWidthPx = if (aspectRatio == null) { + heightPx + } else { + (heightPx * aspectRatio) + } + return heightPx to imageViewWidthPx + } +} \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 35e2dc3ba5..346d8ebf36 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -5,7 +5,6 @@ import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView -import androidx.core.view.updateLayoutParams import androidx.databinding.BindingAdapter import dagger.hilt.android.EntryPointAccessors import net.pantasystem.milktea.common.glide.GlideApp @@ -13,6 +12,8 @@ import net.pantasystem.milktea.common_android.ui.VisibilityHelper.setMemoVisibil import net.pantasystem.milktea.common_android_ui.BindingProvider import net.pantasystem.milktea.model.notes.reaction.LegacyReaction import net.pantasystem.milktea.model.notes.reaction.Reaction +import net.pantasystem.milktea.note.reaction.CustomEmojiImageViewSizeHelper.applySizeByAspectRatio +import net.pantasystem.milktea.note.reaction.CustomEmojiImageViewSizeHelper.calculateImageWidthAndHeightSize import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData @@ -40,21 +41,18 @@ object NoteReactionViewHelper { } else { reactionImageTypeView.setMemoVisibility(View.VISIBLE) reactionTextTypeView.setMemoVisibility(View.GONE) - val metrics = context.resources.displayMetrics - val imageViewHeightPx = REACTION_IMAGE_WIDTH_SIZE_DP * metrics.density - val imageAspectRatio = ImageAspectRatioCache.get(emoji.url ?: emoji.uri) ?: emoji.aspectRatio - val imageViewWidthPx = if (imageAspectRatio == null) { - imageViewHeightPx - } else { - (imageViewHeightPx * imageAspectRatio) - } - - reactionImageTypeView.updateLayoutParams { - this@updateLayoutParams.also { - it.height = imageViewHeightPx.toInt() - it.width = imageViewWidthPx.toInt() - } - } + val imageAspectRatio = + ImageAspectRatioCache.get(emoji.url ?: emoji.uri) ?: emoji.aspectRatio + + val (imageViewWidthPx, imageViewHeightPx) = reactionImageTypeView.context.calculateImageWidthAndHeightSize( + REACTION_IMAGE_WIDTH_SIZE_DP, + imageAspectRatio + ) + reactionImageTypeView.applySizeByAspectRatio( + REACTION_IMAGE_WIDTH_SIZE_DP, + imageAspectRatio + ) + GlideApp.with(reactionImageTypeView.context) .load(emoji.url ?: emoji.uri) @@ -63,7 +61,7 @@ object NoteReactionViewHelper { .into(reactionImageTypeView) } } - + @JvmStatic fun setReactionCount( From b8b020c8058d1c155fd69209e80bb555d84efffb Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 26 May 2023 12:39:46 +0900 Subject: [PATCH 222/432] fix --- .../milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt index 4729741de9..8da39cf158 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt @@ -24,6 +24,6 @@ object CustomEmojiImageViewSizeHelper { } else { (heightPx * aspectRatio) } - return heightPx to imageViewWidthPx + return imageViewWidthPx to heightPx } } \ No newline at end of file From 62afb35fd72720313cb0b3f48ff46a33346ab07c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 26 May 2023 13:35:26 +0900 Subject: [PATCH 223/432] =?UTF-8?q?feat:=20TabbedListMediator=E3=81=8CFlex?= =?UTF-8?q?boxLayoutManager=E3=81=AB=E5=AF=BE=E5=BF=9C=E3=81=97=E3=81=A6?= =?UTF-8?q?=E3=81=84=E3=81=AA=E3=81=8B=E3=81=A3=E3=81=9F=E3=81=AE=E3=81=A7?= =?UTF-8?q?FlexboxLayoutManager=E5=AF=BE=E5=BF=9C=E7=89=88=E3=81=AE?= =?UTF-8?q?=E4=BD=9C=E6=88=90=E3=82=92=E8=A1=8C=E3=81=AA=E3=81=A3=E3=81=9F?= =?UTF-8?q?=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tab/TabViewCompositeClickListener.kt | 32 +++ .../tab/TabbedFlexboxListMediator.kt | 240 ++++++++++++++++++ 2 files changed, 272 insertions(+) create mode 100644 modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/tab/TabViewCompositeClickListener.kt create mode 100644 modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/tab/TabbedFlexboxListMediator.kt diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/tab/TabViewCompositeClickListener.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/tab/TabViewCompositeClickListener.kt new file mode 100644 index 0000000000..3fa171f83f --- /dev/null +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/tab/TabViewCompositeClickListener.kt @@ -0,0 +1,32 @@ +package net.pantasystem.milktea.common_android_ui.tab + + +import com.google.android.material.tabs.TabLayout +import java.util.* + +class TabViewCompositeClickListener(private val mTabLayout: TabLayout) { + + private val listeners: MutableList<(tab: TabLayout.Tab, position: Int) -> Unit> = ArrayList() + + fun addListener(listener: (tab: TabLayout.Tab, position: Int) -> Unit) { + listeners.add(listener) + } + + fun removeListener(listener: (tab: TabLayout.Tab, position: Int) -> Unit) { + listeners.remove(listener) + } + + fun build() { + for (i in 0 until mTabLayout.tabCount) { + mTabLayout.getTabAt(i)!!.view.setOnClickListener { + for (listener in listeners) { + listener(mTabLayout.getTabAt(i)!!, i) + } + } + } + } + + fun getListeners(): List<(tab: TabLayout.Tab, position: Int) -> Unit> { + return listeners + } +} \ No newline at end of file diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/tab/TabbedFlexboxListMediator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/tab/TabbedFlexboxListMediator.kt new file mode 100644 index 0000000000..9ecdca03c1 --- /dev/null +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/tab/TabbedFlexboxListMediator.kt @@ -0,0 +1,240 @@ +package net.pantasystem.milktea.common_android_ui.tab + + +import androidx.recyclerview.widget.LinearSmoothScroller +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.SmoothScroller +import com.google.android.flexbox.FlexboxLayoutManager +import com.google.android.material.tabs.TabLayout +import com.google.android.material.tabs.TabLayout.OnTabSelectedListener + +/** + * This class is made to provide the ability to sync between RecyclerView's specific items with + * TabLayout tabs. + * + * @param mRecyclerView The RecyclerView that is going to be synced with the TabLayout + * @param mTabLayout The TabLayout that is going to be synced with the RecyclerView specific + * items. + * @param mIndices The indices of the RecyclerView's items that is going to be playing a + * role of "check points" for the syncing operation. + * @param mIsSmoothScroll Defines the ability of smooth scroll when clicking the tabs of the + * TabLayout. + */ +class TabbedFlexboxListMediator( + private val mRecyclerView: RecyclerView, + private val mTabLayout: TabLayout, + private var mIndices: List, + private var mIsSmoothScroll: Boolean = false +) { + + private var mIsAttached = false + + private var mRecyclerState = RecyclerView.SCROLL_STATE_IDLE + private var mTabClickFlag = false + + private val smoothScroller: SmoothScroller = + object : LinearSmoothScroller(mRecyclerView.context) { + override fun getVerticalSnapPreference(): Int { + return SNAP_TO_START + } + } + + private var tabViewCompositeClickListener: TabViewCompositeClickListener = + TabViewCompositeClickListener(mTabLayout) + + /** + * Calling this method will ensure that the data that has been provided to the mediator is + * valid for use, and start syncing between the the RecyclerView and the TabLayout. + * + * Call this method when you have: + * 1- provided a RecyclerView Adapter, + * 2- provided a TabLayout with the appropriate number of tabs, + * 3- provided indices of the recyclerview items that you are syncing the tabs with. (You + * need to be providing indices of at most the number of Tabs inflated in the TabLayout.) + */ + fun attach() { + mRecyclerView.adapter + ?: throw RuntimeException("Cannot attach with no Adapter provided to RecyclerView") + + if (mTabLayout.tabCount == 0) + throw RuntimeException("Cannot attach with no tabs provided to TabLayout") + + if (mIndices.size > mTabLayout.tabCount) + throw RuntimeException("Cannot attach using more indices than the available tabs") + + notifyIndicesChanged() + mIsAttached = true + } + + /** + * Calling this method will ensure to stop the synchronization between the RecyclerView and + * the TabLayout. + */ + + fun detach() { + clearListeners() + mIsAttached = false + } + + /** + * This method will ensure that the synchronization is up-to-date with the data provided. + */ + private fun reAttach() { + detach() + attach() + } + + /** + * Calling this method will + */ + fun updateMediatorWithNewIndices(newIndices: List): TabbedFlexboxListMediator { + mIndices = newIndices + + if (mIsAttached) { + reAttach() + } + + return this + } + + /** + * This method will ensure that any listeners that have been added by the mediator will be + * removed, including the one listener from + * @see TabbedListMediator#addOnViewOfTabClickListener((TabLayout.Tab, int) -> Unit) + */ + + private fun clearListeners() { + mRecyclerView.clearOnScrollListeners() + for (i in 0 until mTabLayout.tabCount) { + mTabLayout.getTabAt(i)!!.view.setOnClickListener(null) + } + for (i in tabViewCompositeClickListener.getListeners().indices) { + tabViewCompositeClickListener.getListeners().toMutableList().removeAt(i) + } + mTabLayout.removeOnTabSelectedListener(onTabSelectedListener) + mRecyclerView.removeOnScrollListener(onScrollListener) + } + + /** + * This method will attach the listeners required to make the synchronization possible. + */ + + private fun notifyIndicesChanged() { + tabViewCompositeClickListener.addListener { _, _ -> mTabClickFlag = true } + tabViewCompositeClickListener.build() + mTabLayout.addOnTabSelectedListener(onTabSelectedListener) + mRecyclerView.addOnScrollListener(onScrollListener) + } + + private val onTabSelectedListener = object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + + if (!mTabClickFlag) return + + val position = tab.position + + if (mIsSmoothScroll) { + smoothScroller.targetPosition = mIndices[position] + mRecyclerView.layoutManager?.startSmoothScroll(smoothScroller) + } else { +// (mRecyclerView.layoutManager as FlexboxLayoutManager?)?.scrollToPositionWithOffset( +// mIndices[position], +// 0 +// ) + (mRecyclerView.layoutManager as FlexboxLayoutManager?)?.scrollToPosition(mIndices[position]) + mTabClickFlag = false + } + } + + override fun onTabUnselected(tab: TabLayout.Tab) {} + override fun onTabReselected(tab: TabLayout.Tab) {} + } + + private val onScrollListener = object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + mRecyclerState = newState + if (mIsSmoothScroll && newState == RecyclerView.SCROLL_STATE_IDLE) { + mTabClickFlag = false + } + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (mTabClickFlag) { + return + } + + val flexboxLayoutManager: FlexboxLayoutManager = + recyclerView.layoutManager as FlexboxLayoutManager? + ?: throw RuntimeException("No FlexboxLayoutManager attached to the RecyclerView.") + + var itemPosition = + flexboxLayoutManager.findFirstCompletelyVisibleItemPosition() + + if (itemPosition == -1) { + itemPosition = + flexboxLayoutManager.findFirstVisibleItemPosition() + } + + if (mRecyclerState == RecyclerView.SCROLL_STATE_DRAGGING + || mRecyclerState == RecyclerView.SCROLL_STATE_SETTLING + ) { + for (i in mIndices.indices) { + if (itemPosition == mIndices[i]) { + if (!mTabLayout.getTabAt(i)!!.isSelected) { + mTabLayout.getTabAt(i)!!.select() + } + if (flexboxLayoutManager.findLastCompletelyVisibleItemPosition() == mIndices[mIndices.size - 1]) { + if (!mTabLayout.getTabAt(mIndices.size - 1)!!.isSelected) { + mTabLayout.getTabAt(mIndices.size - 1)!!.select() + } + return + } + } + } + } + } + } + + /** + * @return the state of the mediator, either attached or not. + */ + + fun isAttached(): Boolean { + return mIsAttached + } + + /** + * @return the state of the mediator, is smooth scrolling or not. + */ + + fun isSmoothScroll(): Boolean { + return mIsSmoothScroll + } + + /** + * @param smooth sets up the mediator with smooth scrolling + */ + + fun setSmoothScroll(smooth: Boolean) { + mIsSmoothScroll = smooth + } + + /** + * @param listener the listener the will applied on "the view" of the tab. This method is useful + * when attaching a click listener on the tabs of the TabLayout. + * Note that this method is REQUIRED in case of the need of adding a click listener on the view + * of a tab layout. Since the mediator uses a click flag @see TabbedListMediator#mTabClickFlag + * it's taking the place of the normal on click listener, and thus the need of the composite click + * listener pattern, so adding listeners should be done using this method. + */ + + fun addOnViewOfTabClickListener( + listener: (tab: TabLayout.Tab, position: Int) -> Unit + ) { + tabViewCompositeClickListener.addListener(listener) + if (mIsAttached) { + notifyIndicesChanged() + } + } +} \ No newline at end of file From a5a39716e26f6e447d6a9a084a61e034ce2c835a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 26 May 2023 13:36:51 +0900 Subject: [PATCH 224/432] =?UTF-8?q?feat:=20=E7=94=BB=E5=83=8F=E3=81=AE?= =?UTF-8?q?=E6=AF=94=E7=8E=87=E3=82=92=E7=B6=AD=E6=8C=81=E3=81=97=E3=81=A6?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_android_ui/build.gradle | 1 + .../note/emojis/EmojiPickerFragment.kt | 49 ++++--------------- .../reaction/choices/EmojiListItemsAdapter.kt | 18 ++++++- .../src/main/res/layout/item_emoji_choice.xml | 8 +-- 4 files changed, 30 insertions(+), 46 deletions(-) diff --git a/modules/common_android_ui/build.gradle b/modules/common_android_ui/build.gradle index a6b79368d6..67730c85ff 100644 --- a/modules/common_android_ui/build.gradle +++ b/modules/common_android_ui/build.gradle @@ -98,5 +98,6 @@ dependencies { implementation libs.coil.compose testImplementation libs.junit.jupiter.api testRuntimeOnly libs.junit.jupiter.engine + implementation libs.flexbox } \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt index f52ee888d8..ad7e456299 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt @@ -3,16 +3,13 @@ package net.pantasystem.milktea.note.emojis import android.content.Context import android.os.Bundle import android.view.View -import android.view.ViewTreeObserver import android.view.inputmethod.EditorInfo import android.widget.EditText import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentManager import androidx.fragment.app.viewModels import androidx.lifecycle.* -import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.RecyclerView -import com.ahmadhamwi.tabsync.TabbedListMediator import com.google.android.flexbox.AlignItems import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.material.tabs.TabLayout @@ -24,7 +21,7 @@ import kotlinx.coroutines.flow.filterNot import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import net.pantasystem.milktea.common_android.resource.convertDp2Px +import net.pantasystem.milktea.common_android_ui.tab.TabbedFlexboxListMediator import net.pantasystem.milktea.model.notes.reaction.LegacyReaction import net.pantasystem.milktea.model.notes.reaction.Reaction import net.pantasystem.milktea.model.notes.reaction.ReactionSelection @@ -134,6 +131,7 @@ class EmojiSelectionBinder( val adapter = EmojiListItemsAdapter( + isApplyImageAspectRatio = true, onEmojiLongClicked = { emojiType -> val exists = emojiPickerViewModel.uiState.value.isExistsConfig(emojiType) if (!exists) { @@ -149,43 +147,14 @@ class EmojiSelectionBinder( } ) - val layoutManager = GridLayoutManager(context, 5) - layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return when(EmojiListItemsAdapter.ItemType.values()[adapter.getItemViewType(position)]) { - EmojiListItemsAdapter.ItemType.Header -> 5 - EmojiListItemsAdapter.ItemType.Emoji -> 1 - } - } - - } - recyclerView.layoutManager = layoutManager - - fun calculateSpanCount(): Int { - val viewWidth = recyclerView.measuredWidth - val itemWidth = context.convertDp2Px(54f).toInt() - return viewWidth / itemWidth + val layoutManager by lazy { + val flexBoxLayoutManager = FlexboxLayoutManager(context) + flexBoxLayoutManager.alignItems = AlignItems.STRETCH + flexBoxLayoutManager } - val listener = object : ViewTreeObserver.OnGlobalLayoutListener { - override fun onGlobalLayout() { - val count = calculateSpanCount().coerceAtLeast(4) - val lm = GridLayoutManager(context, count) - - lm.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { - override fun getSpanSize(position: Int): Int { - return when(EmojiListItemsAdapter.ItemType.values()[adapter.getItemViewType(position)]) { - EmojiListItemsAdapter.ItemType.Header -> count - EmojiListItemsAdapter.ItemType.Emoji -> 1 - } - } - } - recyclerView.viewTreeObserver.removeOnGlobalLayoutListener(this) - recyclerView.layoutManager = lm - } - } - recyclerView.viewTreeObserver.addOnGlobalLayoutListener(listener) + recyclerView.layoutManager = layoutManager recyclerView.adapter = adapter @@ -199,7 +168,7 @@ class EmojiSelectionBinder( } } - var tabbedListMediator: TabbedListMediator? = null + var tabbedListMediator: TabbedFlexboxListMediator? = null emojiPickerViewModel.uiState.filterNot { it.tabHeaderLabels.isEmpty() }.distinctUntilChangedBy { @@ -214,7 +183,7 @@ class EmojiSelectionBinder( tabLayout.addTab(tab, false) } tabbedListMediator?.detach() - tabbedListMediator = TabbedListMediator( + tabbedListMediator = TabbedFlexboxListMediator( recyclerView, tabLayout, it.emojiListItems.mapIndexedNotNull { index, emojiListItemType -> diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt index 8e20f800f7..02545afcc5 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt @@ -3,6 +3,7 @@ package net.pantasystem.milktea.note.reaction.choices import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.LinearLayout import androidx.databinding.DataBindingUtil import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter @@ -15,9 +16,12 @@ import net.pantasystem.milktea.note.EmojiType import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.ItemEmojiChoiceBinding import net.pantasystem.milktea.note.databinding.ItemEmojiListItemHeaderBinding +import net.pantasystem.milktea.note.reaction.CustomEmojiImageViewSizeHelper.applySizeByAspectRatio +import net.pantasystem.milktea.note.reaction.ImageAspectRatioCache import net.pantasystem.milktea.note.reaction.SaveImageAspectRequestListener class EmojiListItemsAdapter( + private val isApplyImageAspectRatio: Boolean, private val onEmojiSelected: (EmojiType) -> Unit, private val onEmojiLongClicked: (EmojiType) -> Boolean, ) : ListAdapter( @@ -49,7 +53,7 @@ class EmojiListItemsAdapter( } sealed class VH(view: View) : RecyclerView.ViewHolder(view) - class EmojiVH(val binding: ItemEmojiChoiceBinding) : VH(binding.root) { + class EmojiVH(val binding: ItemEmojiChoiceBinding, private val isApplyImageAspectRatio: Boolean) : VH(binding.root) { fun onBind( item: EmojiType, onEmojiSelected: (EmojiType) -> Unit, @@ -57,6 +61,14 @@ class EmojiListItemsAdapter( ) { when (item) { is EmojiType.CustomEmoji -> { + if (isApplyImageAspectRatio) { + binding.reactionImagePreview.applySizeByAspectRatio( + 20, + item.emoji.aspectRatio ?: ImageAspectRatioCache.get( + item.emoji.url ?: item.emoji.uri + ) + ) + } GlideApp.with(binding.reactionImagePreview) .load(item.emoji.url ?: item.emoji.uri) // FIXME: webpの場合うまく表示できなくなる @@ -68,6 +80,7 @@ class EmojiListItemsAdapter( ) ) .into(binding.reactionImagePreview) + binding.reactionStringPreview.setMemoVisibility(View.GONE) binding.reactionImagePreview.setMemoVisibility(View.VISIBLE) } @@ -122,7 +135,8 @@ class EmojiListItemsAdapter( false ) return EmojiVH( - binding + binding, + isApplyImageAspectRatio ) } } diff --git a/modules/features/note/src/main/res/layout/item_emoji_choice.xml b/modules/features/note/src/main/res/layout/item_emoji_choice.xml index 543af7919c..9619be61da 100644 --- a/modules/features/note/src/main/res/layout/item_emoji_choice.xml +++ b/modules/features/note/src/main/res/layout/item_emoji_choice.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android"> Date: Fri, 26 May 2023 19:07:12 +0900 Subject: [PATCH 225/432] refactor --- .../user/MediatorUserDataSource.kt | 13 +------------ .../data/infrastructure/user/db/UserRecord.kt | 19 ++++++++++++++++++- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt index 6b8694655b..2507f6bf01 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt @@ -97,18 +97,7 @@ class MediatorUserDataSource @Inject constructor( memCache.put(user.id, user) - val newRecord = UserRecord( - accountId = user.id.accountId, - serverId = user.id.id, - avatarUrl = user.avatarUrl, - host = user.host, - isBot = user.isBot, - isCat = user.isCat, - isSameHost = user.isSameHost, - name = user.name, - userName = user.userName, - avatarBlurhash = user.avatarBlurhash, - ) + val newRecord = UserRecord.from(user) val record = userDao.get(user.id.accountId, user.id.id) val result = if (record == null) AddResult.Created else AddResult.Updated val dbId = if (record == null) { diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt index 38e67ddbf6..61ed7d58c9 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt @@ -64,7 +64,24 @@ data class UserRecord( @ColumnInfo(name = "id") @PrimaryKey(autoGenerate = true) val id: Long = 0L, -) +) { + companion object { + fun from(user: User): UserRecord { + return UserRecord( + accountId = user.id.accountId, + serverId = user.id.id, + avatarUrl = user.avatarUrl, + host = user.host, + isBot = user.isBot, + isCat = user.isCat, + isSameHost = user.isSameHost, + name = user.name, + userName = user.userName, + avatarBlurhash = user.avatarBlurhash, + ) + } + } +} @Entity( tableName = "user_info_state", From 6438f7e5a8679d81219d4a2d4ab3807231ead92e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 26 May 2023 19:49:59 +0900 Subject: [PATCH 226/432] fix --- .../milktea/note/reaction/choices/EmojiListItemsAdapter.kt | 2 +- modules/features/note/src/main/res/layout/item_emoji_choice.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt index 02545afcc5..75c476ab63 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt @@ -63,7 +63,7 @@ class EmojiListItemsAdapter( is EmojiType.CustomEmoji -> { if (isApplyImageAspectRatio) { binding.reactionImagePreview.applySizeByAspectRatio( - 20, + 28, item.emoji.aspectRatio ?: ImageAspectRatioCache.get( item.emoji.url ?: item.emoji.uri ) diff --git a/modules/features/note/src/main/res/layout/item_emoji_choice.xml b/modules/features/note/src/main/res/layout/item_emoji_choice.xml index 9619be61da..4a7d97314f 100644 --- a/modules/features/note/src/main/res/layout/item_emoji_choice.xml +++ b/modules/features/note/src/main/res/layout/item_emoji_choice.xml @@ -6,7 +6,7 @@ android:layout_width="wrap_content" android:layout_height="36dp" android:padding="4dp" - android:layout_margin="4dp" + android:layout_margin="8dp" android:background="?attr/selectableItemBackgroundBorderless" > Date: Fri, 26 May 2023 20:53:37 +0900 Subject: [PATCH 227/432] =?UTF-8?q?feat:=20=E6=AF=94=E8=BC=83=E6=99=82?= =?UTF-8?q?=E3=81=AE=E6=9D=A1=E4=BB=B6=E3=82=92=E4=BF=9D=E5=AD=98=E5=8F=AF?= =?UTF-8?q?=E8=83=BD=E3=81=AA=E3=83=87=E3=83=BC=E3=82=BF=E3=81=A8=E6=AF=94?= =?UTF-8?q?=E8=BC=83=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/MediatorUserDataSource.kt | 2 +- .../data/infrastructure/user/db/UserRecord.kt | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt index 2507f6bf01..a45982e45c 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/MediatorUserDataSource.kt @@ -109,7 +109,7 @@ class MediatorUserDataSource @Inject constructor( // NOTE: 新たに追加される予定のオブジェクトと既にキャッシュしているオブジェクトの絵文字リストを比較している // NOTE: 比較した上で同一でなければキャッシュの更新処理を行う - if (record?.toModel()?.emojis?.toSet() != user.emojis.toSet()) { + if (!record?.emojis.isEqualToModels(user.emojis)) { // NOTE: 既にキャッシュに存在していた場合一度全て剥がす if (record != null) { userDao.detachAllUserEmojis(dbId) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt index 61ed7d58c9..5646834ed4 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/user/db/UserRecord.kt @@ -297,6 +297,25 @@ data class UserEmojiRecord( aspectRatio = aspectRatio, ) } + + fun isEqualToModel(model: Emoji): Boolean { + return name == model.name && + url == model.url && + uri == model.uri && + aspectRatio == model.aspectRatio + } +} + +fun List?.isEqualToModels(models: List): Boolean { + if (this == null && models.isEmpty()) return true + if (this == null) return false + if (size != models.size) return false + val records = this.toSet() + return models.all { model -> + records.any { record -> + record.isEqualToModel(model) + } + } } @Entity( From a486215bc63ca9dacac756ed76fce167ef9ca6e6 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 27 May 2023 14:46:01 +0900 Subject: [PATCH 228/432] =?UTF-8?q?refactor:=20=E6=A4=9C=E7=B4=A2=E6=99=82?= =?UTF-8?q?=E3=81=AE=E7=8A=B6=E6=85=8B=E3=82=92=E4=B8=80=E3=81=A4=E3=81=AE?= =?UTF-8?q?=E9=85=8D=E5=88=97=E3=81=A7=E8=A1=A8=E7=8F=BE=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/EmojiPickerUiState.kt | 49 +++++++++++-------- .../note/emojis/EmojiPickerFragment.kt | 27 ---------- .../main/res/layout/fragment_emoji_picker.xml | 21 +------- 3 files changed, 30 insertions(+), 67 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt index 8ba2e1640c..45f982b3cd 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt @@ -173,27 +173,8 @@ data class EmojiPickerUiState( } - val emojiListItems: List = listOf( - EmojiListItemType.Header(StringSource.invoke(R.string.user)), - ) + userSettingEmojis.map { - EmojiListItemType.EmojiItem(it) - } + EmojiListItemType.Header(StringSource.invoke(R.string.often_use)) + frequencyUsedReactionsV2.map { - EmojiListItemType.EmojiItem(it) - } + EmojiListItemType.Header(StringSource(R.string.recently_used)) + recentlyUsed.map { - EmojiListItemType.EmojiItem(it) - } + EmojiListItemType.Header(StringSource.invoke(R.string.other)) + otherEmojis.map { - EmojiListItemType.EmojiItem(it) - } + categories.map { category -> - listOf(EmojiListItemType.Header(StringSource.invoke(category))) + getCategoryBy(category).map { - EmojiListItemType.EmojiItem(it) - } - }.flatten() + val emojiListItems: List = generateEmojiListItems() - val searchFilteredEmojis = customEmojis.filterEmojiBy(keyword).map { - EmojiType.CustomEmoji(it) - }.sortedBy { - LevenshteinDistance(it.emoji.name, keyword) - } val tabHeaderLabels = emojiListItems.mapNotNull { (it as? EmojiListItemType.Header)?.label @@ -204,6 +185,34 @@ data class EmojiPickerUiState( emojiType.areItemsTheSame(it) } } + + private fun generateEmojiListItems(): List { + return if (keyword.isBlank()) { + listOf( + EmojiListItemType.Header(StringSource.invoke(R.string.user)), + ) + userSettingEmojis.map { + EmojiListItemType.EmojiItem(it) + } + EmojiListItemType.Header(StringSource.invoke(R.string.often_use)) + frequencyUsedReactionsV2.map { + EmojiListItemType.EmojiItem(it) + } + EmojiListItemType.Header(StringSource(R.string.recently_used)) + recentlyUsed.map { + EmojiListItemType.EmojiItem(it) + } + EmojiListItemType.Header(StringSource.invoke(R.string.other)) + otherEmojis.map { + EmojiListItemType.EmojiItem(it) + } + categories.map { category -> + listOf(EmojiListItemType.Header(StringSource.invoke(category))) + getCategoryBy(category).map { + EmojiListItemType.EmojiItem(it) + } + }.flatten() + } else { + customEmojis.filterEmojiBy(keyword).map { + EmojiType.CustomEmoji(it) + }.sortedBy { + LevenshteinDistance(it.emoji.name, keyword) + }.map { + EmojiListItemType.EmojiItem(it) + } + } + } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt index ad7e456299..0265290625 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt @@ -29,7 +29,6 @@ import net.pantasystem.milktea.note.EmojiListItemType import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentEmojiPickerBinding import net.pantasystem.milktea.note.emojis.viewmodel.EmojiPickerViewModel -import net.pantasystem.milktea.note.reaction.choices.EmojiChoicesAdapter import net.pantasystem.milktea.note.reaction.choices.EmojiListItemsAdapter import net.pantasystem.milktea.note.toTextReaction @@ -53,7 +52,6 @@ class EmojiPickerFragment : Fragment(R.layout.fragment_emoji_picker), ReactionSe scope = viewLifecycleOwner.lifecycleScope, fragmentManager = childFragmentManager, lifecycleOwner = viewLifecycleOwner, - searchSuggestionListView = binding.searchSuggestionsView, tabLayout = binding.reactionChoicesTab, recyclerView = binding.reactionChoicesViewPager, searchWordTextField = binding.searchReactionEditText, @@ -95,7 +93,6 @@ class EmojiSelectionBinder( val scope: CoroutineScope, val fragmentManager: FragmentManager, val lifecycleOwner: LifecycleOwner, - val searchSuggestionListView: RecyclerView, val tabLayout: TabLayout, val recyclerView: RecyclerView, val searchWordTextField: EditText, @@ -104,30 +101,7 @@ class EmojiSelectionBinder( val onSearchEmojiTextFieldEntered: (String) -> Unit, ) { - private val flexBoxLayoutManager: FlexboxLayoutManager by lazy { - val flexBoxLayoutManager = FlexboxLayoutManager(context) - flexBoxLayoutManager.alignItems = AlignItems.STRETCH - flexBoxLayoutManager - } - fun bind() { - val searchedReactionAdapter = EmojiChoicesAdapter( - onEmojiSelected = { - onReactionSelected(it.toTextReaction()) - }, - onEmojiLongClicked = { emojiType -> - val exists = emojiPickerViewModel.uiState.value.isExistsConfig(emojiType) - if (!exists) { - AddEmojiToUserConfigDialog.newInstance(emojiType) - .show(fragmentManager, "AddEmojiToUserConfigDialog") - true - } else { - false - } - } - ) - searchSuggestionListView.adapter = searchedReactionAdapter - searchSuggestionListView.layoutManager = flexBoxLayoutManager val adapter = EmojiListItemsAdapter( @@ -163,7 +137,6 @@ class EmojiSelectionBinder( lifecycleOwner.repeatOnLifecycle(Lifecycle.State.RESUMED) { emojiPickerViewModel.uiState.collect { adapter.submitList(it.emojiListItems) - searchedReactionAdapter.submitList(it.searchFilteredEmojis) } } } diff --git a/modules/features/note/src/main/res/layout/fragment_emoji_picker.xml b/modules/features/note/src/main/res/layout/fragment_emoji_picker.xml index 6425875308..69499bb0f1 100644 --- a/modules/features/note/src/main/res/layout/fragment_emoji_picker.xml +++ b/modules/features/note/src/main/res/layout/fragment_emoji_picker.xml @@ -51,28 +51,9 @@ android:id="@+id/reaction_choices_view_pager" android:layout_width="match_parent" android:layout_height="match_parent" - android:visibility="@{ emojiPickerViewModel.uiState.isSearchMode ? View.GONE : View.VISIBLE }" android:layout_below="@id/reaction_choices_tab" + android:visibility="visible" /> - - - - - - - From e14af19e50fdab6ca891df15cf5265746104dadf Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 00:28:12 +0900 Subject: [PATCH 229/432] =?UTF-8?q?feat:=20pagination=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E3=81=8B=E8=A1=A8=E7=8F=BE=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/model/account/page/Pageable.kt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt index d1b25655c6..7316c06bf6 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt @@ -369,7 +369,7 @@ sealed class Pageable : Serializable { sealed class Mastodon : Pageable() { data class PublicTimeline( val isOnlyMedia: Boolean? = null - ) : Mastodon(), CanOnlyMedia { + ) : Mastodon(), CanOnlyMedia, UntilPaginate, SincePaginate { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_PUBLIC_TIMELINE, @@ -390,7 +390,7 @@ sealed class Pageable : Serializable { data class LocalTimeline( val isOnlyMedia: Boolean? = null - ) : Mastodon(), CanOnlyMedia { + ) : Mastodon(), CanOnlyMedia, UntilPaginate, SincePaginate { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_LOCAL_TIMELINE, @@ -410,7 +410,7 @@ sealed class Pageable : Serializable { } data class HashTagTimeline(val hashtag: String, val isOnlyMedia: Boolean? = null) : - Mastodon(), CanOnlyMedia { + Mastodon(), CanOnlyMedia, SincePaginate, UntilPaginate { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_HASHTAG_TIMELINE, @@ -430,7 +430,7 @@ sealed class Pageable : Serializable { } } - data class ListTimeline(val listId: String) : Mastodon() { + data class ListTimeline(val listId: String) : Mastodon(), SincePaginate, UntilPaginate { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_LIST_TIMELINE, @@ -439,7 +439,7 @@ sealed class Pageable : Serializable { } } - object HomeTimeline : Mastodon() { + object HomeTimeline : Mastodon(), SincePaginate, UntilPaginate { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_HOME_TIMELINE, @@ -452,7 +452,7 @@ sealed class Pageable : Serializable { val isOnlyMedia: Boolean? = null, val excludeReplies: Boolean? = null, val excludeReblogs: Boolean? = null, - ) : Mastodon(), CanOnlyMedia { + ) : Mastodon(), CanOnlyMedia, SincePaginate, UntilPaginate { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_USER_TIMELINE, @@ -472,7 +472,7 @@ sealed class Pageable : Serializable { } } - object BookmarkTimeline : Mastodon() { + object BookmarkTimeline : Mastodon(), SincePaginate, UntilPaginate { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_BOOKMARK_TIMELINE @@ -495,7 +495,7 @@ sealed class Pageable : Serializable { data class TagTimeline( val tag: String, - ) : Mastodon() { + ) : Mastodon(), SincePaginate, UntilPaginate { override fun toParams(): PageParams { return PageParams( type = PageType.MASTODON_TAG_TIMELINE, From 7b7a4a8b0455e8361995cf4e689cf68b92f81c6c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 00:31:26 +0900 Subject: [PATCH 230/432] =?UTF-8?q?feat:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E4=BF=9D=E6=8C=81=E3=81=AE=E5=8F=AF=E5=90=A6?= =?UTF-8?q?=E3=82=92=E4=BF=9D=E5=AD=98=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/data/infrastructure/DataBase.kt | 3 ++- .../milktea/data/infrastructure/account/page/db/PageRecord.kt | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt index cb2de0a328..bfd31978c2 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt @@ -117,7 +117,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut PleromaMetadataFeatures::class, ], - version = 46, + version = 47, exportSchema = true, autoMigrations = [ AutoMigration(from = 11, to = 12), @@ -155,6 +155,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut AutoMigration(from = 43, to = 44), AutoMigration(from = 44, to = 45), AutoMigration(from = 45, to = 46), + AutoMigration(from = 46, to = 47), ], views = [UserView::class, GroupMemberView::class, UserListMemberView::class] ) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt index 0b2a217179..3140939512 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt @@ -19,6 +19,9 @@ data class PageRecord( @Embedded val pageParams: PageRecordParams, + @ColumnInfo(name = "isSavePagePosition") + val isSavePagePosition: Boolean? = false, + @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "pageId") var pageId: Long From 0fbbf0695d8b1f9508c7baa1a0e06d5569cc2867 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 00:31:30 +0900 Subject: [PATCH 231/432] =?UTF-8?q?feat:=20=E3=83=9A=E3=83=BC=E3=82=B8?= =?UTF-8?q?=E4=BD=8D=E7=BD=AE=E4=BF=9D=E6=8C=81=E3=81=AE=E5=8F=AF=E5=90=A6?= =?UTF-8?q?=E3=82=92=E4=BF=9D=E5=AD=98=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../47.json | 3884 +++++++++++++++++ 1 file changed, 3884 insertions(+) create mode 100644 modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/47.json diff --git a/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/47.json b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/47.json new file mode 100644 index 0000000000..60f75a0d6b --- /dev/null +++ b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/47.json @@ -0,0 +1,3884 @@ +{ + "formatVersion": 1, + "database": { + "version": 47, + "identityHash": "74b428945f0dd11983704517d6b662a3", + "entities": [ + { + "tableName": "connection_information", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT NOT NULL, `instanceBaseUrl` TEXT NOT NULL, `encryptedI` TEXT NOT NULL, `viaName` TEXT, `createdAt` TEXT NOT NULL, `isDirect` INTEGER NOT NULL, `updatedAt` TEXT NOT NULL, PRIMARY KEY(`accountId`, `encryptedI`, `instanceBaseUrl`), FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceBaseUrl", + "columnName": "instanceBaseUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedI", + "columnName": "encryptedI", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "viaName", + "columnName": "viaName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirect", + "columnName": "isDirect", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "encryptedI", + "instanceBaseUrl" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "reaction_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `accountId` INTEGER, `target_post_id` TEXT, `target_user_id` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetPostId", + "columnName": "target_post_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetUserId", + "columnName": "target_user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "reaction_user_setting", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`reaction`, `instance_domain`))", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "reaction", + "instance_domain" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "page", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT, `title` TEXT NOT NULL, `pageNumber` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `global_timeline_with_files` INTEGER, `global_timeline_type` TEXT, `local_timeline_with_files` INTEGER, `local_timeline_exclude_nsfw` INTEGER, `local_timeline_type` TEXT, `hybrid_timeline_withFiles` INTEGER, `hybrid_timeline_includeLocalRenotes` INTEGER, `hybrid_timeline_includeMyRenotes` INTEGER, `hybrid_timeline_includeRenotedMyRenotes` INTEGER, `hybrid_timeline_type` TEXT, `home_timeline_withFiles` INTEGER, `home_timeline_includeLocalRenotes` INTEGER, `home_timeline_includeMyRenotes` INTEGER, `home_timeline_includeRenotedMyRenotes` INTEGER, `home_timeline_type` TEXT, `user_list_timeline_listId` TEXT, `user_list_timeline_withFiles` INTEGER, `user_list_timeline_includeLocalRenotes` INTEGER, `user_list_timeline_includeMyRenotes` INTEGER, `user_list_timeline_includeRenotedMyRenotes` INTEGER, `user_list_timeline_type` TEXT, `mention_following` INTEGER, `mention_visibility` TEXT, `mention_type` TEXT, `show_noteId` TEXT, `show_type` TEXT, `tag_tag` TEXT, `tag_reply` INTEGER, `tag_renote` INTEGER, `tag_withFiles` INTEGER, `tag_poll` INTEGER, `tag_type` TEXT, `featured_offset` INTEGER, `featured_type` TEXT, `notification_following` INTEGER, `notification_markAsRead` INTEGER, `notification_type` TEXT, `user_userId` TEXT, `user_includeReplies` INTEGER, `user_includeMyRenotes` INTEGER, `user_withFiles` INTEGER, `user_type` TEXT, `search_query` TEXT, `search_host` TEXT, `search_userId` TEXT, `search_type` TEXT, `favorite_type` TEXT, `antenna_antennaId` TEXT, `antenna_type` TEXT, FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageNumber", + "columnName": "pageNumber", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.withFiles", + "columnName": "global_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.type", + "columnName": "global_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localTimeline.withFiles", + "columnName": "local_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.excludeNsfw", + "columnName": "local_timeline_exclude_nsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.type", + "columnName": "local_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.withFiles", + "columnName": "hybrid_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeLocalRenotes", + "columnName": "hybrid_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeMyRenotes", + "columnName": "hybrid_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeRenotedMyRenotes", + "columnName": "hybrid_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.type", + "columnName": "hybrid_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "homeTimeline.withFiles", + "columnName": "home_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeLocalRenotes", + "columnName": "home_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeMyRenotes", + "columnName": "home_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeRenotedMyRenotes", + "columnName": "home_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.type", + "columnName": "home_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.listId", + "columnName": "user_list_timeline_listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.withFiles", + "columnName": "user_list_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeLocalRenotes", + "columnName": "user_list_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeMyRenotes", + "columnName": "user_list_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeRenotedMyRenotes", + "columnName": "user_list_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.type", + "columnName": "user_list_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.following", + "columnName": "mention_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mention.visibility", + "columnName": "mention_visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.type", + "columnName": "mention_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.noteId", + "columnName": "show_noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.type", + "columnName": "show_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.tag", + "columnName": "tag_tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.reply", + "columnName": "tag_reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.renote", + "columnName": "tag_renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.withFiles", + "columnName": "tag_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.poll", + "columnName": "tag_poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.type", + "columnName": "tag_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "featured.offset", + "columnName": "featured_offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "featured.type", + "columnName": "featured_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notification.following", + "columnName": "notification_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.markAsRead", + "columnName": "notification_markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.type", + "columnName": "notification_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.userId", + "columnName": "user_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeReplies", + "columnName": "user_includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeMyRenotes", + "columnName": "user_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.withFiles", + "columnName": "user_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.type", + "columnName": "user_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.query", + "columnName": "search_query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.host", + "columnName": "search_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.userId", + "columnName": "search_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.type", + "columnName": "search_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "favorite.type", + "columnName": "favorite_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.antennaId", + "columnName": "antenna_antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.type", + "columnName": "antenna_type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_page_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "poll_choice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`choice` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`choice`, `weight`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "choice", + "columnName": "choice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "choice", + "weight", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_poll_choice_table_draft_note_id_choice", + "unique": false, + "columnNames": [ + "draft_note_id", + "choice" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_poll_choice_table_draft_note_id_choice` ON `${TABLE_NAME}` (`draft_note_id`, `choice`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "user_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, PRIMARY KEY(`userId`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_user_id_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_id_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_file_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL DEFAULT 'name none', `remote_file_id` TEXT, `file_path` TEXT, `is_sensitive` INTEGER, `type` TEXT, `thumbnailUrl` TEXT, `draft_note_id` INTEGER NOT NULL, `folder_id` TEXT, `file_id` INTEGER PRIMARY KEY AUTOINCREMENT, FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'name none'" + }, + { + "fieldPath": "remoteFileId", + "columnName": "remote_file_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileId", + "columnName": "file_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "file_id" + ] + }, + "indices": [ + { + "name": "index_draft_file_table_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_table_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_note_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `visibility` TEXT NOT NULL, `text` TEXT, `cw` TEXT, `viaMobile` INTEGER, `localOnly` INTEGER, `noExtractMentions` INTEGER, `noExtractHashtags` INTEGER, `noExtractEmojis` INTEGER, `replyId` TEXT, `renoteId` TEXT, `channelId` TEXT, `scheduleWillPostAt` TEXT, `draft_note_id` INTEGER PRIMARY KEY AUTOINCREMENT, `isSensitive` INTEGER, `multiple` INTEGER, `expiresAt` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cw", + "columnName": "cw", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "viaMobile", + "columnName": "viaMobile", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localOnly", + "columnName": "localOnly", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractMentions", + "columnName": "noExtractMentions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractHashtags", + "columnName": "noExtractHashtags", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractEmojis", + "columnName": "noExtractEmojis", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "replyId", + "columnName": "replyId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "renoteId", + "columnName": "renoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "scheduleWillPostAt", + "columnName": "scheduleWillPostAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.multiple", + "columnName": "multiple", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.expiresAt", + "columnName": "expiresAt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_draft_note_table_accountId_text", + "unique": false, + "columnNames": [ + "accountId", + "text" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_note_table_accountId_text` ON `${TABLE_NAME}` (`accountId`, `text`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "url_preview", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `icon` TEXT, `description` TEXT, `thumbnail` TEXT, `siteName` TEXT, PRIMARY KEY(`url`))", + "fields": [ + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnail", + "columnName": "thumbnail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "siteName", + "columnName": "siteName", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "url" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "account_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remoteId` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `userName` TEXT NOT NULL, `encryptedToken` TEXT NOT NULL, `instanceType` TEXT NOT NULL DEFAULT 'misskey', `accountId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedToken", + "columnName": "encryptedToken", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceType", + "columnName": "instanceType", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'misskey'" + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "accountId" + ] + }, + "indices": [ + { + "name": "index_account_table_remoteId", + "unique": false, + "columnNames": [ + "remoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_remoteId` ON `${TABLE_NAME}` (`remoteId`)" + }, + { + "name": "index_account_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_account_table_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_userName` ON `${TABLE_NAME}` (`userName`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "page_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `title` TEXT NOT NULL, `weight` INTEGER NOT NULL, `isSavePagePosition` INTEGER, `pageId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `withFiles` INTEGER, `excludeNsfw` INTEGER, `includeLocalRenotes` INTEGER, `includeMyRenotes` INTEGER, `includeRenotedMyRenotes` INTEGER, `listId` TEXT, `following` INTEGER, `visibility` TEXT, `noteId` TEXT, `tag` TEXT, `reply` INTEGER, `renote` INTEGER, `poll` INTEGER, `offset` INTEGER, `markAsRead` INTEGER, `userId` TEXT, `includeReplies` INTEGER, `query` TEXT, `host` TEXT, `antennaId` TEXT, `channelId` TEXT, `clipId` TEXT)", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSavePagePosition", + "columnName": "isSavePagePosition", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageParams.type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageParams.withFiles", + "columnName": "withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.excludeNsfw", + "columnName": "excludeNsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeLocalRenotes", + "columnName": "includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeMyRenotes", + "columnName": "includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeRenotedMyRenotes", + "columnName": "includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.listId", + "columnName": "listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.following", + "columnName": "following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.tag", + "columnName": "tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.reply", + "columnName": "reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.renote", + "columnName": "renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.poll", + "columnName": "poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.offset", + "columnName": "offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.markAsRead", + "columnName": "markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.includeReplies", + "columnName": "includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.query", + "columnName": "query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.antennaId", + "columnName": "antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.clipId", + "columnName": "clipId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "pageId" + ] + }, + "indices": [ + { + "name": "index_page_table_weight", + "unique": false, + "columnNames": [ + "weight" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_weight` ON `${TABLE_NAME}` (`weight`)" + }, + { + "name": "index_page_table_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "meta_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `bannerUrl` TEXT, `cacheRemoteFiles` INTEGER, `description` TEXT, `disableGlobalTimeline` INTEGER, `disableLocalTimeline` INTEGER, `disableRegistration` INTEGER, `driveCapacityPerLocalUserMb` INTEGER, `driveCapacityPerRemoteUserMb` INTEGER, `enableDiscordIntegration` INTEGER, `enableEmail` INTEGER, `enableEmojiReaction` INTEGER, `enableGithubIntegration` INTEGER, `enableRecaptcha` INTEGER, `enableServiceWorker` INTEGER, `enableTwitterIntegration` INTEGER, `errorImageUrl` TEXT, `feedbackUrl` TEXT, `iconUrl` TEXT, `maintainerEmail` TEXT, `maintainerName` TEXT, `mascotImageUrl` TEXT, `maxNoteTextLength` INTEGER, `name` TEXT, `recaptchaSiteKey` TEXT, `secure` INTEGER, `swPublicKey` TEXT, `toSUrl` TEXT, `version` TEXT NOT NULL, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cacheRemoteFiles", + "columnName": "cacheRemoteFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "disableGlobalTimeline", + "columnName": "disableGlobalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableLocalTimeline", + "columnName": "disableLocalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableRegistration", + "columnName": "disableRegistration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerLocalUserMb", + "columnName": "driveCapacityPerLocalUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerRemoteUserMb", + "columnName": "driveCapacityPerRemoteUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableDiscordIntegration", + "columnName": "enableDiscordIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmail", + "columnName": "enableEmail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmojiReaction", + "columnName": "enableEmojiReaction", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableGithubIntegration", + "columnName": "enableGithubIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableRecaptcha", + "columnName": "enableRecaptcha", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableServiceWorker", + "columnName": "enableServiceWorker", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableTwitterIntegration", + "columnName": "enableTwitterIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "errorImageUrl", + "columnName": "errorImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "feedbackUrl", + "columnName": "feedbackUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerEmail", + "columnName": "maintainerEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerName", + "columnName": "maintainerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mascotImageUrl", + "columnName": "mascotImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maxNoteTextLength", + "columnName": "maxNoteTextLength", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recaptchaSiteKey", + "columnName": "recaptchaSiteKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secure", + "columnName": "secure", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "swPublicKey", + "columnName": "swPublicKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toSUrl", + "columnName": "toSUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "emoji_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `host` TEXT, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` TEXT, PRIMARY KEY(`name`, `instanceDomain`), FOREIGN KEY(`instanceDomain`) REFERENCES `meta_table`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_emoji_table_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [ + { + "table": "meta_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "instanceDomain" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "emoji_alias_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`alias` TEXT NOT NULL, `name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, PRIMARY KEY(`alias`, `name`, `instanceDomain`), FOREIGN KEY(`name`, `instanceDomain`) REFERENCES `emoji_table`(`name`, `instanceDomain`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "alias", + "columnName": "alias", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "alias", + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_alias_table_name_instanceDomain", + "unique": false, + "columnNames": [ + "name", + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_alias_table_name_instanceDomain` ON `${TABLE_NAME}` (`name`, `instanceDomain`)" + } + ], + "foreignKeys": [ + { + "table": "emoji_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "name", + "instanceDomain" + ], + "referencedColumns": [ + "name", + "instanceDomain" + ] + } + ] + }, + { + "tableName": "unread_notifications_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, PRIMARY KEY(`accountId`, `notificationId`), FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "nicknames", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`nickname` TEXT NOT NULL, `username` TEXT NOT NULL, `host` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "nickname", + "columnName": "nickname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_nicknames_username_host", + "unique": true, + "columnNames": [ + "username", + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_nicknames_username_host` ON `${TABLE_NAME}` (`username`, `host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "utf8_emojis_by_amio", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`codes` TEXT NOT NULL, `name` TEXT NOT NULL, `char` TEXT NOT NULL, PRIMARY KEY(`codes`))", + "fields": [ + { + "fieldPath": "codes", + "columnName": "codes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "charCode", + "columnName": "char", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "codes" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "drive_file_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `relatedAccountId` INTEGER NOT NULL, `createdAt` TEXT, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `md5` TEXT, `size` INTEGER, `url` TEXT NOT NULL, `isSensitive` INTEGER NOT NULL, `thumbnailUrl` TEXT, `folderId` TEXT, `userId` TEXT, `comment` TEXT, `blurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`relatedAccountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "relatedAccountId", + "columnName": "relatedAccountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "md5", + "columnName": "md5", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folderId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "blurhash", + "columnName": "blurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_drive_file_v1_serverId_relatedAccountId", + "unique": true, + "columnNames": [ + "serverId", + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_drive_file_v1_serverId_relatedAccountId` ON `${TABLE_NAME}` (`serverId`, `relatedAccountId`)" + }, + { + "name": "index_drive_file_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_drive_file_v1_relatedAccountId", + "unique": false, + "columnNames": [ + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_relatedAccountId` ON `${TABLE_NAME}` (`relatedAccountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "relatedAccountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "draft_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`draftNoteId` INTEGER NOT NULL, `filePropertyId` INTEGER, `localFileId` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`filePropertyId`) REFERENCES `drive_file_v1`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`localFileId`) REFERENCES `draft_local_file_v2_table`(`localFileId`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "draftNoteId", + "columnName": "draftNoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filePropertyId", + "columnName": "filePropertyId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId", + "unique": true, + "columnNames": [ + "draftNoteId", + "filePropertyId", + "localFileId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId` ON `${TABLE_NAME}` (`draftNoteId`, `filePropertyId`, `localFileId`)" + }, + { + "name": "index_draft_file_v2_table_draftNoteId", + "unique": false, + "columnNames": [ + "draftNoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId` ON `${TABLE_NAME}` (`draftNoteId`)" + }, + { + "name": "index_draft_file_v2_table_localFileId", + "unique": false, + "columnNames": [ + "localFileId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_localFileId` ON `${TABLE_NAME}` (`localFileId`)" + }, + { + "name": "index_draft_file_v2_table_filePropertyId", + "unique": false, + "columnNames": [ + "filePropertyId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_filePropertyId` ON `${TABLE_NAME}` (`filePropertyId`)" + } + ], + "foreignKeys": [ + { + "table": "drive_file_v1", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "filePropertyId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "draft_local_file_v2_table", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "localFileId" + ], + "referencedColumns": [ + "localFileId" + ] + } + ] + }, + { + "tableName": "draft_local_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `file_path` TEXT NOT NULL, `is_sensitive` INTEGER, `type` TEXT NOT NULL, `thumbnailUrl` TEXT, `folder_id` TEXT, `file_size` INTEGER, `comment` TEXT, `localFileId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "localFileId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "group_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `ownerId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "ownerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_v1_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_group_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_group_v1_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_v1_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "group_member_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`groupId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`groupId`) REFERENCES `group_v1`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_member_v1_groupId", + "unique": false, + "columnNames": [ + "groupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_member_v1_groupId` ON `${TABLE_NAME}` (`groupId`)" + }, + { + "name": "index_group_member_v1_groupId_userId", + "unique": true, + "columnNames": [ + "groupId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_member_v1_groupId_userId` ON `${TABLE_NAME}` (`groupId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "group_v1", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "groupId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `userName` TEXT NOT NULL, `name` TEXT, `avatarUrl` TEXT, `isCat` INTEGER, `isBot` INTEGER, `host` TEXT NOT NULL, `isSameHost` INTEGER NOT NULL, `avatarBlurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatarUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isCat", + "columnName": "isCat", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isBot", + "columnName": "isBot", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSameHost", + "columnName": "isSameHost", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "avatarBlurhash", + "columnName": "avatarBlurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_serverId_accountId", + "unique": true, + "columnNames": [ + "serverId", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_serverId_accountId` ON `${TABLE_NAME}` (`serverId`, `accountId`)" + }, + { + "name": "index_user_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_userName` ON `${TABLE_NAME}` (`userName`)" + }, + { + "name": "index_user_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_host", + "unique": false, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_detailed_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_detailed_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_detailed_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_emoji", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `userId` INTEGER NOT NULL, `aspectRatio` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "aspectRatio", + "columnName": "aspectRatio", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_emoji_name_userId", + "unique": true, + "columnNames": [ + "name", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_emoji_name_userId` ON `${TABLE_NAME}` (`name`, `userId`)" + }, + { + "name": "index_user_emoji_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_emoji_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "pinned_note_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_pinned_note_id_noteId_userId", + "unique": true, + "columnNames": [ + "noteId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_pinned_note_id_noteId_userId` ON `${TABLE_NAME}` (`noteId`, `userId`)" + }, + { + "name": "index_pinned_note_id_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pinned_note_id_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`faviconUrl` TEXT, `iconUrl` TEXT, `name` TEXT, `softwareName` TEXT, `softwareVersion` TEXT, `themeColor` TEXT, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "faviconUrl", + "columnName": "faviconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareName", + "columnName": "softwareName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareVersion", + "columnName": "softwareVersion", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_instance_info_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_instance_info_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_profile_field", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `value` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_profile_field_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_profile_field_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "word_filter_regex_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pattern` TEXT NOT NULL, `parentId` INTEGER NOT NULL, PRIMARY KEY(`parentId`), FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "pattern", + "columnName": "pattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentId" + ] + }, + "indices": [ + { + "name": "index_word_filter_regex_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_regex_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_word_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`word` TEXT NOT NULL, `parentId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "word", + "columnName": "word", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_word_filter_word_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_word_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_list", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_list_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_user_list_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_list_member", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userListId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userListId`) REFERENCES `user_list`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userListId", + "columnName": "userListId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_member_userListId", + "unique": false, + "columnNames": [ + "userListId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_member_userListId` ON `${TABLE_NAME}` (`userListId`)" + }, + { + "name": "index_user_list_member_userListId_userId", + "unique": true, + "columnNames": [ + "userListId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_member_userListId_userId` ON `${TABLE_NAME}` (`userListId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "user_list", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userListId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "instance_info_v1_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `host` TEXT NOT NULL, `name` TEXT, `description` TEXT, `clientMaxBodyByteSize` INTEGER, `iconUrl` TEXT, `themeColor` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "clientMaxBodyByteSize", + "columnName": "clientMaxBodyByteSize", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_instance_info_v1_table_host", + "unique": true, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_instance_info_v1_table_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "search_histories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `keyword` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keyword", + "columnName": "keyword", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_search_histories_keyword_accountId", + "unique": true, + "columnNames": [ + "keyword", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_search_histories_keyword_accountId` ON `${TABLE_NAME}` (`keyword`, `accountId`)" + }, + { + "name": "index_search_histories_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_search_histories_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_info_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_info_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_info_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_related_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_related_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_related_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nodeinfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `nodeInfoVersion` TEXT NOT NULL, `name` TEXT NOT NULL, `version` TEXT NOT NULL, PRIMARY KEY(`host`))", + "fields": [ + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nodeInfoVersion", + "columnName": "nodeInfoVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "host" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `title` TEXT NOT NULL, `description` TEXT NOT NULL, `email` TEXT NOT NULL, `version` TEXT NOT NULL, `urls_streamingApi` TEXT, `configuration_statuses_maxCharacters` INTEGER, `configuration_statuses_maxMediaAttachments` INTEGER, `configuration_polls_maxOptions` INTEGER, `configuration_polls_maxCharactersPerOption` INTEGER, `configuration_polls_minExpiration` INTEGER, `configuration_polls_maxExpiration` INTEGER, `configuration_emoji_reactions_myReactions` INTEGER, `configuration_emoji_reactions_maxReactionsPerAccount` INTEGER, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "urls.streamingApi", + "columnName": "urls_streamingApi", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxCharacters", + "columnName": "configuration_statuses_maxCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxMediaAttachments", + "columnName": "configuration_statuses_maxMediaAttachments", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxOptions", + "columnName": "configuration_polls_maxOptions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxCharactersPerOption", + "columnName": "configuration_polls_maxCharactersPerOption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.minExpiration", + "columnName": "configuration_polls_minExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxExpiration", + "columnName": "configuration_polls_maxExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactions", + "columnName": "configuration_emoji_reactions_myReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactionsPerAccount", + "columnName": "configuration_emoji_reactions_maxReactionsPerAccount", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "custom_emojis", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT, `name` TEXT NOT NULL, `emojiHost` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "emojiHost", + "columnName": "emojiHost", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_custom_emojis_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_custom_emojis_emojiHost", + "unique": false, + "columnNames": [ + "emojiHost" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost` ON `${TABLE_NAME}` (`emojiHost`)" + }, + { + "name": "index_custom_emojis_category", + "unique": false, + "columnNames": [ + "category" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_category` ON `${TABLE_NAME}` (`category`)" + }, + { + "name": "index_custom_emojis_emojiHost_name", + "unique": true, + "columnNames": [ + "emojiHost", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost_name` ON `${TABLE_NAME}` (`emojiHost`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "custom_emoji_aliases", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`emojiId` INTEGER NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`emojiId`, `value`), FOREIGN KEY(`emojiId`) REFERENCES `custom_emojis`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "emojiId", + "columnName": "emojiId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "emojiId", + "value" + ] + }, + "indices": [ + { + "name": "index_custom_emoji_aliases_emojiId_value", + "unique": false, + "columnNames": [ + "emojiId", + "value" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emoji_aliases_emojiId_value` ON `${TABLE_NAME}` (`emojiId`, `value`)" + } + ], + "foreignKeys": [ + { + "table": "custom_emojis", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "emojiId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "notification_json_cache_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, `json` TEXT NOT NULL, `key` TEXT, `weight` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `notificationId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "json", + "columnName": "json", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [ + { + "name": "index_notification_json_cache_v1_key", + "unique": false, + "columnNames": [ + "key" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notification_json_cache_v1_key` ON `${TABLE_NAME}` (`key`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_word_filters_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `filterId` TEXT NOT NULL, `phrase` TEXT NOT NULL, `wholeWord` INTEGER NOT NULL, `expiresAt` TEXT, `irreversible` INTEGER NOT NULL, `isContextHome` INTEGER NOT NULL, `isContextNotifications` INTEGER NOT NULL, `isContextPublic` INTEGER NOT NULL, `isContextThread` INTEGER NOT NULL, `isContextAccount` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `filterId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filterId", + "columnName": "filterId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phrase", + "columnName": "phrase", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "wholeWord", + "columnName": "wholeWord", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expiresAt", + "columnName": "expiresAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "irreversible", + "columnName": "irreversible", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextHome", + "columnName": "isContextHome", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextNotifications", + "columnName": "isContextNotifications", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextPublic", + "columnName": "isContextPublic", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextThread", + "columnName": "isContextThread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextAccount", + "columnName": "isContextAccount", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "filterId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "renote_mute_users", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `postedAt` TEXT, PRIMARY KEY(`userId`, `accountId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "postedAt", + "columnName": "postedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "accountId" + ] + }, + "indices": [ + { + "name": "index_renote_mute_users_postedAt", + "unique": false, + "columnNames": [ + "postedAt" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_postedAt` ON `${TABLE_NAME}` (`postedAt`)" + }, + { + "name": "index_renote_mute_users_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_fedibird_capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_mastodon_instance_fedibird_capabilities_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mastodon_instance_fedibird_capabilities_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "pleroma_metadata_features", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_pleroma_metadata_features_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pleroma_metadata_features_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + } + ], + "views": [ + { + "viewName": "user_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select user.*, nicknames.nickname from user left join nicknames on user.userName = nicknames.username and user.host = nicknames.host" + }, + { + "viewName": "group_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.groupId, u.id as userId, u.avatarUrl, u.serverId from group_member_v1 as m \n inner join group_v1 as g\n inner join user as u\n on m.groupId = g.id\n and m.userId = u.serverId\n and g.accountId = u.accountId" + }, + { + "viewName": "user_list_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.userListId, u.id as userId, u.avatarUrl, u.serverId from user_list_member as m \n inner join user_list as ul\n inner join user as u\n on m.userListId = ul.id\n and m.userId = u.serverId\n and ul.accountId = u.accountId" + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '74b428945f0dd11983704517d6b662a3')" + ] + } +} \ No newline at end of file From 469cae27d08a1d45831545e436dc6c11a700b140 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 00:36:17 +0900 Subject: [PATCH 232/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 2bb8cf6cd4..b2632f2649 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -596,6 +596,7 @@ %d人が投稿 引用として添付しますか? 下書き投稿を選択 + Remember scroll position diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 0c874f7fe1..00fd09350d 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -590,6 +590,7 @@ %d people posting Attach as a quote post? Select draft post + Remember scroll position \ No newline at end of file diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 8c1ba14d0f..1d2c0d5bc2 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -595,4 +595,5 @@ %d people posting Attach as a quote post? Select draft post + Remember scroll position From fc588a66b1fc719a261bb9223412dcbd21660518 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 00:40:41 +0900 Subject: [PATCH 233/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E3=81=8B?= =?UTF-8?q?=E3=82=89=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=81=AE=E4=BD=8D=E7=BD=AE=E3=81=AE=E4=BF=9D=E6=8C=81=E3=82=92?= =?UTF-8?q?=E9=81=B8=E6=8A=9E=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/infrastructure/account/page/db/PageRecord.kt | 4 +++- .../milktea/setting/EditTabSettingDialog.kt | 11 ++++++++++- .../src/main/res/layout/dialog_edit_tab_name.xml | 9 ++++++++- .../pantasystem/milktea/model/account/page/Page.kt | 5 +++-- 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt index 3140939512..57058963c5 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt @@ -34,6 +34,7 @@ data class PageRecord( title = page.title, weight = page.weight, pageParams = PageRecordParams.from(page.pageParams), + isSavePagePosition = page.isSavePagePosition, pageId = page.pageId ) } @@ -45,7 +46,8 @@ data class PageRecord( title = title, weight = weight, pageParams = pageParams.toParams(), - pageId = pageId + pageId = pageId, + isSavePagePosition = isSavePagePosition ?: false ) } } \ No newline at end of file diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/EditTabSettingDialog.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/EditTabSettingDialog.kt index 58a94a9258..2e29ee98cd 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/EditTabSettingDialog.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/EditTabSettingDialog.kt @@ -10,6 +10,7 @@ import androidx.fragment.app.activityViewModels import dagger.hilt.android.AndroidEntryPoint import net.pantasystem.milktea.model.account.page.CanOnlyMedia import net.pantasystem.milktea.model.account.page.Pageable +import net.pantasystem.milktea.model.account.page.UntilPaginate import net.pantasystem.milktea.setting.databinding.DialogEditTabNameBinding import net.pantasystem.milktea.setting.viewmodel.page.PageSettingViewModel @@ -42,12 +43,20 @@ class EditTabSettingDialog : AppCompatDialogFragment(){ } } + if (page.pageable() is UntilPaginate) { + binding.toggleSavePagePosition.isVisible = true + binding.toggleSavePagePosition.isChecked = page.isSavePagePosition + } else { + binding.toggleSavePagePosition.isVisible = false + } binding.okButton.setOnClickListener { val name = binding.editTabName.text?.toString() if(name?.isNotBlank() == true){ - var target = page + var target = page.copy( + isSavePagePosition = binding.toggleSavePagePosition.isChecked + ) when(val pageable = target.pageable()) { is CanOnlyMedia<*> -> { target = target.copy( diff --git a/modules/features/setting/src/main/res/layout/dialog_edit_tab_name.xml b/modules/features/setting/src/main/res/layout/dialog_edit_tab_name.xml index aacee46f49..442e2fc050 100644 --- a/modules/features/setting/src/main/res/layout/dialog_edit_tab_name.xml +++ b/modules/features/setting/src/main/res/layout/dialog_edit_tab_name.xml @@ -23,12 +23,19 @@ android:layout_below="@id/titleView" android:text="@string/settings_edit_tab_only_media"/> + + Date: Sun, 28 May 2023 01:14:23 +0900 Subject: [PATCH 234/432] =?UTF-8?q?feat:=20=E7=8F=BE=E5=9C=A8=E3=81=AE?= =?UTF-8?q?=E8=B3=BC=E8=AA=AD=E4=BD=8D=E7=BD=AE=E3=81=AB=E5=AF=BE=E5=BF=9C?= =?UTF-8?q?=E3=81=99=E3=82=8BNoteId=E3=82=92=E4=BF=9D=E6=8C=81=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/di/module/NoteModule.kt | 28 ++++++++++----- .../TimelineScrollPositionRepositoryImpl.kt | 35 +++++++++++++++++++ .../notes/TimelineScrollPositionRepository.kt | 11 ++++++ 3 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelineScrollPositionRepositoryImpl.kt create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/notes/TimelineScrollPositionRepository.kt diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt index 9166024364..6c9058f036 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/di/module/NoteModule.kt @@ -1,24 +1,22 @@ package net.pantasystem.milktea.data.di.module +import android.content.Context import dagger.Binds import dagger.Module +import dagger.Provides import dagger.hilt.InstallIn +import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent import net.pantasystem.milktea.app_store.notes.NoteTranslationStore import net.pantasystem.milktea.app_store.notes.TimelineStore -import net.pantasystem.milktea.data.infrastructure.notes.NoteStreamingImpl -import net.pantasystem.milktea.data.infrastructure.notes.NoteTranslationStoreImpl -import net.pantasystem.milktea.data.infrastructure.notes.ReplyStreamingImpl -import net.pantasystem.milktea.data.infrastructure.notes.TimelineStoreImpl +import net.pantasystem.milktea.common.getPreferences +import net.pantasystem.milktea.data.infrastructure.notes.* import net.pantasystem.milktea.data.infrastructure.notes.draft.DraftNoteRepositoryImpl import net.pantasystem.milktea.data.infrastructure.notes.impl.DraftNoteServiceImpl import net.pantasystem.milktea.data.infrastructure.notes.impl.NoteRepositoryImpl import net.pantasystem.milktea.data.infrastructure.notes.impl.ObjectBoxNoteDataSource import net.pantasystem.milktea.data.infrastructure.notes.renote.RenotesPagingServiceImpl -import net.pantasystem.milktea.model.notes.NoteDataSource -import net.pantasystem.milktea.model.notes.NoteRepository -import net.pantasystem.milktea.model.notes.NoteStreaming -import net.pantasystem.milktea.model.notes.ReplyStreaming +import net.pantasystem.milktea.model.notes.* import net.pantasystem.milktea.model.notes.draft.DraftNoteRepository import net.pantasystem.milktea.model.notes.draft.DraftNoteService import net.pantasystem.milktea.model.notes.repost.RenotesPagingService @@ -62,6 +60,20 @@ abstract class NoteBindModule{ } +@Module +@InstallIn(SingletonComponent::class) +object NoteProvideModule { + @Provides + @Singleton + fun provideTimelineScrollPositionRepository( + @ApplicationContext context: Context + ): TimelineScrollPositionRepository { + return TimelineScrollPositionRepositoryImpl( + context.getPreferences() + ) + } +} + @Module diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelineScrollPositionRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelineScrollPositionRepositoryImpl.kt new file mode 100644 index 0000000000..3a84df38b8 --- /dev/null +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/TimelineScrollPositionRepositoryImpl.kt @@ -0,0 +1,35 @@ +package net.pantasystem.milktea.data.infrastructure.notes + +import android.content.SharedPreferences +import net.pantasystem.milktea.model.notes.Note +import net.pantasystem.milktea.model.notes.TimelineScrollPositionRepository + +class TimelineScrollPositionRepositoryImpl( + private val sharedPreferences: SharedPreferences +) : TimelineScrollPositionRepository { + override suspend fun save(pageId: Long, noteId: Note.Id) { + sharedPreferences.edit() + .putString("timeline_scroll_position_note_id_$pageId", noteId.noteId) + .putLong("timeline_scroll_position_account_id_$pageId", noteId.accountId) + .apply() + } + + override suspend fun get(pageId: Long): Note.Id? { + val noteId = sharedPreferences.getString("timeline_scroll_position_note_id_$pageId", null) + val accountId = sharedPreferences.getLong("timeline_scroll_position_account_id_$pageId", 0L).takeIf { + it > 0L + } + return if (noteId == null || accountId == null) { + null + } else { + Note.Id(accountId, noteId) + } + } + + override suspend fun remove(pageId: Long) { + sharedPreferences.edit() + .remove("timeline_scroll_position_note_id_$pageId") + .remove("timeline_scroll_position_account_id_$pageId") + .apply() + } +} \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/notes/TimelineScrollPositionRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/TimelineScrollPositionRepository.kt new file mode 100644 index 0000000000..d566671b49 --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/notes/TimelineScrollPositionRepository.kt @@ -0,0 +1,11 @@ +package net.pantasystem.milktea.model.notes + +interface TimelineScrollPositionRepository { + + suspend fun save(pageId: Long, noteId: Note.Id) + + suspend fun get(pageId: Long): Note.Id? + + suspend fun remove(pageId: Long) + +} \ No newline at end of file From 03b5d501ee315bfe8d0ccac79bbfb5e68a94edef Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 01:14:43 +0900 Subject: [PATCH 235/432] =?UTF-8?q?feat:=20=E5=89=8D=E5=9B=9E=E3=81=AE?= =?UTF-8?q?=E8=B3=BC=E8=AA=AD=E4=BD=8D=E7=BD=AE=E3=81=8C=E3=81=82=E3=82=8B?= =?UTF-8?q?=E5=A0=B4=E5=90=88=E3=81=AF=E3=81=9D=E3=81=93=E3=81=8B=E3=82=89?= =?UTF-8?q?=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=82=80=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/timeline/TimelineFragment.kt | 19 ++--- .../timeline/viewmodel/TimelineViewModel.kt | 71 +++++++++++++++---- 2 files changed, 68 insertions(+), 22 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt index d34a795d6e..0630a36d93 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt @@ -34,9 +34,7 @@ import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentSwipeRefreshRecyclerViewBinding -import net.pantasystem.milktea.note.timeline.viewmodel.TimeMachineEventViewModel -import net.pantasystem.milktea.note.timeline.viewmodel.TimelineViewModel -import net.pantasystem.milktea.note.timeline.viewmodel.provideViewModel +import net.pantasystem.milktea.note.timeline.viewmodel.* import net.pantasystem.milktea.note.view.NoteCardActionHandler import net.pantasystem.milktea.note.viewmodel.NotesViewModel import javax.inject.Inject @@ -81,9 +79,14 @@ class TimelineFragment : Fragment(R.layout.fragment_swipe_refresh_recycler_view) private val mViewModel: TimelineViewModel by viewModels { TimelineViewModel.provideViewModel( timelineViewModelFactory, - null, - mPage?.accountId ?: accountId, - mPageable + accountId = (mPage?.accountId ?: accountId)?.let { + AccountId(it) + }, + pageId = mPage?.pageId?.let { + PageId(it) + }, + pageable = mPageable, + isSaveScrollPosition = mPage?.isSavePagePosition ) } @@ -236,7 +239,7 @@ class TimelineFragment : Fragment(R.layout.fragment_swipe_refresh_recycler_view) override fun onMenuItemSelected(menuItem: MenuItem): Boolean { when (menuItem.itemId) { R.id.refresh_timeline -> { - mViewModel.loadInit() + mViewModel.loadInit(ignoreSavedScrollPosition = true) return true } R.id.set_time_machine -> { @@ -251,7 +254,7 @@ class TimelineFragment : Fragment(R.layout.fragment_swipe_refresh_recycler_view) viewLifecycleOwner.lifecycleScope.launch { whenResumed { timeMachineEventViewModel.loadEvents.collect { - mViewModel.loadInit(it) + mViewModel.loadInit(it, ignoreSavedScrollPosition = true) } } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt index db26905c53..409603b45d 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt @@ -19,12 +19,12 @@ import net.pantasystem.milktea.common.PageableState import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.common_android_ui.APIErrorStringConverter -import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.CurrentAccountWatcher import net.pantasystem.milktea.model.account.UnauthorizedException import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.notes.NoteStreaming +import net.pantasystem.milktea.model.notes.TimelineScrollPositionRepository import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData @@ -41,17 +41,20 @@ class TimelineViewModel @AssistedInject constructor( private val timelineFilterServiceFactory: TimelineFilterService.Factory, planeNoteViewDataCacheFactory: PlaneNoteViewDataCache.Factory, private val configRepository: LocalConfigRepository, - @Assisted val account: Account?, - @Assisted val accountId: Long? = account?.accountId, + private val timelineScrollPositionRepository: TimelineScrollPositionRepository, + @Assisted val accountId: AccountId?, + @Assisted val pageId: PageId?, @Assisted val pageable: Pageable, + @Assisted val isSaveScrollPosition: Boolean, ) : ViewModel() { @AssistedFactory interface ViewModelAssistedFactory { fun create( - account: Account?, - accountId: Long?, + accountId: AccountId?, + pageId: PageId?, pageable: Pageable, + isSaveScrollPosition: Boolean, ): TimelineViewModel } @@ -62,7 +65,7 @@ class TimelineViewModel @AssistedInject constructor( var position: Int = 0 private val currentAccountWatcher = CurrentAccountWatcher( - if (accountId != null && accountId <= 0) null else accountId, + if (accountId?.value != null && accountId.value <= 0) null else accountId?.value, accountRepository ) @@ -156,12 +159,26 @@ class TimelineViewModel @AssistedInject constructor( } } - fun loadInit(initialUntilDate: Instant? = null) { + fun loadInit(initialUntilDate: Instant? = null, ignoreSavedScrollPosition: Boolean = false) { pagingCoroutineScope.cancel() pagingCoroutineScope.launch { cache.clear() + + if (ignoreSavedScrollPosition) { + pageId?.let { + timelineScrollPositionRepository.remove(it.value) + } + } + val savedScrollPositionId = pageId?.value?.let { + timelineScrollPositionRepository.get(it) + }?.takeIf { + !ignoreSavedScrollPosition + } + timelineStore.clear(initialUntilDate?.let { InitialLoadQuery.UntilDate(it) + } ?: savedScrollPositionId?.let { + InitialLoadQuery.UntilId(it) }) timelineStore.loadPrevious().onFailure { logger.error("load initial timeline failed", it) @@ -212,6 +229,8 @@ class TimelineViewModel @AssistedInject constructor( if (config?.isStopNoteCaptureWhenBackground == true) { cache.suspendNoteCapture() } + + saveNowScrollPosition() } } @@ -237,21 +256,45 @@ class TimelineViewModel @AssistedInject constructor( } } } + + private suspend fun saveNowScrollPosition() { + if (isSaveScrollPosition && pageId != null) { + val listState = timelineListState.value + listState.filterIsInstance().getOrNull(position)?.note?.note?.note?.id?.let { + timelineScrollPositionRepository.save( + pageId.value, + it + ) + } + } + } } @Suppress("UNCHECKED_CAST") fun TimelineViewModel.Companion.provideViewModel( assistedFactory: TimelineViewModel.ViewModelAssistedFactory, - account: Account?, - accountId: Long? = account?.accountId, + accountId: AccountId?, + pageId: PageId?, pageable: Pageable, + isSaveScrollPosition: Boolean?, ) = object : ViewModelProvider.Factory { override fun create(modelClass: Class): T { - return assistedFactory.create(account, accountId, pageable) as T + return assistedFactory.create( + accountId = accountId, + pageId = pageId, + pageable, + isSaveScrollPosition ?: false, + ) as T } } + +class PageId(val value: Long) + + +class AccountId(val value: Long) + sealed interface TimelineListItem { object Loading : TimelineListItem data class Note(val note: PlaneNoteViewData) : TimelineListItem @@ -342,10 +385,10 @@ class NoteStreamingCollector( .flatMapLatest { noteStreaming.connect(currentAccountWatcher::getAccount, pageable) }.map { - timelineStore.onReceiveNote(it.id) - }.catch { - logger.error("receive not error", it) - }.launchIn(coroutineScope + Dispatchers.IO) + timelineStore.onReceiveNote(it.id) + }.catch { + logger.error("receive not error", it) + }.launchIn(coroutineScope + Dispatchers.IO) } } From b8f891bb6425256847cd5147e31b0038613e4a98 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 01:15:57 +0900 Subject: [PATCH 236/432] =?UTF-8?q?feat:=20=E5=88=9D=E6=9C=9F=E8=A8=AD?= =?UTF-8?q?=E5=AE=9A=E6=99=82=E3=81=AB=E3=83=9B=E3=83=BC=E3=83=A0=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AE=E3=83=9A?= =?UTF-8?q?=E3=83=BC=E3=82=B8=E4=BD=8D=E7=BD=AE=E3=82=92=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E3=81=99=E3=82=8B=E8=A8=AD=E5=AE=9A=E3=82=92=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/model/account/page/PageableTemplate.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt index 3168e31250..ec473c7e3f 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt @@ -15,7 +15,7 @@ class PageableTemplate(val account: Account?) { fun localTimeline(title: String) = Page(account?.accountId?: - 1, title, 0, Pageable.LocalTimeline()) - fun homeTimeline(title: String, withFiles: Boolean? = null) = Page(account?.accountId?: - 1, title, 0, Pageable.HomeTimeline(withFiles = withFiles)) + fun homeTimeline(title: String, withFiles: Boolean? = null) = Page(account?.accountId?: - 1, title, 0, Pageable.HomeTimeline(withFiles = withFiles), isSavePagePosition = true) fun userListTimeline(listId: String) = Pageable.UserListTimeline(listId = listId) @@ -83,7 +83,8 @@ class PageableTemplate(val account: Account?) { account?.accountId ?: -1, title, 0, - Pageable.Mastodon.HomeTimeline + Pageable.Mastodon.HomeTimeline, + isSavePagePosition = true, ) } From a166f6f6b613c4fdaf651ff116dc030ef75512fd Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 01:16:34 +0900 Subject: [PATCH 237/432] =?UTF-8?q?feat:=20=E7=BF=BB=E8=A8=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index b2632f2649..84052fa14d 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -596,7 +596,7 @@ %d人が投稿 引用として添付しますか? 下書き投稿を選択 - Remember scroll position + スクロール位置を保持する From 5c6628fe2978b9ec8cc0af14c54759eb7a8240ae Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 11:09:02 +0900 Subject: [PATCH 238/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 84052fa14d..f390f2652b 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -597,6 +597,7 @@ 引用として添付しますか? 下書き投稿を選択 スクロール位置を保持する + 投稿のみ diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 00fd09350d..225be03cd1 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -591,6 +591,7 @@ Attach as a quote post? Select draft post Remember scroll position + Post only \ No newline at end of file diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 1d2c0d5bc2..fad04a851d 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -596,4 +596,5 @@ Attach as a quote post? Select draft post Remember scroll position + Post only From da07dffc12c164ec92b91c11b6c75370d008a9e2 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 11:09:33 +0900 Subject: [PATCH 239/432] =?UTF-8?q?feat:=20renote=E3=82=92=E9=99=A4?= =?UTF-8?q?=E3=81=84=E3=81=9F=E3=83=A6=E3=83=BC=E3=82=B6=E3=82=BF=E3=82=A4?= =?UTF-8?q?=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=82=92=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/account/AccountTabPagerAdapter.kt | 13 +++++++++++++ .../milktea/account/AccountTabViewModel.kt | 6 ++++++ .../milktea/user/activity/UserDetailActivity.kt | 12 ++++++++++++ .../milktea/user/viewmodel/UserDetailViewModel.kt | 8 ++++++++ 4 files changed, 39 insertions(+) diff --git a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabPagerAdapter.kt b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabPagerAdapter.kt index b19bd51955..6c5e76b39c 100644 --- a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabPagerAdapter.kt +++ b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabPagerAdapter.kt @@ -31,6 +31,7 @@ class AccountTabPagerAdapter( withFiles = true ) ) + is AccountTabTypes.PinNote -> userPinnedNotesFragmentFactory.create(tab.userId) is AccountTabTypes.Reactions -> UserReactionsFragment.newInstance(tab.userId) is AccountTabTypes.UserTimeline -> pageableFragmentFactory.create( @@ -65,6 +66,18 @@ class AccountTabPagerAdapter( ) AccountTabTypes.Account -> AccountFragment() AccountTabTypes.Message -> MessagingHistoryFragment() + is AccountTabTypes.MastodonUserTimelineOnlyPosts -> pageableFragmentFactory.create( + Pageable.Mastodon.UserTimeline( + tab.userId.id, + excludeReblogs = true, + ) + ) + is AccountTabTypes.UserTimelineOnlyPosts -> pageableFragmentFactory.create( + Pageable.UserTimeline( + tab.userId.id, + includeMyRenotes = false, + ) + ) } } diff --git a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabViewModel.kt b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabViewModel.kt index 8f61b1986d..0debb52d65 100644 --- a/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabViewModel.kt +++ b/modules/features/account/src/main/java/net/pantasystem/milktea/account/AccountTabViewModel.kt @@ -34,6 +34,7 @@ class AccountTabViewModel @Inject constructor( if (isEnableMessaging) AccountTabTypes.Message else null, AccountTabTypes.UserTimeline(userId), AccountTabTypes.UserTimelineWithReplies(userId), + AccountTabTypes.UserTimelineOnlyPosts(userId), AccountTabTypes.PinNote(userId), AccountTabTypes.Media(userId), if (isEnableGallery) AccountTabTypes.Gallery( @@ -47,6 +48,7 @@ class AccountTabViewModel @Inject constructor( AccountTabTypes.Account, AccountTabTypes.MastodonUserTimeline(userId), AccountTabTypes.MastodonUserTimelineWithReplies(userId), + AccountTabTypes.MastodonUserTimelineOnlyPosts(userId), AccountTabTypes.MastodonMedia(userId) ) } @@ -70,6 +72,8 @@ sealed class AccountTabTypes( data class UserTimelineWithReplies(val userId: User.Id) : AccountTabTypes(R.string.notes_and_replies) + data class UserTimelineOnlyPosts(val userId: User.Id) : AccountTabTypes(R.string.post_only) + data class PinNote(val userId: User.Id) : AccountTabTypes(R.string.pin) data class Gallery(val userId: User.Id, val accountId: Long) : AccountTabTypes(R.string.gallery) @@ -78,6 +82,8 @@ sealed class AccountTabTypes( data class Media(val userId: User.Id) : AccountTabTypes(R.string.media) data class MastodonUserTimeline(val userId: User.Id) : AccountTabTypes(R.string.post) + + data class MastodonUserTimelineOnlyPosts(val userId: User.Id) : AccountTabTypes(R.string.post_only) data class MastodonUserTimelineWithReplies(val userId: User.Id) : AccountTabTypes(R.string.notes_and_replies) diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt index 12b0027ee5..b569a753b8 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt @@ -513,6 +513,18 @@ class UserTimelinePagerAdapterV2( excludeReplies = false, ) ) + is UserDetailTabType.MastodonUserTimelineOnlyPosts -> pageableFragmentFactory.create( + Pageable.Mastodon.UserTimeline( + tab.userId.id, + excludeReblogs = true, + ) + ) + is UserDetailTabType.UserTimelineOnlyPosts -> pageableFragmentFactory.create( + Pageable.UserTimeline( + tab.userId.id, + includeMyRenotes = false, + ) + ) } } diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt index 9178e41788..639c93bb9f 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt @@ -127,6 +127,7 @@ class UserDetailViewModel @AssistedInject constructor( Account.InstanceType.MISSKEY -> { listOfNotNull( UserDetailTabType.UserTimeline(user.id), + UserDetailTabType.UserTimelineOnlyPosts(user.id), UserDetailTabType.UserTimelineWithReplies(user.id), UserDetailTabType.PinNote(user.id), UserDetailTabType.Media(user.id), @@ -139,6 +140,7 @@ class UserDetailViewModel @AssistedInject constructor( Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { listOf( UserDetailTabType.MastodonUserTimeline(user.id), + UserDetailTabType.MastodonUserTimelineOnlyPosts(user.id), UserDetailTabType.MastodonUserTimelineWithReplies(user.id), UserDetailTabType.MastodonMedia(user.id) ) @@ -374,6 +376,9 @@ sealed class UserDetailTabType( data class UserTimeline(val userId: User.Id) : UserDetailTabType(R.string.post) data class UserTimelineWithReplies(val userId: User.Id) : UserDetailTabType(R.string.notes_and_replies) + + data class UserTimelineOnlyPosts(val userId: User.Id) : UserDetailTabType(R.string.post_only) + data class PinNote(val userId: User.Id) : UserDetailTabType(R.string.pin) data class Gallery(val userId: User.Id, val accountId: Long) : UserDetailTabType(R.string.gallery) @@ -383,5 +388,8 @@ sealed class UserDetailTabType( data class MastodonUserTimeline(val userId: User.Id) : UserDetailTabType(R.string.post) data class MastodonUserTimelineWithReplies(val userId: User.Id) : UserDetailTabType(R.string.notes_and_replies) + + data class MastodonUserTimelineOnlyPosts(val userId: User.Id) : UserDetailTabType(R.string.post_only) + data class MastodonMedia(val userId: User.Id) : UserDetailTabType(R.string.media) } \ No newline at end of file From 87563c8b7805f8f35f364788cbacbbcd2779e389 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 11:22:03 +0900 Subject: [PATCH 240/432] =?UTF-8?q?fix:=20=E4=BE=8B=E5=A4=96=E3=81=AE?= =?UTF-8?q?=E6=8F=A1=E3=82=8A=E3=81=A4=E3=81=B6=E3=81=97=E3=82=92=E4=BF=AE?= =?UTF-8?q?=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/user/viewmodel/UserDetailViewModel.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt index 639c93bb9f..2beb67fc12 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt @@ -233,7 +233,7 @@ class UserDetailViewModel @AssistedInject constructor( viewModelScope.launch { userState.value?.let { user -> blockRepository.create(user.id).mapCancellableCatching { - userRepository.sync(user.id) + userRepository.sync(user.id).getOrThrow() }.onFailure { logger.error("block failed", it) _errors.tryEmit(it) @@ -311,7 +311,7 @@ class UserDetailViewModel @AssistedInject constructor( runCancellableCatching { getUserId() }.mapCancellableCatching { - renoteMuteRepository.create(it) + renoteMuteRepository.create(it).getOrThrow() }.onFailure { _errors.tryEmit(it) } @@ -323,7 +323,7 @@ class UserDetailViewModel @AssistedInject constructor( runCancellableCatching { getUserId() }.mapCancellableCatching { - renoteMuteRepository.delete(it) + renoteMuteRepository.delete(it).getOrThrow() }.onFailure { _errors.tryEmit(it) } From 75f4e9c83f78b5971024444f1d16af8fc219b76e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 11:25:40 +0900 Subject: [PATCH 241/432] refactor: remove logger --- .../pantasystem/milktea/common/glide/apng/ApngDecoder.kt | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/modules/common/src/main/java/net/pantasystem/milktea/common/glide/apng/ApngDecoder.kt b/modules/common/src/main/java/net/pantasystem/milktea/common/glide/apng/ApngDecoder.kt index 6a5e15787e..61ca051dbf 100644 --- a/modules/common/src/main/java/net/pantasystem/milktea/common/glide/apng/ApngDecoder.kt +++ b/modules/common/src/main/java/net/pantasystem/milktea/common/glide/apng/ApngDecoder.kt @@ -1,6 +1,5 @@ package net.pantasystem.milktea.common.glide.apng -import android.util.Log import com.bumptech.glide.load.Options import com.bumptech.glide.load.ResourceDecoder import com.bumptech.glide.load.engine.Resource @@ -10,7 +9,6 @@ import com.github.penfeizhou.animation.decode.FrameSeqDecoder import com.github.penfeizhou.animation.io.ByteBufferReader import com.github.penfeizhou.animation.loader.ByteBufferLoader import com.github.penfeizhou.animation.loader.Loader -import okhttp3.internal.toHexString import java.nio.ByteBuffer @@ -37,18 +35,14 @@ class ByteBufferApngDecoder : ResourceDecoder> } override fun handles(source: ByteBuffer, options: Options): Boolean { - Log.d("ByteBufferApngDecoder", "apng decoder on decode") val byteBufferArray = ByteArray(8) source.get(byteBufferArray, 0, 4) val header = ByteBuffer.wrap(byteBufferArray).long ushr 32 if (header != PNG) { - Log.d("ByteBufferApngDecoder", "is not png:${header.toHexString()}") return false } - val result = APNGParser.isAPNG(ByteBufferReader(source)) - Log.d("ByteBufferApngDecoder", "handlers isApng:$result") - return result + return APNGParser.isAPNG(ByteBufferReader(source)) } From a4632d93b2409866e55e5aef038b09593d345145 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 11:39:52 +0900 Subject: [PATCH 242/432] =?UTF-8?q?fix:=20=E6=AC=A1=E5=9B=9E=E8=AA=AD?= =?UTF-8?q?=E3=81=BF=E8=BE=BC=E3=81=BF=E6=99=82=E3=81=AB=E4=B8=80=E3=81=A4?= =?UTF-8?q?=E5=BE=8C=E3=82=8D=E3=81=8B=E3=82=89=E8=AA=AD=E3=81=BF=E8=BE=BC?= =?UTF-8?q?=E3=82=93=E3=81=A7=E3=81=97=E3=81=BE=E3=81=86=E5=95=8F=E9=A1=8C?= =?UTF-8?q?=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/timeline/viewmodel/TimelineViewModel.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt index 409603b45d..077ccf70f1 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt @@ -23,6 +23,7 @@ import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.CurrentAccountWatcher import net.pantasystem.milktea.model.account.UnauthorizedException import net.pantasystem.milktea.model.account.page.Pageable +import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.model.notes.NoteStreaming import net.pantasystem.milktea.model.notes.TimelineScrollPositionRepository import net.pantasystem.milktea.model.setting.LocalConfigRepository @@ -260,7 +261,14 @@ class TimelineViewModel @AssistedInject constructor( private suspend fun saveNowScrollPosition() { if (isSaveScrollPosition && pageId != null) { val listState = timelineListState.value - listState.filterIsInstance().getOrNull(position)?.note?.note?.note?.id?.let { + var savePos = position - 1 + while(savePos < listState.size && listState.getOrNull(savePos) !is TimelineListItem.Note) { + savePos++ + } + val savePosId: Note.Id? = listState.getOrNull(savePos)?.let { + (it as? TimelineListItem.Note)?.note?.note?.note?.id + } + savePosId?.also { timelineScrollPositionRepository.save( pageId.value, it From 9827c3240cf792adffb3e39784df3cf958843ca7 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 12:02:50 +0900 Subject: [PATCH 243/432] =?UTF-8?q?feat:=20=E3=82=B9=E3=82=AF=E3=83=AD?= =?UTF-8?q?=E3=83=BC=E3=83=AB=E4=BD=8D=E7=BD=AE=E3=82=92=E3=82=B9=E3=82=AF?= =?UTF-8?q?=E3=83=AD=E3=83=BC=E3=83=AB=E3=82=A4=E3=83=99=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=81=8C=E7=99=BA=E7=94=9F=E3=81=97=E3=81=9F=E3=82=BF=E3=82=A4?= =?UTF-8?q?=E3=83=9F=E3=83=B3=E3=82=B0=E3=81=A7=E3=82=82=E4=BF=9D=E5=AD=98?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common/coroutines/Throttle.kt | 13 +++++++++++++ .../note/timeline/viewmodel/TimelineViewModel.kt | 12 ++++++++---- 2 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 modules/common/src/main/java/net/pantasystem/milktea/common/coroutines/Throttle.kt diff --git a/modules/common/src/main/java/net/pantasystem/milktea/common/coroutines/Throttle.kt b/modules/common/src/main/java/net/pantasystem/milktea/common/coroutines/Throttle.kt new file mode 100644 index 0000000000..818882f2df --- /dev/null +++ b/modules/common/src/main/java/net/pantasystem/milktea/common/coroutines/Throttle.kt @@ -0,0 +1,13 @@ +package net.pantasystem.milktea.common.coroutines + +import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.transform + +fun Flow.throttleLatest(delayMillis: Long): Flow = this + .conflate() + .transform { + emit(it) + delay(delayMillis) + } \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt index 077ccf70f1..b98b246b81 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/viewmodel/TimelineViewModel.kt @@ -1,9 +1,6 @@ package net.pantasystem.milktea.note.timeline.viewmodel -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.asLiveData -import androidx.lifecycle.viewModelScope +import androidx.lifecycle.* import dagger.assisted.Assisted import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject @@ -17,6 +14,7 @@ import net.pantasystem.milktea.common.APIError import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.PageableState import net.pantasystem.milktea.common.StateContent +import net.pantasystem.milktea.common.coroutines.throttleLatest import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.common_android_ui.APIErrorStringConverter import net.pantasystem.milktea.model.account.AccountRepository @@ -129,6 +127,8 @@ class TimelineViewModel @AssistedInject constructor( private var isActive = true + private val saveScrollPositionScrolledEvent = MutableSharedFlow(extraBufferCapacity = 4) + init { viewModelScope.launch { @@ -141,6 +141,9 @@ class TimelineViewModel @AssistedInject constructor( } } + saveScrollPositionScrolledEvent.distinctUntilChanged().throttleLatest(500).onEach { + saveNowScrollPosition() + }.launchIn(viewModelScope) } @@ -245,6 +248,7 @@ class TimelineViewModel @AssistedInject constructor( viewModelScope.launch { timelineStore.releaseUnusedPages(firstVisiblePosition) } + saveScrollPositionScrolledEvent.tryEmit(firstVisiblePosition) } private fun onVisibleFirst() { From e09ef48306710c74c4a60b95e5e646ca7861a1dc Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 15:19:27 +0900 Subject: [PATCH 244/432] =?UTF-8?q?feat:=20calckey=E3=81=AE=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=82=B9=E3=82=BF=E3=83=B3=E3=82=B9=E3=83=AA=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api/misskey/InstanceInfosAPI.kt | 13 +++++++ .../milktea/auth/AuthFormScreen.kt | 4 +- .../milktea/auth/viewmodel/SignUpViewModel.kt | 38 +++++++++++++------ .../auth/viewmodel/app/AppAuthViewModel.kt | 34 +++++++++++++---- .../milktea/auth/viewmodel/app/UIState.kt | 2 +- 5 files changed, 70 insertions(+), 21 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/InstanceInfosAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/InstanceInfosAPI.kt index f5fd6c370c..b2625007d0 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/InstanceInfosAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/InstanceInfosAPI.kt @@ -31,7 +31,20 @@ class InstanceInfoAPIBuilder @Inject constructor(val okHttpClientProvider: OkHtt .client(okHttpClientProvider.get()) .build() } + + @OptIn(ExperimentalSerializationApi::class) + private val calckeyRetrofitBuilder by lazy { + Retrofit.Builder() + .baseUrl("https://api.calckey.org") + .addConverterFactory(json.asConverterFactory("application/json".toMediaType())) + .client(okHttpClientProvider.get()) + .build() + } fun build(): InstanceInfosAPI { return retrofitBuilder.create(InstanceInfosAPI::class.java) } + + fun buildCalckey(): InstanceInfosAPI { + return calckeyRetrofitBuilder.create(InstanceInfosAPI::class.java) + } } \ No newline at end of file diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/AuthFormScreen.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/AuthFormScreen.kt index df28d6e6f6..6dbb84c9fc 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/AuthFormScreen.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/AuthFormScreen.kt @@ -237,7 +237,7 @@ private fun FilteredInstances( onInputInstanceDomain: (String) -> Unit, ) { val instances = remember(uiState.misskeyInstanceInfosResponse, uiState.formState) { - uiState.misskeyInstanceInfosResponse?.instancesInfos?.filter { + uiState.misskeyInstanceInfosResponse.filter { it.meta.uri.contains(instanceDomain) || it.name.contains(instanceDomain) } ?: emptyList() } @@ -289,7 +289,7 @@ fun Preview_AuthFormScreen() { ), metaState = ResultState.Loading(StateContent.NotExist()), stateType = Authorization.BeforeAuthentication, - misskeyInstanceInfosResponse = null + misskeyInstanceInfosResponse = emptyList(), ), onShowPrivacyPolicy = {}, onShowTermsOfService = {}, diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/SignUpViewModel.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/SignUpViewModel.kt index c071b67319..fe3057cd82 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/SignUpViewModel.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/SignUpViewModel.kt @@ -3,8 +3,7 @@ package net.pantasystem.milktea.auth.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import net.pantasystem.milktea.api.misskey.InstanceInfoAPIBuilder import net.pantasystem.milktea.api.misskey.infos.InstanceInfosResponse @@ -26,11 +25,28 @@ class SignUpViewModel @Inject constructor( @OptIn(FlowPreview::class) private val instancesInfosResponse = suspend { - requireNotNull( - instancesInfosAPIBuilder.build().getInstances() - .throwIfHasError() - .body() - ) + coroutineScope { + listOf( + async { + requireNotNull( + instancesInfosAPIBuilder.buildCalckey().getInstances() + .throwIfHasError() + .body() + ) + }, + async { + requireNotNull( + instancesInfosAPIBuilder.build().getInstances() + .throwIfHasError() + .body() + ) + }, + ).awaitAll().map { + it.instancesInfos + }.flatten().distinctBy { + it.url + } + } }.asFlow().catch { logger.error("インスタンス情報の取得に失敗", it) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) @@ -69,7 +85,7 @@ class SignUpViewModel @Inject constructor( keyword = keyword, selected, info, - infos, + infos ?: emptyList(), ) }.stateIn( viewModelScope, @@ -90,12 +106,12 @@ data class SignUpUiState( val keyword: String = "", val selectedUrl: String? = "misskey.io", val instanceInfo: ResultState = ResultState.Loading(StateContent.NotExist()), - val instancesInfosResponse: InstanceInfosResponse? = null + val instancesInfosResponse: List = emptyList(), ) { - val filteredInfos = (instancesInfosResponse?.instancesInfos?.filter { + val filteredInfos = instancesInfosResponse.filter { it.url.contains(keyword) || it.name.contains(keyword) - } ?: emptyList()).let { list -> + }.let { list -> val misskeyIo = list.firstOrNull { it.url == "misskey.io" } diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AppAuthViewModel.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AppAuthViewModel.kt index 53dfbd5346..d5cedcac3f 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AppAuthViewModel.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/AppAuthViewModel.kt @@ -73,11 +73,29 @@ class AppAuthViewModel @Inject constructor( private val misskeyInstances = suspend { runCancellableCatching { withContext(Dispatchers.IO) { - requireNotNull( - instancesInfoAPIBuilder.build().getInstances() - .throwIfHasError() - .body() - ) + coroutineScope { + val instances = listOf( + async { + requireNotNull( + instancesInfoAPIBuilder.buildCalckey().getInstances() + .throwIfHasError() + .body() + ) + }, + async { + requireNotNull( + instancesInfoAPIBuilder.build().getInstances() + .throwIfHasError() + .body() + ) + } + ).awaitAll() + instances.map { + it.instancesInfos + }.flatten().distinctBy { + it.url + } + } } } }.asFlow().stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) @@ -238,7 +256,9 @@ class AppAuthViewModel @Inject constructor( waiting4ApproveState = waiting4Approve, clientId = "clientId: ${clientIdRepository.getOrCreate().clientId}", instances = instances, - misskeyInstanceInfosResponse = misskeyInstances?.getOrNull() + misskeyInstanceInfosResponse = misskeyInstances?.getOrElse { + emptyList() + } ?: emptyList() ) }.stateIn( viewModelScope, @@ -247,7 +267,7 @@ class AppAuthViewModel @Inject constructor( formState = authUserInputState.value, metaState = metaState.value, stateType = Authorization.BeforeAuthentication, - misskeyInstanceInfosResponse = null, + misskeyInstanceInfosResponse = emptyList(), ) ) diff --git a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/UIState.kt b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/UIState.kt index 09d32c94f2..5bda8f0b9c 100644 --- a/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/UIState.kt +++ b/modules/features/auth/src/main/java/net/pantasystem/milktea/auth/viewmodel/app/UIState.kt @@ -81,7 +81,7 @@ data class AuthUiState( ), val clientId: String = "", val instances: List = emptyList(), - val misskeyInstanceInfosResponse: InstanceInfosResponse? + val misskeyInstanceInfosResponse: List, ) { val isProgress by lazy { metaState is ResultState.Loading || waiting4ApproveState is ResultState.Loading From e4a158b49ec636c6bc23b87a8543bb4f8bb5364d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 17:17:35 +0900 Subject: [PATCH 245/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=83=96=E3=81=AE?= =?UTF-8?q?=E5=80=99=E8=A3=9C=E3=81=AE=E3=82=AA=E3=83=96=E3=82=B8=E3=82=A7?= =?UTF-8?q?=E3=82=AF=E3=83=88=E3=82=92=E7=94=9F=E6=88=90=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=AF=E3=83=A9=E3=82=B9=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../viewmodel/page/PageCandidateGenerator.kt | 195 ++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt new file mode 100644 index 0000000000..30eaf058c6 --- /dev/null +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt @@ -0,0 +1,195 @@ +package net.pantasystem.milktea.setting.viewmodel.page + +import net.pantasystem.milktea.common_android.resource.StringSource +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.account.page.PageType +import net.pantasystem.milktea.model.instance.Version +import net.pantasystem.milktea.model.nodeinfo.NodeInfo +import net.pantasystem.milktea.model.nodeinfo.NodeInfoRepository +import net.pantasystem.milktea.model.nodeinfo.getVersion +import net.pantasystem.milktea.setting.R +import javax.inject.Inject + +class PageCandidateGenerator @Inject constructor( + private val nodeInfoRepository: NodeInfoRepository, +) { + + suspend fun createPageCandidates(account: Account): List { + val nodeInfo = nodeInfoRepository.find(account.getHost()).getOrNull() + val version = nodeInfo?.type?.getVersion() ?: Version("0") + val isCalckey = nodeInfo?.type is NodeInfo.SoftwareType.Misskey.Calckey + return when (account.instanceType) { + Account.InstanceType.MISSKEY -> { + listOfNotNull( + PageCandidate( + account, + PageType.HOME, + StringSource(R.string.home_timeline) + ), + PageCandidate( + account, + PageType.LOCAL, + StringSource(R.string.local_timeline) + ), + PageCandidate( + account, + PageType.SOCIAL, + StringSource(R.string.hybrid_timeline) + ), + PageCandidate( + account, + PageType.GLOBAL, + StringSource(R.string.global_timeline) + ), + if (isCalckey) PageCandidate( + account, + PageType.CALCKEY_RECOMMENDED_TIMELINE, + StringSource(R.string.calckey_recomended_timeline) + ) else null, + if (version >= Version("12")) PageCandidate( + account, + PageType.ANTENNA, + StringSource(R.string.antenna) + ) else null, + PageCandidate( + account, + PageType.NOTIFICATION, + StringSource(R.string.notification) + ), + PageCandidate( + account, + PageType.USER_LIST, + StringSource(R.string.user_list) + ), + PageCandidate( + account, + PageType.MENTION, + StringSource(R.string.mention) + ), + PageCandidate( + account, + PageType.FAVORITE, + StringSource(R.string.favorite) + ), + if (version >= Version("12")) PageCandidate( + account, + PageType.CHANNEL_TIMELINE, + StringSource(R.string.channel) + ) else null, + if (version >= Version("12")) PageCandidate( + account, + PageType.CLIP_NOTES, + StringSource(R.string.clip) + ) else null, + PageCandidate( + account, + PageType.SEARCH, + StringSource(R.string.search) + ), + PageCandidate( + account, + PageType.SEARCH_HASH, + StringSource(R.string.tag) + ), + PageCandidate( + account, + PageType.FEATURED, + StringSource(R.string.featured) + ), + PageCandidate( + account, + PageType.USER, + StringSource(R.string.user) + ), + PageCandidate( + account, + PageType.DETAIL, + StringSource(R.string.detail) + ), + ) + if (version >= Version("12.75.0")) { + listOf( + PageCandidate( + account, + PageType.GALLERY_FEATURED, + StringSource(R.string.featured) + StringSource("(") + StringSource(R.string.gallery) + StringSource(")") + ), + PageCandidate( + account, + PageType.GALLERY_POPULAR, + StringSource(R.string.popular_posts) + StringSource("(") + StringSource( + R.string.gallery) + StringSource(")") + ), + PageCandidate( + account, + PageType.GALLERY_POSTS, + StringSource(R.string.gallery), + ), + PageCandidate( + account, + PageType.MY_GALLERY_POSTS, + StringSource(R.string.my_posts) + StringSource("(") + StringSource(R.string.gallery) + StringSource(")"), + ), + PageCandidate( + account, + PageType.USERS_GALLERY_POSTS, + StringSource(R.string.gallery) + StringSource("(User)") + ), + PageCandidate( + account, + PageType.I_LIKED_GALLERY_POSTS, + StringSource(R.string.my_liking) + StringSource("(") + StringSource(R.string.gallery) + StringSource(")"), + ), + PageCandidate( + account, + PageType.MY_GALLERY_POSTS, + StringSource(R.string.my_posts) + StringSource("(") + StringSource(R.string.gallery) + StringSource(")"), + ) + ) + } else { + emptyList() + } + } + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { + listOf( + PageCandidate( + account, + PageType.MASTODON_HOME_TIMELINE, + StringSource(R.string.home_timeline) + ), + PageCandidate( + account, + PageType.MASTODON_LOCAL_TIMELINE, + StringSource(R.string.local_timeline) + ), + PageCandidate( + account, + PageType.MASTODON_PUBLIC_TIMELINE, + StringSource(R.string.global_timeline), + ), + PageCandidate( + account, + PageType.NOTIFICATION, + StringSource(R.string.notification) + ), + PageCandidate( + account, + PageType.FAVORITE, + StringSource(R.string.favorite) + ), +// PageType.MASTODON_HASHTAG_TIMELINE, + PageCandidate( + account, + PageType.MASTODON_LIST_TIMELINE, + StringSource(R.string.list), + ), + PageCandidate( + account, + PageType.MASTODON_BOOKMARK_TIMELINE, + StringSource(R.string.bookmark) + ) + ) + } + } + } + +} \ No newline at end of file From 95b998acb8bf922e4c8a4266bb9561e724edb014 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 17:21:17 +0900 Subject: [PATCH 246/432] =?UTF-8?q?feat:=20=E7=9B=B4=E6=8E=A5enum=E3=81=AB?= =?UTF-8?q?=E4=BE=9D=E5=AD=98=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../compose/tab/TabItemSelectionDialog.kt | 10 +-- .../setting/compose/tab/TabItemsListScreen.kt | 3 +- .../viewmodel/page/PageSettingViewModel.kt | 72 ++++--------------- 3 files changed, 20 insertions(+), 65 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemSelectionDialog.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemSelectionDialog.kt index f9be5a6851..28d4035eab 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemSelectionDialog.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemSelectionDialog.kt @@ -11,19 +11,19 @@ import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import net.pantasystem.milktea.common_android_ui.account.page.PageTypeHelper +import getStringFromStringSource import net.pantasystem.milktea.model.account.page.PageType +import net.pantasystem.milktea.setting.viewmodel.page.PageCandidate @OptIn(ExperimentalMaterialApi::class) @Composable internal fun TabItemSelectionDialog( modifier: Modifier = Modifier, - items: List, + items: List, onClick: (PageType) -> Unit, ) { LazyColumn( @@ -32,7 +32,7 @@ internal fun TabItemSelectionDialog( items(items) { pageType -> Surface( onClick = { - onClick(pageType) + onClick(pageType.type) }, ) { Box( @@ -42,7 +42,7 @@ internal fun TabItemSelectionDialog( .fillMaxWidth(), ) { Text( - PageTypeHelper.nameByPageType(LocalContext.current, pageType), + getStringFromStringSource(pageType.name), fontWeight = FontWeight.Bold, fontSize = 16.sp ) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemsListScreen.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemsListScreen.kt index 1942a0cfa4..4cd414d314 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemsListScreen.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemsListScreen.kt @@ -14,13 +14,14 @@ import kotlinx.coroutines.launch import net.pantasystem.milktea.model.account.page.Page import net.pantasystem.milktea.model.account.page.PageType import net.pantasystem.milktea.setting.R +import net.pantasystem.milktea.setting.viewmodel.page.PageCandidate @OptIn(ExperimentalMaterialApi::class) @Composable internal fun TabItemsListScreen( dragDropState: DragAndDropState, - pageTypes: List, + pageTypes: List, list: List, onSelectPage: (PageType) -> Unit, onOptionButtonClicked: (Page) -> Unit, diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt index dc7b84e9b5..0688b4f9a6 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt @@ -12,12 +12,9 @@ import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.app_store.setting.SettingStore import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common_android.eventbus.EventBus +import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.page.* -import net.pantasystem.milktea.model.instance.Version -import net.pantasystem.milktea.model.nodeinfo.NodeInfo -import net.pantasystem.milktea.model.nodeinfo.NodeInfoRepository -import net.pantasystem.milktea.model.nodeinfo.getVersion import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserRepository import net.pantasystem.milktea.setting.PageTypeNameMap @@ -29,12 +26,13 @@ class PageSettingViewModel @Inject constructor( private val pageTypeNameMap: PageTypeNameMap, private val userRepository: UserRepository, private val accountStore: AccountStore, - private val nodeInfoRepository: NodeInfoRepository, + private val pageCandidateGenerator: PageCandidateGenerator, ) : ViewModel(), SelectPageTypeToAdd, PageSettingAction { val selectedPages = MutableStateFlow>(emptyList()) - val account = accountStore.observeCurrentAccount.stateIn(viewModelScope, SharingStarted.Eagerly, null) + val account = + accountStore.observeCurrentAccount.stateIn(viewModelScope, SharingStarted.Eagerly, null) val pageAddedEvent = EventBus() @@ -43,57 +41,7 @@ class PageSettingViewModel @Inject constructor( val pageOnUpdateEvent = EventBus() val pageTypes = account.filterNotNull().map { - val nodeInfo = nodeInfoRepository.find(it.getHost()).getOrNull() - val version = nodeInfo?.type?.getVersion() ?: Version("0") - val isCalckey = nodeInfo?.type is NodeInfo.SoftwareType.Misskey.Calckey - when(it.instanceType) { - Account.InstanceType.MISSKEY -> { - listOfNotNull( - PageType.HOME, - PageType.LOCAL, - PageType.SOCIAL, - PageType.GLOBAL, - if (isCalckey) PageType.CALCKEY_RECOMMENDED_TIMELINE else null, - if (version >= Version("12")) PageType.ANTENNA else null, - PageType.NOTIFICATION, - PageType.USER_LIST, - PageType.MENTION, - PageType.FAVORITE, - if (version >= Version("12")) PageType.CHANNEL_TIMELINE else null, - if (version >= Version("12")) PageType.CLIP_NOTES else null, - PageType.SEARCH, - PageType.SEARCH_HASH, - PageType.USER, - PageType.FEATURED, - PageType.DETAIL, - ) + if (version >= Version("12.75.0")) { - listOf( - PageType.GALLERY_FEATURED, - PageType.GALLERY_POPULAR, - PageType.GALLERY_POSTS, - PageType.USERS_GALLERY_POSTS, - PageType.MY_GALLERY_POSTS, - PageType.I_LIKED_GALLERY_POSTS, - ) - } else { - emptyList() - } - } - Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { - listOf( - PageType.MASTODON_HOME_TIMELINE, - PageType.MASTODON_LOCAL_TIMELINE, - PageType.MASTODON_PUBLIC_TIMELINE, -// PageType.MASTODON_HASHTAG_TIMELINE, - PageType.NOTIFICATION, - PageType.FAVORITE, - PageType.MASTODON_LIST_TIMELINE, - PageType.MASTODON_BOOKMARK_TIMELINE, -// PageType.MASTODON_USER_TIMELINE, - - ) - } - } + pageCandidateGenerator.createPageCandidates(it) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) init { @@ -180,7 +128,7 @@ class PageSettingViewModel @Inject constructor( } fun addUsersGalleryByIds(userIds: List) { - viewModelScope.launch { + viewModelScope.launch { runCancellableCatching { userIds.map { async { @@ -297,4 +245,10 @@ class PageSettingViewModel @Inject constructor( pageOnActionEvent.event = page } -} \ No newline at end of file +} + +data class PageCandidate( + val relatedAccount: Account, + val type: PageType, + val name: StringSource, +) \ No newline at end of file From c3ed7bb23f018446e977c7d5dd2603e6c53b6e9e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 17:40:51 +0900 Subject: [PATCH 247/432] =?UTF-8?q?feat:=20=E5=85=A8=E3=81=A6=E3=81=AE?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE=E5=80=99?= =?UTF-8?q?=E8=A3=9C=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/activities/PageSettingActivity.kt | 4 +- .../compose/tab/TabItemSelectionDialog.kt | 53 ++++++++++++------- .../setting/compose/tab/TabItemsListScreen.kt | 6 +-- .../viewmodel/page/PageSettingViewModel.kt | 23 ++++++++ 4 files changed, 63 insertions(+), 23 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt index 941bc3585d..2263f496d1 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt @@ -111,7 +111,7 @@ class PageSettingActivity : AppCompatActivity() { setContent { MdcTheme { - val pageTypes by mPageSettingViewModel.pageTypes.collectAsState() + val pageTypes by mPageSettingViewModel.pageTypesGroupedByAccount.collectAsState() val list by mPageSettingViewModel.selectedPages.collectAsState() val scope = rememberCoroutineScope() val dragAndDropState = @@ -127,7 +127,7 @@ class PageSettingActivity : AppCompatActivity() { pageTypes = pageTypes, list = list, onSelectPage = { - mPageSettingViewModel.add(it) + mPageSettingViewModel.add(it.type) }, onOptionButtonClicked = { mPageSettingViewModel.pageOnActionEvent.event = it diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemSelectionDialog.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemSelectionDialog.kt index 28d4035eab..2e4ce7e0dc 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemSelectionDialog.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemSelectionDialog.kt @@ -1,11 +1,13 @@ package net.pantasystem.milktea.setting.compose.tab +import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.MaterialTheme import androidx.compose.material.Surface import androidx.compose.material.Text import androidx.compose.runtime.Composable @@ -15,39 +17,54 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import getStringFromStringSource -import net.pantasystem.milktea.model.account.page.PageType import net.pantasystem.milktea.setting.viewmodel.page.PageCandidate +import net.pantasystem.milktea.setting.viewmodel.page.PageCandidateGroup -@OptIn(ExperimentalMaterialApi::class) +@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class) @Composable internal fun TabItemSelectionDialog( modifier: Modifier = Modifier, - items: List, - onClick: (PageType) -> Unit, + items: List, + onClick: (PageCandidate) -> Unit, ) { LazyColumn( modifier ) { - items(items) { pageType -> - Surface( - onClick = { - onClick(pageType.type) - }, - ) { - Box( - contentAlignment = Alignment.CenterStart, - modifier = Modifier - .padding(horizontal = 32.dp, vertical = 16.dp) - .fillMaxWidth(), + for (group in items) { + stickyHeader { + Surface( + Modifier.fillMaxWidth(), + color = MaterialTheme.colors.surface, ) { Text( - getStringFromStringSource(pageType.name), - fontWeight = FontWeight.Bold, - fontSize = 16.sp + "@${group.relatedAccount.userName}@${group.relatedAccount.getHost()}", + modifier = Modifier.padding(16.dp) ) } } + + items(group.candidates) { pageType -> + Surface( + onClick = { + onClick(pageType) + }, + ) { + Box( + contentAlignment = Alignment.CenterStart, + modifier = Modifier + .padding(horizontal = 32.dp, vertical = 16.dp) + .fillMaxWidth(), + ) { + Text( + getStringFromStringSource(pageType.name), + fontWeight = FontWeight.Bold, + fontSize = 16.sp + ) + } + } + } } + } } diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemsListScreen.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemsListScreen.kt index 4cd414d314..b77630de31 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemsListScreen.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/compose/tab/TabItemsListScreen.kt @@ -12,18 +12,18 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import kotlinx.coroutines.launch import net.pantasystem.milktea.model.account.page.Page -import net.pantasystem.milktea.model.account.page.PageType import net.pantasystem.milktea.setting.R import net.pantasystem.milktea.setting.viewmodel.page.PageCandidate +import net.pantasystem.milktea.setting.viewmodel.page.PageCandidateGroup @OptIn(ExperimentalMaterialApi::class) @Composable internal fun TabItemsListScreen( dragDropState: DragAndDropState, - pageTypes: List, + pageTypes: List, list: List, - onSelectPage: (PageType) -> Unit, + onSelectPage: (PageCandidate) -> Unit, onOptionButtonClicked: (Page) -> Unit, onNavigateUp: () -> Unit diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt index 0688b4f9a6..74f8824f7c 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt @@ -44,6 +44,23 @@ class PageSettingViewModel @Inject constructor( pageCandidateGenerator.createPageCandidates(it) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) + val pageTypesGroupedByAccount = combine( + accountStore.observeCurrentAccount, + accountStore.observeAccounts, + ) { ca, accounts -> + (listOfNotNull( + ca + ) + accounts.filterNot { + it.accountId == ca?.accountId + }).map { + PageCandidateGroup( + ca, + it, + pageCandidateGenerator.createPageCandidates(it) + ) + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) + init { viewModelScope.launch { account.collect { @@ -251,4 +268,10 @@ data class PageCandidate( val relatedAccount: Account, val type: PageType, val name: StringSource, +) + +data class PageCandidateGroup( + val currentAccount: Account?, + val relatedAccount: Account, + val candidates: List, ) \ No newline at end of file From c76f5bd2da0ce29c9f26918bc32f4dd7e7b2ecd7 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 17:44:37 +0900 Subject: [PATCH 248/432] =?UTF-8?q?feat:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AE=E9=96=A2=E9=80=A3=E4=BB=98=E3=81=91?= =?UTF-8?q?=E7=8A=B6=E6=B3=81=E3=82=92=E4=BF=9D=E6=8C=81=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../48.json | 3890 +++++++++++++++++ .../milktea/data/infrastructure/DataBase.kt | 3 +- .../account/page/db/PageRecord.kt | 5 + .../milktea/model/account/page/Page.kt | 22 +- 4 files changed, 3916 insertions(+), 4 deletions(-) create mode 100644 modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/48.json diff --git a/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/48.json b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/48.json new file mode 100644 index 0000000000..515f397243 --- /dev/null +++ b/modules/data/schemas/net.pantasystem.milktea.data.infrastructure.DataBase/48.json @@ -0,0 +1,3890 @@ +{ + "formatVersion": 1, + "database": { + "version": 48, + "identityHash": "eeb4c81798aaf018955eae65753e8534", + "entities": [ + { + "tableName": "connection_information", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT NOT NULL, `instanceBaseUrl` TEXT NOT NULL, `encryptedI` TEXT NOT NULL, `viaName` TEXT, `createdAt` TEXT NOT NULL, `isDirect` INTEGER NOT NULL, `updatedAt` TEXT NOT NULL, PRIMARY KEY(`accountId`, `encryptedI`, `instanceBaseUrl`), FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceBaseUrl", + "columnName": "instanceBaseUrl", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedI", + "columnName": "encryptedI", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "viaName", + "columnName": "viaName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isDirect", + "columnName": "isDirect", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "encryptedI", + "instanceBaseUrl" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "CASCADE", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "reaction_history", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `accountId` INTEGER, `target_post_id` TEXT, `target_user_id` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT)", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "targetPostId", + "columnName": "target_post_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "targetUserId", + "columnName": "target_user_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "Account", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "reaction_user_setting", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`reaction` TEXT NOT NULL, `instance_domain` TEXT NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`reaction`, `instance_domain`))", + "fields": [ + { + "fieldPath": "reaction", + "columnName": "reaction", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instance_domain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "reaction", + "instance_domain" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "page", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` TEXT, `title` TEXT NOT NULL, `pageNumber` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT, `global_timeline_with_files` INTEGER, `global_timeline_type` TEXT, `local_timeline_with_files` INTEGER, `local_timeline_exclude_nsfw` INTEGER, `local_timeline_type` TEXT, `hybrid_timeline_withFiles` INTEGER, `hybrid_timeline_includeLocalRenotes` INTEGER, `hybrid_timeline_includeMyRenotes` INTEGER, `hybrid_timeline_includeRenotedMyRenotes` INTEGER, `hybrid_timeline_type` TEXT, `home_timeline_withFiles` INTEGER, `home_timeline_includeLocalRenotes` INTEGER, `home_timeline_includeMyRenotes` INTEGER, `home_timeline_includeRenotedMyRenotes` INTEGER, `home_timeline_type` TEXT, `user_list_timeline_listId` TEXT, `user_list_timeline_withFiles` INTEGER, `user_list_timeline_includeLocalRenotes` INTEGER, `user_list_timeline_includeMyRenotes` INTEGER, `user_list_timeline_includeRenotedMyRenotes` INTEGER, `user_list_timeline_type` TEXT, `mention_following` INTEGER, `mention_visibility` TEXT, `mention_type` TEXT, `show_noteId` TEXT, `show_type` TEXT, `tag_tag` TEXT, `tag_reply` INTEGER, `tag_renote` INTEGER, `tag_withFiles` INTEGER, `tag_poll` INTEGER, `tag_type` TEXT, `featured_offset` INTEGER, `featured_type` TEXT, `notification_following` INTEGER, `notification_markAsRead` INTEGER, `notification_type` TEXT, `user_userId` TEXT, `user_includeReplies` INTEGER, `user_includeMyRenotes` INTEGER, `user_withFiles` INTEGER, `user_type` TEXT, `search_query` TEXT, `search_host` TEXT, `search_userId` TEXT, `search_type` TEXT, `favorite_type` TEXT, `antenna_antennaId` TEXT, `antenna_type` TEXT, FOREIGN KEY(`accountId`) REFERENCES `Account`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageNumber", + "columnName": "pageNumber", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.withFiles", + "columnName": "global_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "globalTimeline.type", + "columnName": "global_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localTimeline.withFiles", + "columnName": "local_timeline_with_files", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.excludeNsfw", + "columnName": "local_timeline_exclude_nsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localTimeline.type", + "columnName": "local_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.withFiles", + "columnName": "hybrid_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeLocalRenotes", + "columnName": "hybrid_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeMyRenotes", + "columnName": "hybrid_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.includeRenotedMyRenotes", + "columnName": "hybrid_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hybridTimeline.type", + "columnName": "hybrid_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "homeTimeline.withFiles", + "columnName": "home_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeLocalRenotes", + "columnName": "home_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeMyRenotes", + "columnName": "home_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.includeRenotedMyRenotes", + "columnName": "home_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "homeTimeline.type", + "columnName": "home_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.listId", + "columnName": "user_list_timeline_listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userListTimeline.withFiles", + "columnName": "user_list_timeline_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeLocalRenotes", + "columnName": "user_list_timeline_includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeMyRenotes", + "columnName": "user_list_timeline_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.includeRenotedMyRenotes", + "columnName": "user_list_timeline_includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userListTimeline.type", + "columnName": "user_list_timeline_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.following", + "columnName": "mention_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "mention.visibility", + "columnName": "mention_visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mention.type", + "columnName": "mention_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.noteId", + "columnName": "show_noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "show.type", + "columnName": "show_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.tag", + "columnName": "tag_tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "searchByTag.reply", + "columnName": "tag_reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.renote", + "columnName": "tag_renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.withFiles", + "columnName": "tag_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.poll", + "columnName": "tag_poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "searchByTag.type", + "columnName": "tag_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "featured.offset", + "columnName": "featured_offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "featured.type", + "columnName": "featured_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notification.following", + "columnName": "notification_following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.markAsRead", + "columnName": "notification_markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "notification.type", + "columnName": "notification_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.userId", + "columnName": "user_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeReplies", + "columnName": "user_includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.includeMyRenotes", + "columnName": "user_includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.withFiles", + "columnName": "user_withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userTimeline.type", + "columnName": "user_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.query", + "columnName": "search_query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.host", + "columnName": "search_host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.userId", + "columnName": "search_userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "search.type", + "columnName": "search_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "favorite.type", + "columnName": "favorite_type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.antennaId", + "columnName": "antenna_antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "antenna.type", + "columnName": "antenna_type", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_page_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "Account", + "onDelete": "NO ACTION", + "onUpdate": "NO ACTION", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "poll_choice_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`choice` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, `weight` INTEGER NOT NULL, PRIMARY KEY(`choice`, `weight`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "choice", + "columnName": "choice", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "choice", + "weight", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_poll_choice_table_draft_note_id_choice", + "unique": false, + "columnNames": [ + "draft_note_id", + "choice" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_poll_choice_table_draft_note_id_choice` ON `${TABLE_NAME}` (`draft_note_id`, `choice`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "user_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userId` TEXT NOT NULL, `draft_note_id` INTEGER NOT NULL, PRIMARY KEY(`userId`, `draft_note_id`), FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_user_id_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_id_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_file_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL DEFAULT 'name none', `remote_file_id` TEXT, `file_path` TEXT, `is_sensitive` INTEGER, `type` TEXT, `thumbnailUrl` TEXT, `draft_note_id` INTEGER NOT NULL, `folder_id` TEXT, `file_id` INTEGER PRIMARY KEY AUTOINCREMENT, FOREIGN KEY(`draft_note_id`) REFERENCES `draft_note_table`(`draft_note_id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'name none'" + }, + { + "fieldPath": "remoteFileId", + "columnName": "remote_file_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileId", + "columnName": "file_id", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "file_id" + ] + }, + "indices": [ + { + "name": "index_draft_file_table_draft_note_id", + "unique": false, + "columnNames": [ + "draft_note_id" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_table_draft_note_id` ON `${TABLE_NAME}` (`draft_note_id`)" + } + ], + "foreignKeys": [ + { + "table": "draft_note_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "draft_note_id" + ], + "referencedColumns": [ + "draft_note_id" + ] + } + ] + }, + { + "tableName": "draft_note_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `visibility` TEXT NOT NULL, `text` TEXT, `cw` TEXT, `viaMobile` INTEGER, `localOnly` INTEGER, `noExtractMentions` INTEGER, `noExtractHashtags` INTEGER, `noExtractEmojis` INTEGER, `replyId` TEXT, `renoteId` TEXT, `channelId` TEXT, `scheduleWillPostAt` TEXT, `draft_note_id` INTEGER PRIMARY KEY AUTOINCREMENT, `isSensitive` INTEGER, `multiple` INTEGER, `expiresAt` INTEGER, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "text", + "columnName": "text", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cw", + "columnName": "cw", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "viaMobile", + "columnName": "viaMobile", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localOnly", + "columnName": "localOnly", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractMentions", + "columnName": "noExtractMentions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractHashtags", + "columnName": "noExtractHashtags", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "noExtractEmojis", + "columnName": "noExtractEmojis", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "replyId", + "columnName": "replyId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "renoteId", + "columnName": "renoteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "scheduleWillPostAt", + "columnName": "scheduleWillPostAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "draftNoteId", + "columnName": "draft_note_id", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.multiple", + "columnName": "multiple", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "poll.expiresAt", + "columnName": "expiresAt", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "draft_note_id" + ] + }, + "indices": [ + { + "name": "index_draft_note_table_accountId_text", + "unique": false, + "columnNames": [ + "accountId", + "text" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_note_table_accountId_text` ON `${TABLE_NAME}` (`accountId`, `text`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "url_preview", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`url` TEXT NOT NULL, `title` TEXT NOT NULL, `icon` TEXT, `description` TEXT, `thumbnail` TEXT, `siteName` TEXT, PRIMARY KEY(`url`))", + "fields": [ + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "icon", + "columnName": "icon", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "thumbnail", + "columnName": "thumbnail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "siteName", + "columnName": "siteName", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "url" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "account_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`remoteId` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `userName` TEXT NOT NULL, `encryptedToken` TEXT NOT NULL, `instanceType` TEXT NOT NULL DEFAULT 'misskey', `accountId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "remoteId", + "columnName": "remoteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "encryptedToken", + "columnName": "encryptedToken", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceType", + "columnName": "instanceType", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "'misskey'" + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "accountId" + ] + }, + "indices": [ + { + "name": "index_account_table_remoteId", + "unique": false, + "columnNames": [ + "remoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_remoteId` ON `${TABLE_NAME}` (`remoteId`)" + }, + { + "name": "index_account_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_account_table_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_account_table_userName` ON `${TABLE_NAME}` (`userName`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "page_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `title` TEXT NOT NULL, `weight` INTEGER NOT NULL, `isSavePagePosition` INTEGER, `attachedAccountId` INTEGER, `pageId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `type` TEXT NOT NULL, `withFiles` INTEGER, `excludeNsfw` INTEGER, `includeLocalRenotes` INTEGER, `includeMyRenotes` INTEGER, `includeRenotedMyRenotes` INTEGER, `listId` TEXT, `following` INTEGER, `visibility` TEXT, `noteId` TEXT, `tag` TEXT, `reply` INTEGER, `renote` INTEGER, `poll` INTEGER, `offset` INTEGER, `markAsRead` INTEGER, `userId` TEXT, `includeReplies` INTEGER, `query` TEXT, `host` TEXT, `antennaId` TEXT, `channelId` TEXT, `clipId` TEXT)", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isSavePagePosition", + "columnName": "isSavePagePosition", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "attachedAccountId", + "columnName": "attachedAccountId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageId", + "columnName": "pageId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "pageParams.type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "pageParams.withFiles", + "columnName": "withFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.excludeNsfw", + "columnName": "excludeNsfw", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeLocalRenotes", + "columnName": "includeLocalRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeMyRenotes", + "columnName": "includeMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.includeRenotedMyRenotes", + "columnName": "includeRenotedMyRenotes", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.listId", + "columnName": "listId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.following", + "columnName": "following", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.visibility", + "columnName": "visibility", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.tag", + "columnName": "tag", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.reply", + "columnName": "reply", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.renote", + "columnName": "renote", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.poll", + "columnName": "poll", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.offset", + "columnName": "offset", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.markAsRead", + "columnName": "markAsRead", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.includeReplies", + "columnName": "includeReplies", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "pageParams.query", + "columnName": "query", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.antennaId", + "columnName": "antennaId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.channelId", + "columnName": "channelId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "pageParams.clipId", + "columnName": "clipId", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "pageId" + ] + }, + "indices": [ + { + "name": "index_page_table_weight", + "unique": false, + "columnNames": [ + "weight" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_weight` ON `${TABLE_NAME}` (`weight`)" + }, + { + "name": "index_page_table_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_page_table_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "meta_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `bannerUrl` TEXT, `cacheRemoteFiles` INTEGER, `description` TEXT, `disableGlobalTimeline` INTEGER, `disableLocalTimeline` INTEGER, `disableRegistration` INTEGER, `driveCapacityPerLocalUserMb` INTEGER, `driveCapacityPerRemoteUserMb` INTEGER, `enableDiscordIntegration` INTEGER, `enableEmail` INTEGER, `enableEmojiReaction` INTEGER, `enableGithubIntegration` INTEGER, `enableRecaptcha` INTEGER, `enableServiceWorker` INTEGER, `enableTwitterIntegration` INTEGER, `errorImageUrl` TEXT, `feedbackUrl` TEXT, `iconUrl` TEXT, `maintainerEmail` TEXT, `maintainerName` TEXT, `mascotImageUrl` TEXT, `maxNoteTextLength` INTEGER, `name` TEXT, `recaptchaSiteKey` TEXT, `secure` INTEGER, `swPublicKey` TEXT, `toSUrl` TEXT, `version` TEXT NOT NULL, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "cacheRemoteFiles", + "columnName": "cacheRemoteFiles", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "disableGlobalTimeline", + "columnName": "disableGlobalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableLocalTimeline", + "columnName": "disableLocalTimeline", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "disableRegistration", + "columnName": "disableRegistration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerLocalUserMb", + "columnName": "driveCapacityPerLocalUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "driveCapacityPerRemoteUserMb", + "columnName": "driveCapacityPerRemoteUserMb", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableDiscordIntegration", + "columnName": "enableDiscordIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmail", + "columnName": "enableEmail", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableEmojiReaction", + "columnName": "enableEmojiReaction", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableGithubIntegration", + "columnName": "enableGithubIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableRecaptcha", + "columnName": "enableRecaptcha", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableServiceWorker", + "columnName": "enableServiceWorker", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "enableTwitterIntegration", + "columnName": "enableTwitterIntegration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "errorImageUrl", + "columnName": "errorImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "feedbackUrl", + "columnName": "feedbackUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerEmail", + "columnName": "maintainerEmail", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maintainerName", + "columnName": "maintainerName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "mascotImageUrl", + "columnName": "mascotImageUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "maxNoteTextLength", + "columnName": "maxNoteTextLength", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "recaptchaSiteKey", + "columnName": "recaptchaSiteKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "secure", + "columnName": "secure", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "swPublicKey", + "columnName": "swPublicKey", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "toSUrl", + "columnName": "toSUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "emoji_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, `host` TEXT, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` TEXT, PRIMARY KEY(`name`, `instanceDomain`), FOREIGN KEY(`instanceDomain`) REFERENCES `meta_table`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_table_instanceDomain", + "unique": false, + "columnNames": [ + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_instanceDomain` ON `${TABLE_NAME}` (`instanceDomain`)" + }, + { + "name": "index_emoji_table_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_table_name` ON `${TABLE_NAME}` (`name`)" + } + ], + "foreignKeys": [ + { + "table": "meta_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "instanceDomain" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "emoji_alias_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`alias` TEXT NOT NULL, `name` TEXT NOT NULL, `instanceDomain` TEXT NOT NULL, PRIMARY KEY(`alias`, `name`, `instanceDomain`), FOREIGN KEY(`name`, `instanceDomain`) REFERENCES `emoji_table`(`name`, `instanceDomain`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "alias", + "columnName": "alias", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "instanceDomain", + "columnName": "instanceDomain", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "alias", + "name", + "instanceDomain" + ] + }, + "indices": [ + { + "name": "index_emoji_alias_table_name_instanceDomain", + "unique": false, + "columnNames": [ + "name", + "instanceDomain" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_emoji_alias_table_name_instanceDomain` ON `${TABLE_NAME}` (`name`, `instanceDomain`)" + } + ], + "foreignKeys": [ + { + "table": "emoji_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "name", + "instanceDomain" + ], + "referencedColumns": [ + "name", + "instanceDomain" + ] + } + ] + }, + { + "tableName": "unread_notifications_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, PRIMARY KEY(`accountId`, `notificationId`), FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "nicknames", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`nickname` TEXT NOT NULL, `username` TEXT NOT NULL, `host` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "nickname", + "columnName": "nickname", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "username", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_nicknames_username_host", + "unique": true, + "columnNames": [ + "username", + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_nicknames_username_host` ON `${TABLE_NAME}` (`username`, `host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "utf8_emojis_by_amio", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`codes` TEXT NOT NULL, `name` TEXT NOT NULL, `char` TEXT NOT NULL, PRIMARY KEY(`codes`))", + "fields": [ + { + "fieldPath": "codes", + "columnName": "codes", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "charCode", + "columnName": "char", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "codes" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "drive_file_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `relatedAccountId` INTEGER NOT NULL, `createdAt` TEXT, `name` TEXT NOT NULL, `type` TEXT NOT NULL, `md5` TEXT, `size` INTEGER, `url` TEXT NOT NULL, `isSensitive` INTEGER NOT NULL, `thumbnailUrl` TEXT, `folderId` TEXT, `userId` TEXT, `comment` TEXT, `blurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`relatedAccountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "relatedAccountId", + "columnName": "relatedAccountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "md5", + "columnName": "md5", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "size", + "columnName": "size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "isSensitive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folderId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "blurhash", + "columnName": "blurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_drive_file_v1_serverId_relatedAccountId", + "unique": true, + "columnNames": [ + "serverId", + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_drive_file_v1_serverId_relatedAccountId` ON `${TABLE_NAME}` (`serverId`, `relatedAccountId`)" + }, + { + "name": "index_drive_file_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_drive_file_v1_relatedAccountId", + "unique": false, + "columnNames": [ + "relatedAccountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_drive_file_v1_relatedAccountId` ON `${TABLE_NAME}` (`relatedAccountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "relatedAccountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "draft_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`draftNoteId` INTEGER NOT NULL, `filePropertyId` INTEGER, `localFileId` INTEGER, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`filePropertyId`) REFERENCES `drive_file_v1`(`id`) ON UPDATE NO ACTION ON DELETE SET NULL , FOREIGN KEY(`localFileId`) REFERENCES `draft_local_file_v2_table`(`localFileId`) ON UPDATE NO ACTION ON DELETE SET NULL )", + "fields": [ + { + "fieldPath": "draftNoteId", + "columnName": "draftNoteId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filePropertyId", + "columnName": "filePropertyId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId", + "unique": true, + "columnNames": [ + "draftNoteId", + "filePropertyId", + "localFileId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId_filePropertyId_localFileId` ON `${TABLE_NAME}` (`draftNoteId`, `filePropertyId`, `localFileId`)" + }, + { + "name": "index_draft_file_v2_table_draftNoteId", + "unique": false, + "columnNames": [ + "draftNoteId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_draftNoteId` ON `${TABLE_NAME}` (`draftNoteId`)" + }, + { + "name": "index_draft_file_v2_table_localFileId", + "unique": false, + "columnNames": [ + "localFileId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_localFileId` ON `${TABLE_NAME}` (`localFileId`)" + }, + { + "name": "index_draft_file_v2_table_filePropertyId", + "unique": false, + "columnNames": [ + "filePropertyId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_draft_file_v2_table_filePropertyId` ON `${TABLE_NAME}` (`filePropertyId`)" + } + ], + "foreignKeys": [ + { + "table": "drive_file_v1", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "filePropertyId" + ], + "referencedColumns": [ + "id" + ] + }, + { + "table": "draft_local_file_v2_table", + "onDelete": "SET NULL", + "onUpdate": "NO ACTION", + "columns": [ + "localFileId" + ], + "referencedColumns": [ + "localFileId" + ] + } + ] + }, + { + "tableName": "draft_local_file_v2_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `file_path` TEXT NOT NULL, `is_sensitive` INTEGER, `type` TEXT NOT NULL, `thumbnailUrl` TEXT, `folder_id` TEXT, `file_size` INTEGER, `comment` TEXT, `localFileId` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "filePath", + "columnName": "file_path", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSensitive", + "columnName": "is_sensitive", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "thumbnailUrl", + "columnName": "thumbnailUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "folderId", + "columnName": "folder_id", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "fileSize", + "columnName": "file_size", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "comment", + "columnName": "comment", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "localFileId", + "columnName": "localFileId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "localFileId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "group_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `ownerId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "ownerId", + "columnName": "ownerId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_v1_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_group_v1_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_v1_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_group_v1_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_v1_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "group_member_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`groupId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`groupId`) REFERENCES `group_v1`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "groupId", + "columnName": "groupId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_group_member_v1_groupId", + "unique": false, + "columnNames": [ + "groupId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_group_member_v1_groupId` ON `${TABLE_NAME}` (`groupId`)" + }, + { + "name": "index_group_member_v1_groupId_userId", + "unique": true, + "columnNames": [ + "groupId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_group_member_v1_groupId_userId` ON `${TABLE_NAME}` (`groupId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "group_v1", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "groupId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `userName` TEXT NOT NULL, `name` TEXT, `avatarUrl` TEXT, `isCat` INTEGER, `isBot` INTEGER, `host` TEXT NOT NULL, `isSameHost` INTEGER NOT NULL, `avatarBlurhash` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userName", + "columnName": "userName", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "avatarUrl", + "columnName": "avatarUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isCat", + "columnName": "isCat", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "isBot", + "columnName": "isBot", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isSameHost", + "columnName": "isSameHost", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "avatarBlurhash", + "columnName": "avatarBlurhash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_serverId_accountId", + "unique": true, + "columnNames": [ + "serverId", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_serverId_accountId` ON `${TABLE_NAME}` (`serverId`, `accountId`)" + }, + { + "name": "index_user_userName", + "unique": false, + "columnNames": [ + "userName" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_userName` ON `${TABLE_NAME}` (`userName`)" + }, + { + "name": "index_user_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_host", + "unique": false, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_detailed_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_detailed_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_detailed_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_emoji", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `userId` INTEGER NOT NULL, `aspectRatio` REAL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "aspectRatio", + "columnName": "aspectRatio", + "affinity": "REAL", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_emoji_name_userId", + "unique": true, + "columnNames": [ + "name", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_emoji_name_userId` ON `${TABLE_NAME}` (`name`, `userId`)" + }, + { + "name": "index_user_emoji_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_emoji_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "pinned_note_id", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`noteId` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "noteId", + "columnName": "noteId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_pinned_note_id_noteId_userId", + "unique": true, + "columnNames": [ + "noteId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_pinned_note_id_noteId_userId` ON `${TABLE_NAME}` (`noteId`, `userId`)" + }, + { + "name": "index_pinned_note_id_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pinned_note_id_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`faviconUrl` TEXT, `iconUrl` TEXT, `name` TEXT, `softwareName` TEXT, `softwareVersion` TEXT, `themeColor` TEXT, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "faviconUrl", + "columnName": "faviconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareName", + "columnName": "softwareName", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "softwareVersion", + "columnName": "softwareVersion", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_instance_info_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_instance_info_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_profile_field", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `value` TEXT NOT NULL, `userId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_profile_field_userId", + "unique": false, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_profile_field_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "word_filter_regex_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`pattern` TEXT NOT NULL, `parentId` INTEGER NOT NULL, PRIMARY KEY(`parentId`), FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "pattern", + "columnName": "pattern", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "parentId" + ] + }, + "indices": [ + { + "name": "index_word_filter_regex_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_regex_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "word_filter_word_condition", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`word` TEXT NOT NULL, `parentId` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`parentId`) REFERENCES `word_filter_condition`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "word", + "columnName": "word", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "parentId", + "columnName": "parentId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_word_filter_word_condition_parentId", + "unique": false, + "columnNames": [ + "parentId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_word_filter_word_condition_parentId` ON `${TABLE_NAME}` (`parentId`)" + } + ], + "foreignKeys": [ + { + "table": "word_filter_condition", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "parentId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_list", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT NOT NULL, `accountId` INTEGER NOT NULL, `createdAt` TEXT NOT NULL, `name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_accountId` ON `${TABLE_NAME}` (`accountId`)" + }, + { + "name": "index_user_list_serverId", + "unique": false, + "columnNames": [ + "serverId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_serverId` ON `${TABLE_NAME}` (`serverId`)" + }, + { + "name": "index_user_list_accountId_serverId", + "unique": true, + "columnNames": [ + "accountId", + "serverId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_accountId_serverId` ON `${TABLE_NAME}` (`accountId`, `serverId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_list_member", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`userListId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`userListId`) REFERENCES `user_list`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "userListId", + "columnName": "userListId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_user_list_member_userListId", + "unique": false, + "columnNames": [ + "userListId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_user_list_member_userListId` ON `${TABLE_NAME}` (`userListId`)" + }, + { + "name": "index_user_list_member_userListId_userId", + "unique": true, + "columnNames": [ + "userListId", + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_list_member_userListId_userId` ON `${TABLE_NAME}` (`userListId`, `userId`)" + } + ], + "foreignKeys": [ + { + "table": "user_list", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userListId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "instance_info_v1_table", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `host` TEXT NOT NULL, `name` TEXT, `description` TEXT, `clientMaxBodyByteSize` INTEGER, `iconUrl` TEXT, `themeColor` TEXT, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "clientMaxBodyByteSize", + "columnName": "clientMaxBodyByteSize", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "iconUrl", + "columnName": "iconUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "themeColor", + "columnName": "themeColor", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_instance_info_v1_table_host", + "unique": true, + "columnNames": [ + "host" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_instance_info_v1_table_host` ON `${TABLE_NAME}` (`host`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "search_histories", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `keyword` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, FOREIGN KEY(`accountId`) REFERENCES `account_table`(`accountId`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "keyword", + "columnName": "keyword", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_search_histories_keyword_accountId", + "unique": true, + "columnNames": [ + "keyword", + "accountId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_search_histories_keyword_accountId` ON `${TABLE_NAME}` (`keyword`, `accountId`)" + }, + { + "name": "index_search_histories_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_search_histories_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [ + { + "table": "account_table", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "accountId" + ], + "referencedColumns": [ + "accountId" + ] + } + ] + }, + { + "tableName": "user_info_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`description` TEXT, `followersCount` INTEGER, `followingCount` INTEGER, `hostLower` TEXT, `notesCount` INTEGER, `bannerUrl` TEXT, `url` TEXT, `isLocked` INTEGER NOT NULL, `birthday` TEXT, `createdAt` TEXT, `updatedAt` TEXT, `publicReactions` INTEGER, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "followersCount", + "columnName": "followersCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "followingCount", + "columnName": "followingCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "hostLower", + "columnName": "hostLower", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "notesCount", + "columnName": "notesCount", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "bannerUrl", + "columnName": "bannerUrl", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "isLocked", + "columnName": "isLocked", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "birthday", + "columnName": "birthday", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "updatedAt", + "columnName": "updatedAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "publicReactions", + "columnName": "publicReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_info_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_info_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "user_related_state", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`isFollowing` INTEGER NOT NULL, `isFollower` INTEGER NOT NULL, `isBlocking` INTEGER NOT NULL, `isMuting` INTEGER NOT NULL, `hasPendingFollowRequestFromYou` INTEGER NOT NULL, `hasPendingFollowRequestToYou` INTEGER NOT NULL, `userId` INTEGER NOT NULL, PRIMARY KEY(`userId`), FOREIGN KEY(`userId`) REFERENCES `user`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "isFollowing", + "columnName": "isFollowing", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isFollower", + "columnName": "isFollower", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isBlocking", + "columnName": "isBlocking", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isMuting", + "columnName": "isMuting", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestFromYou", + "columnName": "hasPendingFollowRequestFromYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "hasPendingFollowRequestToYou", + "columnName": "hasPendingFollowRequestToYou", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId" + ] + }, + "indices": [ + { + "name": "index_user_related_state_userId", + "unique": true, + "columnNames": [ + "userId" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_user_related_state_userId` ON `${TABLE_NAME}` (`userId`)" + } + ], + "foreignKeys": [ + { + "table": "user", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "userId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "nodeinfo", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`host` TEXT NOT NULL, `nodeInfoVersion` TEXT NOT NULL, `name` TEXT NOT NULL, `version` TEXT NOT NULL, PRIMARY KEY(`host`))", + "fields": [ + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "nodeInfoVersion", + "columnName": "nodeInfoVersion", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "host" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_info", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uri` TEXT NOT NULL, `title` TEXT NOT NULL, `description` TEXT NOT NULL, `email` TEXT NOT NULL, `version` TEXT NOT NULL, `urls_streamingApi` TEXT, `configuration_statuses_maxCharacters` INTEGER, `configuration_statuses_maxMediaAttachments` INTEGER, `configuration_polls_maxOptions` INTEGER, `configuration_polls_maxCharactersPerOption` INTEGER, `configuration_polls_minExpiration` INTEGER, `configuration_polls_maxExpiration` INTEGER, `configuration_emoji_reactions_myReactions` INTEGER, `configuration_emoji_reactions_maxReactionsPerAccount` INTEGER, PRIMARY KEY(`uri`))", + "fields": [ + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "title", + "columnName": "title", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "description", + "columnName": "description", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "email", + "columnName": "email", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "version", + "columnName": "version", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "urls.streamingApi", + "columnName": "urls_streamingApi", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxCharacters", + "columnName": "configuration_statuses_maxCharacters", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.statuses.maxMediaAttachments", + "columnName": "configuration_statuses_maxMediaAttachments", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxOptions", + "columnName": "configuration_polls_maxOptions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxCharactersPerOption", + "columnName": "configuration_polls_maxCharactersPerOption", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.minExpiration", + "columnName": "configuration_polls_minExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.polls.maxExpiration", + "columnName": "configuration_polls_maxExpiration", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactions", + "columnName": "configuration_emoji_reactions_myReactions", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "configuration.emojiReactions.maxReactionsPerAccount", + "columnName": "configuration_emoji_reactions_maxReactionsPerAccount", + "affinity": "INTEGER", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "custom_emojis", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`serverId` TEXT, `name` TEXT NOT NULL, `emojiHost` TEXT NOT NULL, `url` TEXT, `uri` TEXT, `type` TEXT, `category` TEXT, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "serverId", + "columnName": "serverId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "emojiHost", + "columnName": "emojiHost", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "url", + "columnName": "url", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "category", + "columnName": "category", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [ + { + "name": "index_custom_emojis_name", + "unique": false, + "columnNames": [ + "name" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_name` ON `${TABLE_NAME}` (`name`)" + }, + { + "name": "index_custom_emojis_emojiHost", + "unique": false, + "columnNames": [ + "emojiHost" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost` ON `${TABLE_NAME}` (`emojiHost`)" + }, + { + "name": "index_custom_emojis_category", + "unique": false, + "columnNames": [ + "category" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emojis_category` ON `${TABLE_NAME}` (`category`)" + }, + { + "name": "index_custom_emojis_emojiHost_name", + "unique": true, + "columnNames": [ + "emojiHost", + "name" + ], + "orders": [], + "createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_custom_emojis_emojiHost_name` ON `${TABLE_NAME}` (`emojiHost`, `name`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "custom_emoji_aliases", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`emojiId` INTEGER NOT NULL, `value` TEXT NOT NULL, PRIMARY KEY(`emojiId`, `value`), FOREIGN KEY(`emojiId`) REFERENCES `custom_emojis`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "emojiId", + "columnName": "emojiId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "emojiId", + "value" + ] + }, + "indices": [ + { + "name": "index_custom_emoji_aliases_emojiId_value", + "unique": false, + "columnNames": [ + "emojiId", + "value" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_custom_emoji_aliases_emojiId_value` ON `${TABLE_NAME}` (`emojiId`, `value`)" + } + ], + "foreignKeys": [ + { + "table": "custom_emojis", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "emojiId" + ], + "referencedColumns": [ + "id" + ] + } + ] + }, + { + "tableName": "notification_json_cache_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `notificationId` TEXT NOT NULL, `json` TEXT NOT NULL, `key` TEXT, `weight` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `notificationId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "notificationId", + "columnName": "notificationId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "json", + "columnName": "json", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "weight", + "columnName": "weight", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "notificationId" + ] + }, + "indices": [ + { + "name": "index_notification_json_cache_v1_key", + "unique": false, + "columnNames": [ + "key" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_notification_json_cache_v1_key` ON `${TABLE_NAME}` (`key`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_word_filters_v1", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `filterId` TEXT NOT NULL, `phrase` TEXT NOT NULL, `wholeWord` INTEGER NOT NULL, `expiresAt` TEXT, `irreversible` INTEGER NOT NULL, `isContextHome` INTEGER NOT NULL, `isContextNotifications` INTEGER NOT NULL, `isContextPublic` INTEGER NOT NULL, `isContextThread` INTEGER NOT NULL, `isContextAccount` INTEGER NOT NULL, PRIMARY KEY(`accountId`, `filterId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "filterId", + "columnName": "filterId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "phrase", + "columnName": "phrase", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "wholeWord", + "columnName": "wholeWord", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "expiresAt", + "columnName": "expiresAt", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "irreversible", + "columnName": "irreversible", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextHome", + "columnName": "isContextHome", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextNotifications", + "columnName": "isContextNotifications", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextPublic", + "columnName": "isContextPublic", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextThread", + "columnName": "isContextThread", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "isContextAccount", + "columnName": "isContextAccount", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "accountId", + "filterId" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "renote_mute_users", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`accountId` INTEGER NOT NULL, `userId` TEXT NOT NULL, `createdAt` TEXT NOT NULL, `postedAt` TEXT, PRIMARY KEY(`userId`, `accountId`))", + "fields": [ + { + "fieldPath": "accountId", + "columnName": "accountId", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userId", + "columnName": "userId", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "postedAt", + "columnName": "postedAt", + "affinity": "TEXT", + "notNull": false + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "userId", + "accountId" + ] + }, + "indices": [ + { + "name": "index_renote_mute_users_postedAt", + "unique": false, + "columnNames": [ + "postedAt" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_postedAt` ON `${TABLE_NAME}` (`postedAt`)" + }, + { + "name": "index_renote_mute_users_accountId", + "unique": false, + "columnNames": [ + "accountId" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_renote_mute_users_accountId` ON `${TABLE_NAME}` (`accountId`)" + } + ], + "foreignKeys": [] + }, + { + "tableName": "mastodon_instance_fedibird_capabilities", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_mastodon_instance_fedibird_capabilities_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_mastodon_instance_fedibird_capabilities_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + }, + { + "tableName": "pleroma_metadata_features", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`type` TEXT NOT NULL, `uri` TEXT NOT NULL, PRIMARY KEY(`uri`, `type`), FOREIGN KEY(`uri`) REFERENCES `mastodon_instance_info`(`uri`) ON UPDATE CASCADE ON DELETE CASCADE )", + "fields": [ + { + "fieldPath": "type", + "columnName": "type", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "uri", + "columnName": "uri", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "uri", + "type" + ] + }, + "indices": [ + { + "name": "index_pleroma_metadata_features_uri", + "unique": false, + "columnNames": [ + "uri" + ], + "orders": [], + "createSql": "CREATE INDEX IF NOT EXISTS `index_pleroma_metadata_features_uri` ON `${TABLE_NAME}` (`uri`)" + } + ], + "foreignKeys": [ + { + "table": "mastodon_instance_info", + "onDelete": "CASCADE", + "onUpdate": "CASCADE", + "columns": [ + "uri" + ], + "referencedColumns": [ + "uri" + ] + } + ] + } + ], + "views": [ + { + "viewName": "user_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select user.*, nicknames.nickname from user left join nicknames on user.userName = nicknames.username and user.host = nicknames.host" + }, + { + "viewName": "group_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.groupId, u.id as userId, u.avatarUrl, u.serverId from group_member_v1 as m \n inner join group_v1 as g\n inner join user as u\n on m.groupId = g.id\n and m.userId = u.serverId\n and g.accountId = u.accountId" + }, + { + "viewName": "user_list_member_view", + "createSql": "CREATE VIEW `${VIEW_NAME}` AS select m.userListId, u.id as userId, u.avatarUrl, u.serverId from user_list_member as m \n inner join user_list as ul\n inner join user as u\n on m.userListId = ul.id\n and m.userId = u.serverId\n and ul.accountId = u.accountId" + } + ], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'eeb4c81798aaf018955eae65753e8534')" + ] + } +} \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt index bfd31978c2..d04ddf5dea 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/DataBase.kt @@ -117,7 +117,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut PleromaMetadataFeatures::class, ], - version = 47, + version = 48, exportSchema = true, autoMigrations = [ AutoMigration(from = 11, to = 12), @@ -156,6 +156,7 @@ import net.pantasystem.milktea.data.infrastructure.user.renote.mute.db.RenoteMut AutoMigration(from = 44, to = 45), AutoMigration(from = 45, to = 46), AutoMigration(from = 46, to = 47), + AutoMigration(from = 47, to = 48), ], views = [UserView::class, GroupMemberView::class, UserListMemberView::class] ) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt index 57058963c5..6f9bb30691 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/account/page/db/PageRecord.kt @@ -22,6 +22,9 @@ data class PageRecord( @ColumnInfo(name = "isSavePagePosition") val isSavePagePosition: Boolean? = false, + @ColumnInfo(name = "attachedAccountId") + val attachedAccountId: Long? = null, + @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "pageId") var pageId: Long @@ -35,6 +38,7 @@ data class PageRecord( weight = page.weight, pageParams = PageRecordParams.from(page.pageParams), isSavePagePosition = page.isSavePagePosition, + attachedAccountId = page.attachedAccountId, pageId = page.pageId ) } @@ -47,6 +51,7 @@ data class PageRecord( weight = weight, pageParams = pageParams.toParams(), pageId = pageId, + attachedAccountId = attachedAccountId, isSavePagePosition = isSavePagePosition ?: false ) } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Page.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Page.kt index f27f5d85f4..9702286a88 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Page.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Page.kt @@ -12,11 +12,27 @@ data class Page( var weight: Int, val pageParams: PageParams, val isSavePagePosition: Boolean, - var pageId: Long + val attachedAccountId: Long?, + var pageId: Long, ) : Serializable, Parcelable { - constructor(accountId: Long, title: String, weight: Int, pageable: Pageable, isSavePagePosition: Boolean = false, pageId: Long = 0) - : this(accountId, title, weight, pageable.toParams(), isSavePagePosition, pageId) + constructor( + accountId: Long, + title: String, + weight: Int, + pageable: Pageable, + isSavePagePosition: Boolean = false, + attachedAccountId: Long? = null, + pageId: Long = 0, + ) : this( + accountId, + title, + weight, + pageable.toParams(), + isSavePagePosition, + attachedAccountId, + pageId + ) fun pageable(): Pageable { From c53ae2c82d24e825232cd6ae0c1a75757e75ac9f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 17:45:27 +0900 Subject: [PATCH 249/432] =?UTF-8?q?feat:=20=E9=96=A2=E9=80=A3=E4=BB=98?= =?UTF-8?q?=E3=81=91=E3=81=9FAccountId=E3=82=92=E5=84=AA=E5=85=88=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/note/timeline/TimelineFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt index 0630a36d93..5e35764903 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt @@ -79,7 +79,7 @@ class TimelineFragment : Fragment(R.layout.fragment_swipe_refresh_recycler_view) private val mViewModel: TimelineViewModel by viewModels { TimelineViewModel.provideViewModel( timelineViewModelFactory, - accountId = (mPage?.accountId ?: accountId)?.let { + accountId = (mPage?.attachedAccountId?: mPage?.accountId ?: accountId)?.let { AccountId(it) }, pageId = mPage?.pageId?.let { From 8f71190a3f5bb33ab6ececcbf65ea68a9a941aa0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 17:54:02 +0900 Subject: [PATCH 250/432] =?UTF-8?q?feat:=20=E4=BB=96=E3=82=A2=E3=82=AB?= =?UTF-8?q?=E3=82=A6=E3=83=B3=E3=83=88=E3=81=A8=E9=96=A2=E9=80=A3=E3=81=A5?= =?UTF-8?q?=E3=81=91=E3=82=89=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E5=A0=B4?= =?UTF-8?q?=E5=90=88=E3=81=AFtitle=E3=81=AB=E3=81=9D=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E5=90=8D=E3=82=92=E5=90=AB?= =?UTF-8?q?=E3=82=81=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=80=81?= =?UTF-8?q?=E3=81=BE=E3=81=9FId=E3=82=92=E4=BF=9D=E6=8C=81=E3=81=95?= =?UTF-8?q?=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/activities/PageSettingActivity.kt | 2 +- .../viewmodel/page/PageSettingViewModel.kt | 78 +++++++++++-------- .../viewmodel/page/SelectPageTypeToAdd.kt | 4 +- 3 files changed, 49 insertions(+), 35 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt index 2263f496d1..726f6d2274 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt @@ -127,7 +127,7 @@ class PageSettingActivity : AppCompatActivity() { pageTypes = pageTypes, list = list, onSelectPage = { - mPageSettingViewModel.add(it.type) + mPageSettingViewModel.add(it) }, onOptionButtonClicked = { mPageSettingViewModel.pageOnActionEvent.event = it diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt index 74f8824f7c..4199e7a1ce 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt @@ -40,10 +40,6 @@ class PageSettingViewModel @Inject constructor( val pageOnUpdateEvent = EventBus() - val pageTypes = account.filterNotNull().map { - pageCandidateGenerator.createPageCandidates(it) - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) - val pageTypesGroupedByAccount = combine( accountStore.observeCurrentAccount, accountStore.observeAccounts, @@ -109,10 +105,18 @@ class PageSettingViewModel @Inject constructor( } - private fun addPage(page: Page) { + private fun addPage(page: Page, relatedAccount: Account?) { + val p = if (relatedAccount == null || page.accountId == relatedAccount.accountId) { + page + } else { + page.copy( + attachedAccountId = relatedAccount.accountId, + title = page.title + ("(@${relatedAccount.userName}@${relatedAccount.getHost()})") + ) + } val list = ArrayList(selectedPages.value) - page.weight = list.size - list.add(page) + p.weight = list.size + list.add(p) setList(list) } @@ -141,7 +145,7 @@ class PageSettingViewModel @Inject constructor( PageableTemplate(account.value!!) .user(user.id.id, title = user.displayName) } - addPage(page) + addPage(page, null) } fun addUsersGalleryByIds(userIds: List) { @@ -155,7 +159,9 @@ class PageSettingViewModel @Inject constructor( val name = if (settingStore.isUserNameDefault) user.shortDisplayName else user.displayName account.value!!.newPage(Pageable.Gallery.User(userId = user.id.id), name = name) - }.forEach(::addPage) + }.forEach { + addPage(it, null) + } } } } @@ -167,89 +173,99 @@ class PageSettingViewModel @Inject constructor( } - override fun add(type: PageType) { - pageAddedEvent.event = type - val name = pageTypeNameMap.get(type) - when (type) { + override fun add(type: PageCandidate) { + pageAddedEvent.event = type.type + val name = pageTypeNameMap.get(type.type) + when (type.type) { PageType.GLOBAL -> { - addPage(PageableTemplate(account.value!!).globalTimeline(name)) + addPage(PageableTemplate(account.value!!).globalTimeline(name), type.relatedAccount) } PageType.SOCIAL -> { - addPage(PageableTemplate(account.value!!).hybridTimeline(name)) + addPage(PageableTemplate(account.value!!).hybridTimeline(name), type.relatedAccount) } PageType.LOCAL -> { - addPage(PageableTemplate(account.value!!).localTimeline(name)) + addPage(PageableTemplate(account.value!!).localTimeline(name), type.relatedAccount) } PageType.HOME -> { - addPage(PageableTemplate(account.value!!).homeTimeline(name)) + addPage(PageableTemplate(account.value!!).homeTimeline(name), type.relatedAccount) } PageType.NOTIFICATION -> { - addPage(PageableTemplate(account.value!!).notification(name)) + addPage(PageableTemplate(account.value!!).notification(name), type.relatedAccount) } PageType.FAVORITE -> { - addPage(PageableTemplate(account.value!!).favorite(name)) + addPage(PageableTemplate(account.value!!).favorite(name), type.relatedAccount) } PageType.FEATURED -> { - addPage(PageableTemplate(account.value!!).featured(name)) + addPage(PageableTemplate(account.value!!).featured(name), type.relatedAccount) } PageType.MENTION -> { - addPage(PageableTemplate(account.value!!).mention(name)) + addPage(PageableTemplate(account.value!!).mention(name), type.relatedAccount) } PageType.GALLERY_FEATURED -> addPage( account.value!!.newPage( Pageable.Gallery.Featured, name - ) + ), + type.relatedAccount ) PageType.GALLERY_POPULAR -> addPage( account.value!!.newPage( Pageable.Gallery.Popular, name - ) + ), + type.relatedAccount ) PageType.GALLERY_POSTS -> addPage( account.value!!.newPage( Pageable.Gallery.Posts, name - ) + ), + type.relatedAccount ) PageType.MY_GALLERY_POSTS -> addPage( account.value!!.newPage( Pageable.Gallery.MyPosts, name - ) + ), + type.relatedAccount ) PageType.I_LIKED_GALLERY_POSTS -> addPage( account.value!!.newPage( Pageable.Gallery.ILikedPosts, name - ) + ), + type.relatedAccount ) PageType.MASTODON_HOME_TIMELINE -> addPage( account.value!!.newPage( Pageable.Mastodon.HomeTimeline, name, - ) + ), + type.relatedAccount ) PageType.MASTODON_LOCAL_TIMELINE -> addPage( account.value!!.newPage( Pageable.Mastodon.LocalTimeline(), name, - ) + ), + type.relatedAccount ) PageType.MASTODON_PUBLIC_TIMELINE -> addPage( account.value!!.newPage( Pageable.Mastodon.PublicTimeline(), name, - ) + ), + type.relatedAccount ) PageType.CALCKEY_RECOMMENDED_TIMELINE -> addPage( account.value!!.newPage( Pageable.CalckeyRecommendedTimeline, name, - ) + ), + type.relatedAccount ) PageType.MASTODON_BOOKMARK_TIMELINE -> addPage( account.value!!.newPage( Pageable.Mastodon.BookmarkTimeline, name, - ) + ), + type.relatedAccount ) else -> { Log.d("PageSettingViewModel", "管轄外な設定パターン:$type, name:$name") diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/SelectPageTypeToAdd.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/SelectPageTypeToAdd.kt index 6c6c390ff3..a33c46a8a8 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/SelectPageTypeToAdd.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/SelectPageTypeToAdd.kt @@ -1,8 +1,6 @@ package net.pantasystem.milktea.setting.viewmodel.page -import net.pantasystem.milktea.model.account.page.PageType - interface SelectPageTypeToAdd { - fun add(type: PageType) + fun add(type: PageCandidate) } \ No newline at end of file From 354d8c90d6d53e36491867824f7985f7e6d997d0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 17:54:05 +0900 Subject: [PATCH 251/432] =?UTF-8?q?feat:=20=E4=BB=96=E3=82=A2=E3=82=AB?= =?UTF-8?q?=E3=82=A6=E3=83=B3=E3=83=88=E3=81=A8=E9=96=A2=E9=80=A3=E3=81=A5?= =?UTF-8?q?=E3=81=91=E3=82=89=E3=82=8C=E3=81=A6=E3=81=84=E3=82=8B=E5=A0=B4?= =?UTF-8?q?=E5=90=88=E3=81=AFtitle=E3=81=AB=E3=81=9D=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E5=90=8D=E3=82=92=E5=90=AB?= =?UTF-8?q?=E3=82=81=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=80=81?= =?UTF-8?q?=E3=81=BE=E3=81=9FId=E3=82=92=E4=BF=9D=E6=8C=81=E3=81=95?= =?UTF-8?q?=E3=81=9B=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/setting/viewmodel/page/PageSettingViewModel.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt index 4199e7a1ce..0b13a1f1a9 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt @@ -6,7 +6,10 @@ import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll -import kotlinx.coroutines.flow.* +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.app_store.setting.SettingStore From d3cf70128a7e028bdb7911219b055d3940d16c9b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 18:07:14 +0900 Subject: [PATCH 252/432] =?UTF-8?q?feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=81=AB=E5=AF=BE=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=82=92?= =?UTF-8?q?=E6=8C=87=E5=AE=9A=E3=81=97=E3=81=A6=E9=96=A2=E9=80=A3=E3=81=99?= =?UTF-8?q?=E3=82=8B=E7=B5=B5=E6=96=87=E5=AD=97=E3=82=92=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/EmojiPickerUiState.kt | 28 +++++++++++++++---- .../note/emojis/EmojiPickerFragment.kt | 10 +++++++ .../emojis/viewmodel/EmojiPickerViewModel.kt | 5 ++++ .../note/reaction/ReactionSelectionDialog.kt | 2 +- 4 files changed, 38 insertions(+), 7 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt index 45f982b3cd..107a165a5b 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/EmojiPickerUiState.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.note +import androidx.lifecycle.SavedStateHandle import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -25,12 +26,28 @@ class EmojiPickerUiStateService( private val reactionHistoryRepository: ReactionHistoryRepository, private val userEmojiConfigRepository: UserEmojiConfigRepository, private val logger: Logger, + savedStateHandle: SavedStateHandle, coroutineScope: CoroutineScope, ) { + companion object { + const val EXTRA_ACCOUNT_ID = "EmojiPickerViewModel.EXTRA_ACCOUNT_ID" + } + @OptIn(ExperimentalCoroutinesApi::class) + val account = savedStateHandle.getStateFlow(EXTRA_ACCOUNT_ID, -1L).map { + it.takeIf { + it > 0 + } + }.flatMapLatest { specifiedId -> + accountStore.state.map { state -> + specifiedId?.let { + state.get(it) + } ?: state.currentAccount + } + }.stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), null) @OptIn(ExperimentalCoroutinesApi::class) - private val emojis = accountStore.observeCurrentAccount + private val emojis = account .filterNotNull() .flatMapLatest { ac -> customEmojiRepository.observeBy(ac.getHost()) @@ -40,8 +57,7 @@ class EmojiPickerUiStateService( .stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), emptyList()) @OptIn(ExperimentalCoroutinesApi::class) - private val reactionCount = accountStore - .observeCurrentAccount + private val reactionCount = account .filterNotNull() .flatMapLatest { ac -> reactionHistoryRepository.observeSumReactions(ac.normalizedInstanceUri) @@ -51,7 +67,7 @@ class EmojiPickerUiStateService( .stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), emptyList()) @OptIn(ExperimentalCoroutinesApi::class) - private val userSetting = accountStore.observeCurrentAccount + private val userSetting = account .filterNotNull() .flatMapLatest { ac -> userEmojiConfigRepository.observeByInstanceDomain(ac.normalizedInstanceUri) @@ -70,7 +86,7 @@ class EmojiPickerUiStateService( Reactions(emptyList(), emptyList()) ) - private val baseInfo = combine(accountStore.observeCurrentAccount, emojis) { account, emojis -> + private val baseInfo = combine(account, emojis) { account, emojis -> BaseInfo(account, emojis) }.stateIn( coroutineScope, @@ -80,7 +96,7 @@ class EmojiPickerUiStateService( @OptIn(ExperimentalCoroutinesApi::class) private val recentlyUsedReactions = - accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { + account.filterNotNull().flatMapLatest { reactionHistoryRepository.observeRecentlyUsedBy(it.normalizedInstanceUri, limit = 20) }.catch { logger.error("絵文字の直近使用履歴の取得に失敗", it) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt index 0265290625..2b85d50e6a 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/EmojiPickerFragment.kt @@ -26,6 +26,7 @@ import net.pantasystem.milktea.model.notes.reaction.LegacyReaction import net.pantasystem.milktea.model.notes.reaction.Reaction import net.pantasystem.milktea.model.notes.reaction.ReactionSelection import net.pantasystem.milktea.note.EmojiListItemType +import net.pantasystem.milktea.note.EmojiPickerUiStateService import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentEmojiPickerBinding import net.pantasystem.milktea.note.emojis.viewmodel.EmojiPickerViewModel @@ -35,6 +36,15 @@ import net.pantasystem.milktea.note.toTextReaction @AndroidEntryPoint class EmojiPickerFragment : Fragment(R.layout.fragment_emoji_picker), ReactionSelection { + companion object { + fun newInstance(accountId: Long?): EmojiPickerFragment { + return EmojiPickerFragment().also { fragment -> + fragment.arguments = Bundle().apply { + putLong(EmojiPickerUiStateService.EXTRA_ACCOUNT_ID, accountId ?: -1L) + } + } + } + } interface OnEmojiSelectedListener { fun onSelect(emoji: String) } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/viewmodel/EmojiPickerViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/viewmodel/EmojiPickerViewModel.kt index dc0aade6b0..f9d8787d54 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/viewmodel/EmojiPickerViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/viewmodel/EmojiPickerViewModel.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.note.emojis.viewmodel +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -18,7 +19,10 @@ class EmojiPickerViewModel @Inject constructor( userEmojiConfigRepository: UserEmojiConfigRepository, customEmojiRepository: CustomEmojiRepository, loggerFactory: Logger.Factory, + savedStateHandle: SavedStateHandle, ) : ViewModel() { + + private val logger = loggerFactory.create("EmojiPickerViewModel") private val uiStateService = EmojiPickerUiStateService( @@ -28,6 +32,7 @@ class EmojiPickerViewModel @Inject constructor( coroutineScope = viewModelScope, customEmojiRepository = customEmojiRepository, logger = logger, + savedStateHandle = savedStateHandle, ) val searchWord = uiStateService.searchWord diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionSelectionDialog.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionSelectionDialog.kt index 0515e6bfc7..4b0be83f75 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionSelectionDialog.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionSelectionDialog.kt @@ -62,7 +62,7 @@ class ReactionSelectionDialog : BottomSheetDialogFragment(), binding.lifecycleOwner = this if (savedInstanceState == null) { - val fragment = EmojiPickerFragment() + val fragment = EmojiPickerFragment.newInstance(noteId.accountId) childFragmentManager.beginTransaction().also { ft -> ft.add(R.id.fragmentBaseContainer, fragment) }.commit() From 42434d8878c8dac7c5d695dd50853b8a6c73f497 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 18:17:34 +0900 Subject: [PATCH 253/432] =?UTF-8?q?feat:=20accountId=E3=82=92=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=81=97=E3=81=A6=E7=94=BB=E9=9D=A2=E3=82=92=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_navigation/SearchNavigation.kt | 13 +++++++++++-- .../milktea/search/SearchResultActivity.kt | 8 ++++++++ .../milktea/search/SearchResultViewModel.kt | 18 +++++++++++++++--- .../milktea/search/SearchViewModel.kt | 5 ++++- 4 files changed, 38 insertions(+), 6 deletions(-) diff --git a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchNavigation.kt b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchNavigation.kt index 1153305b12..f6e4a7a061 100644 --- a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchNavigation.kt +++ b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchNavigation.kt @@ -5,7 +5,16 @@ interface SearchNavigation : ActivityNavigation sealed interface SearchNavType { val searchWord: String? val acct: String? - data class ResultScreen(override val searchWord: String, override val acct: String? = null) : SearchNavType - data class SearchScreen(override val searchWord: String? = null, override val acct: String? = null) : SearchNavType + val accountId: Long? + + data class ResultScreen( + override val searchWord: String, override val acct: String? = null, + override val accountId: Long? = null, + ) : SearchNavType + + data class SearchScreen( + override val searchWord: String? = null, override val acct: String? = null, + override val accountId: Long? = null, + ) : SearchNavType } \ No newline at end of file diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt index f0368cea1c..71fa2ae67b 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultActivity.kt @@ -192,6 +192,9 @@ class SearchNavigationImpl @Inject constructor( if (args.acct != null) { intent.putExtra(SearchResultViewModel.EXTRA_ACCT, args.acct) } + if (args.accountId != null) { + intent.putExtra(SearchResultViewModel.EXTRA_ACCOUNT_ID, args.accountId) + } intent } is SearchNavType.SearchScreen -> { @@ -202,6 +205,11 @@ class SearchNavigationImpl @Inject constructor( if (args.acct != null) { intent.putExtra(SearchResultViewModel.EXTRA_ACCT, args.acct) } + + if (args.accountId != null) { + intent.putExtra(SearchViewModel.EXTRA_ACCOUNT_ID, args.accountId) + } + intent } } diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt index 889f490f09..7218a40f61 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore @@ -30,18 +31,29 @@ class SearchResultViewModel @Inject constructor( const val EXTRA_KEYWORD = "net.pantasystem.milktea.search.SearchResultViewModel.EXTRA_KEYWORD" const val EXTRA_ACCT = "net.pantasystem.milktea.search.SearchResultActivity.EXTRA_ACCT" - + const val EXTRA_ACCOUNT_ID = "net.pantasystem.milktea.search.SearchResultActivity.EXTRA_ACCOUNT_ID" } private val logger by lazy { loggerFactory.create("SearchResultVM") } + @OptIn(ExperimentalCoroutinesApi::class) + val account = savedStateHandle.getStateFlow(EXTRA_ACCOUNT_ID, - 1L).map { + it.takeIf { it > 0 } + }.flatMapLatest { acId -> + accountStore.state.map { state -> + acId?.let { + state.get(it) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + private val keyword = savedStateHandle.getStateFlow(EXTRA_KEYWORD, "") private val acct = savedStateHandle.getStateFlow(EXTRA_ACCT, null) private val user = combine( acct, - accountStore.observeCurrentAccount.filterNotNull() + account.filterNotNull() ) { acct, ac -> userRepository.findByUserName( ac.accountId, @@ -53,7 +65,7 @@ class SearchResultViewModel @Inject constructor( }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) val uiState = combine( - accountStore.observeCurrentAccount, + account, keyword, acct, user diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt index 33944cb6af..8f1e74204c 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchViewModel.kt @@ -35,12 +35,15 @@ class SearchViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle ) : ViewModel() { + companion object { + const val EXTRA_ACCOUNT_ID = "net.pantasystem.milktea.search.SearchViewModel.EXTRA_ACCOUNT_ID" + } private val logger by lazy { loggerFactory.create("SearchViewModel") } val keyword = savedStateHandle.getStateFlow("keyword", "") - private val currentAccountWatcher = CurrentAccountWatcher(null, accountRepository) + private val currentAccountWatcher = CurrentAccountWatcher(savedStateHandle[EXTRA_ACCOUNT_ID], accountRepository) @OptIn(ExperimentalCoroutinesApi::class) val hashtagResult = keyword.filter { From 6f4f70ae842ff8cd4535d3a96367f22b6b65202a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 18:42:47 +0900 Subject: [PATCH 254/432] =?UTF-8?q?feat:=20=E7=8F=BE=E5=9C=A8=E9=96=8B?= =?UTF-8?q?=E3=81=84=E3=81=A6=E3=81=84=E3=82=8B=E3=82=BF=E3=83=96=E3=81=AB?= =?UTF-8?q?=E9=96=A2=E9=80=A3=E3=81=97=E3=81=A6=E3=81=84=E3=82=8BAccountId?= =?UTF-8?q?=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../misskeyandroidclient/ui/main/FabClickHandler.kt | 4 ++-- .../CurrentPageableTimelineViewModel.kt | 13 +++++++++---- .../milktea/gallery/GalleryPostsFragment.kt | 13 +++++++------ .../milktea/note/detail/NoteDetailFragment.kt | 11 +++++++---- .../milktea/note/timeline/TimelineFragment.kt | 2 +- .../milktea/notification/NotificationFragment.kt | 2 +- 6 files changed, 27 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt b/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt index 6e6c3c0c95..40e7f422fb 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt @@ -35,11 +35,11 @@ internal class FabClickHandler( startActivity(intent) } is SuitableType.Channel -> { - val accountId = accountStore.currentAccountId!! + val accountId = type.accountId ?: accountStore.currentAccountId!! startActivity( NoteEditorActivity.newBundle( this, - channelId = Channel.Id(accountId, suitableType.channelId) + channelId = Channel.Id(accountId, suitableType.channelId), ) ) } diff --git a/modules/common_viewmodel/src/main/java/net/pantasystem/milktea/common_viewmodel/CurrentPageableTimelineViewModel.kt b/modules/common_viewmodel/src/main/java/net/pantasystem/milktea/common_viewmodel/CurrentPageableTimelineViewModel.kt index 6759d52608..5b4848fa13 100644 --- a/modules/common_viewmodel/src/main/java/net/pantasystem/milktea/common_viewmodel/CurrentPageableTimelineViewModel.kt +++ b/modules/common_viewmodel/src/main/java/net/pantasystem/milktea/common_viewmodel/CurrentPageableTimelineViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import net.pantasystem.milktea.model.account.page.Pageable import javax.inject.Inject @@ -34,12 +35,16 @@ class CurrentPageableTimelineViewModel @Inject constructor( ) : ViewModel() { private val _currentType = MutableStateFlow( - CurrentPageType.Page(Pageable.HomeTimeline())) + CurrentPageType.Page(null, Pageable.HomeTimeline())) val currentType: StateFlow = _currentType - fun setCurrentPageable(pageable: Pageable) { - _currentType.value = CurrentPageType.Page(pageable) + + private val _currentAccountId = MutableStateFlow(null) + val currentAccountId = _currentAccountId.asStateFlow() + + fun setCurrentPageable(accountId: Long?, pageable: Pageable) { + _currentType.value = CurrentPageType.Page(accountId, pageable) } fun setCurrentPageType(type: CurrentPageType) { @@ -49,6 +54,6 @@ class CurrentPageableTimelineViewModel @Inject constructor( } sealed interface CurrentPageType { - data class Page(val pageable: Pageable) : CurrentPageType + data class Page(val accountId: Long?, val pageable: Pageable) : CurrentPageType object Account : CurrentPageType } \ No newline at end of file diff --git a/modules/features/gallery/src/main/java/net/pantasystem/milktea/gallery/GalleryPostsFragment.kt b/modules/features/gallery/src/main/java/net/pantasystem/milktea/gallery/GalleryPostsFragment.kt index 80a37f8a55..a89555c1a7 100644 --- a/modules/features/gallery/src/main/java/net/pantasystem/milktea/gallery/GalleryPostsFragment.kt +++ b/modules/features/gallery/src/main/java/net/pantasystem/milktea/gallery/GalleryPostsFragment.kt @@ -68,13 +68,14 @@ class GalleryPostsFragment : Fragment() { @Inject lateinit var authorizationNavigation: AuthorizationNavigation - val viewModel: GalleryPostsViewModel by viewModels { - val pageable = arguments?.getSerializable(EXTRA_PAGEABLE) as Pageable.Gallery - var accountId = arguments?.getLong(EXTRA_ACCOUNT_ID, -1) - if (accountId == -1L) { - accountId = null + private val accountId: Long? by lazy { + arguments?.getLong(EXTRA_ACCOUNT_ID, -1)?.takeIf { + it > 0 } + } + val viewModel: GalleryPostsViewModel by viewModels { + val pageable = arguments?.getSerializable(EXTRA_PAGEABLE) as Pageable.Gallery GalleryPostsViewModel.provideFactory(viewModelFactory, pageable, accountId) } @@ -147,7 +148,7 @@ class GalleryPostsFragment : Fragment() { override fun onResume() { super.onResume() - currentTimelineViewModel.setCurrentPageable(pageable) + currentTimelineViewModel.setCurrentPageable(accountId, pageable) } } \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailFragment.kt index d648d38fd9..571266bcca 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/detail/NoteDetailFragment.kt @@ -94,10 +94,13 @@ class NoteDetailFragment : Fragment(R.layout.fragment_note_detail) { (arguments?.getSerializable(EXTRA_PAGE) as? Page)?.pageable() as? Pageable.Show ?: Pageable.Show(arguments?.getString(EXTRA_NOTE_ID)!!) } - private val noteDetailViewModel: NoteDetailViewModel by viewModels { - val accountId = arguments?.getLong(EXTRA_ACCOUNT_ID, -1)?.let { - if (it == -1L) null else it + + val accountId: Long? by lazy { + arguments?.getLong(EXTRA_ACCOUNT_ID, -1)?.takeIf { + it > 0 } + } + private val noteDetailViewModel: NoteDetailViewModel by viewModels { NoteDetailViewModel.provideFactory( noteDetailViewModelAssistedFactory, page, @@ -149,7 +152,7 @@ class NoteDetailFragment : Fragment(R.layout.fragment_note_detail) { override fun onResume() { super.onResume() - currentPageableTimelineViewModel.setCurrentPageable(page) + currentPageableTimelineViewModel.setCurrentPageable(accountId, page) } @MainThread diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt index 5e35764903..192b6fdffb 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/timeline/TimelineFragment.kt @@ -292,7 +292,7 @@ class TimelineFragment : Fragment(R.layout.fragment_swipe_refresh_recycler_view) isShowing = true mViewModel.onResume() - currentPageableTimelineViewModel.setCurrentPageable(mPageable) + currentPageableTimelineViewModel.setCurrentPageable(mViewModel.accountId?.value, mPageable) try { layoutManager.scrollToPosition(mViewModel.position) } catch (_: Exception) { diff --git a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt index 17b8dca741..237f9e12e5 100644 --- a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt +++ b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt @@ -168,7 +168,7 @@ class NotificationFragment : Fragment(R.layout.fragment_notification) { override fun onResume() { super.onResume() - currentPageableTimelineViewModel.setCurrentPageable(Pageable.Notification()) + currentPageableTimelineViewModel.setCurrentPageable(null, Pageable.Notification()) } private val mScrollListener = object : RecyclerView.OnScrollListener() { From 798fa4139100e80f66e739952cd2470f9d732864 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 18:58:22 +0900 Subject: [PATCH 255/432] =?UTF-8?q?refactor:=20=E5=88=A5=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=AB=E5=88=87=E3=82=8A=E5=87=BA=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/viewmodel/AccountViewModel.kt | 69 ++++++------------- .../viewmodel/AccountViewModelUiState.kt | 44 ++++++++++++ 2 files changed, 66 insertions(+), 47 deletions(-) create mode 100644 modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModelUiState.kt diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModel.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModel.kt index b49b3ab73a..e178a2a8f0 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModel.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModel.kt @@ -15,7 +15,6 @@ import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.SignOutUseCase import net.pantasystem.milktea.model.account.page.Page import net.pantasystem.milktea.model.instance.InstanceInfoService -import net.pantasystem.milktea.model.instance.InstanceInfoType import net.pantasystem.milktea.model.instance.SyncMetaExecutor import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserDataSource @@ -35,7 +34,6 @@ class AccountViewModel @Inject constructor( private val syncMetaExecutor: SyncMetaExecutor, ) : ViewModel() { - private val logger = loggerFactory.create("AccountViewModel") @@ -63,20 +61,7 @@ class AccountViewModel @Inject constructor( accountStore.observeCurrentAccount, metaList, ) { accounts, users, current, metaList -> - val userMap = users.associateBy { - it.id.accountId - } - val metaMap = metaList.filterNotNull().associateBy { - it.uri - } - accounts.map { - AccountInfo( - it, - userMap[it.accountId], - metaMap[it.normalizedInstanceUri], - current?.accountId == it.accountId - ) - } + accounts.toAccountInfoList(current, metaList, users) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) val uiState = combine( @@ -96,19 +81,35 @@ class AccountViewModel @Inject constructor( userDataSource.observe(User.Id(account.accountId, account.remoteId)).map { it as? User.Detail } - }.flowOn(Dispatchers.IO).stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + }.flowOn(Dispatchers.IO).stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + null, + ) - private val _switchAccountEvent = MutableSharedFlow(extraBufferCapacity = 10, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val _switchAccountEvent = MutableSharedFlow( + extraBufferCapacity = 10, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) val switchAccountEvent = _switchAccountEvent.asSharedFlow() - private val _showFollowersEvent = MutableSharedFlow(extraBufferCapacity = 10, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val _showFollowersEvent = MutableSharedFlow( + extraBufferCapacity = 10, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) val showFollowersEvent = _showFollowersEvent.asSharedFlow() - private val _showFollowingsEvent = MutableSharedFlow(extraBufferCapacity = 10, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val _showFollowingsEvent = MutableSharedFlow( + extraBufferCapacity = 10, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) val showFollowingsEvent = _showFollowingsEvent.asSharedFlow() - private val _showProfileEvent = MutableSharedFlow(extraBufferCapacity = 10, onBufferOverflow = BufferOverflow.DROP_OLDEST) + private val _showProfileEvent = MutableSharedFlow( + extraBufferCapacity = 10, + onBufferOverflow = BufferOverflow.DROP_OLDEST + ) val showProfileEvent = _showProfileEvent.asSharedFlow() @@ -172,32 +173,6 @@ class AccountViewModel @Inject constructor( } } - fun removePage(page: Page) { - viewModelScope.launch { - try { - accountStore.removePage(page) - } catch (e: Throwable) { - logger.error("pageの削除に失敗", e = e) - } - } - } } -data class AccountInfo( - val account: Account, - val user: User?, - val instanceMeta: InstanceInfoType?, - val isCurrentAccount: Boolean -) - -data class AccountViewModelUiState( - val currentAccount: Account? = null, - val accounts: List = emptyList(), -) { - val currentAccountInfo: AccountInfo? by lazy { - accounts.firstOrNull { - it.account.accountId == currentAccount?.accountId - } - } -} \ No newline at end of file diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModelUiState.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModelUiState.kt new file mode 100644 index 0000000000..116357272f --- /dev/null +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModelUiState.kt @@ -0,0 +1,44 @@ +package net.pantasystem.milktea.common_android_ui.account.viewmodel + +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.instance.InstanceInfoType +import net.pantasystem.milktea.model.user.User + +data class AccountInfo( + val account: Account, + val user: User?, + val instanceMeta: InstanceInfoType?, + val isCurrentAccount: Boolean, +) + +data class AccountViewModelUiState( + val currentAccount: Account? = null, + val accounts: List = emptyList(), +) { + val currentAccountInfo: AccountInfo? by lazy { + accounts.firstOrNull { + it.account.accountId == currentAccount?.accountId + } + } +} + +fun List.toAccountInfoList( + currentAccount: Account?, + instanceInfoList: List, + users: List, +): List { + val userMap = users.associateBy { + it.id.accountId + } + val metaMap = instanceInfoList.filterNotNull().associateBy { + it.uri + } + return map { + AccountInfo( + it, + userMap[it.accountId], + metaMap[it.normalizedInstanceUri], + currentAccount?.accountId == it.accountId + ) + } +} \ No newline at end of file From 4eae26a4f0606150ff9611e0ca12630f18a130c2 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 20:20:19 +0900 Subject: [PATCH 256/432] =?UTF-8?q?refactor:=20=E5=88=A5=E3=83=95=E3=82=A1?= =?UTF-8?q?=E3=82=A4=E3=83=AB=E3=81=AB=E5=88=87=E3=82=8A=E5=87=BA=E3=81=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/viewmodel/AccountViewModel.kt | 45 +++----------- .../AccountViewModelUiStateHelper.kt | 60 +++++++++++++++++++ 2 files changed, 69 insertions(+), 36 deletions(-) create mode 100644 modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModelUiStateHelper.kt diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModel.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModel.kt index e178a2a8f0..18dee58253 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModel.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModel.kt @@ -25,11 +25,11 @@ import javax.inject.Inject @Suppress("UNCHECKED_CAST") @HiltViewModel class AccountViewModel @Inject constructor( + loggerFactory: Logger.Factory, + instanceInfoService: InstanceInfoService, private val accountStore: AccountStore, private val userDataSource: UserDataSource, - loggerFactory: Logger.Factory, private val userRepository: UserRepository, - private val instanceInfoService: InstanceInfoService, private val signOutUseCase: SignOutUseCase, private val syncMetaExecutor: SyncMetaExecutor, ) : ViewModel() { @@ -37,43 +37,16 @@ class AccountViewModel @Inject constructor( private val logger = loggerFactory.create("AccountViewModel") - private val users = accountStore.observeAccounts.flatMapLatest { accounts -> - val flows = accounts.map { - userDataSource.observe(User.Id(it.accountId, it.remoteId)).flowOn(Dispatchers.IO) - } - combine(flows) { - it.toList() - } - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) - - private val metaList = accountStore.observeAccounts.flatMapLatest { accounts -> - val flows = accounts.map { - instanceInfoService.observe(it.normalizedInstanceUri).flowOn(Dispatchers.IO) - } - combine(flows) { - it.toList() - } - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) - - private val accountWithUserList = combine( - accountStore.observeAccounts, - users, + private val uiStateHelper = AccountViewModelUiStateHelper( accountStore.observeCurrentAccount, - metaList, - ) { accounts, users, current, metaList -> - accounts.toAccountInfoList(current, metaList, users) - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) + accountStore, + userDataSource, + instanceInfoService, + viewModelScope + ) - val uiState = combine( - accountStore.observeCurrentAccount, - accountWithUserList - ) { current, accounts -> - AccountViewModelUiState( - currentAccount = current, - accounts = accounts - ) - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), AccountViewModelUiState()) + val uiState = uiStateHelper.uiState val currentAccount = accountStore.observeCurrentAccount.stateIn(viewModelScope, SharingStarted.Lazily, null) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModelUiStateHelper.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModelUiStateHelper.kt new file mode 100644 index 0000000000..57ceec9f2d --- /dev/null +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/viewmodel/AccountViewModelUiStateHelper.kt @@ -0,0 +1,60 @@ +package net.pantasystem.milktea.common_android_ui.account.viewmodel + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* +import net.pantasystem.milktea.app_store.account.AccountStore +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.instance.InstanceInfoService +import net.pantasystem.milktea.model.user.User +import net.pantasystem.milktea.model.user.UserDataSource + +class AccountViewModelUiStateHelper( + currentAccountFlow: Flow, + accountStore: AccountStore, + private val userDataSource: UserDataSource, + private val instanceInfoService: InstanceInfoService, + viewModelScope: CoroutineScope, +) { + + @OptIn(ExperimentalCoroutinesApi::class) + private val users = accountStore.observeAccounts.flatMapLatest { accounts -> + val flows = accounts.map { + userDataSource.observe(User.Id(it.accountId, it.remoteId)).flowOn(Dispatchers.IO) + } + combine(flows) { + it.toList() + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) + + @OptIn(ExperimentalCoroutinesApi::class) + private val metaList = accountStore.observeAccounts.flatMapLatest { accounts -> + val flows = accounts.map { + instanceInfoService.observe(it.normalizedInstanceUri).flowOn(Dispatchers.IO) + } + combine(flows) { + it.toList() + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) + + private val accountWithUserList = combine( + accountStore.observeAccounts, + users, + currentAccountFlow, + metaList, + ) { accounts, users, current, metaList -> + accounts.toAccountInfoList(current, metaList, users) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) + + val uiState = combine( + currentAccountFlow, + accountWithUserList + ) { current, accounts -> + AccountViewModelUiState( + currentAccount = current, + accounts = accounts + ) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), AccountViewModelUiState()) + +} \ No newline at end of file From 19c0eef73d4766224b4292022fd10d2584dbefa5 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 20:34:49 +0900 Subject: [PATCH 257/432] =?UTF-8?q?feat:=20Account=E3=81=AE=E5=88=87?= =?UTF-8?q?=E3=82=8A=E6=9B=BF=E3=81=88=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD?= =?UTF-8?q?=E3=82=B0=E3=81=AEUIState=E3=82=92=E6=8C=81=E3=81=A4=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=80=81=E3=81=BE=E3=81=9F=E3=82=A2?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=82=92=E5=88=87=E3=82=8A?= =?UTF-8?q?=E6=9B=BF=E3=81=88=E3=82=8B=E3=83=AD=E3=82=B8=E3=83=83=E3=82=AF?= =?UTF-8?q?=E3=82=92=E5=AE=9F=E8=A3=85=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../editor/viewmodel/NoteEditorViewModel.kt | 77 ++++++++++++++----- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt index 2aa08dd2d8..d4d1342f26 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt @@ -13,6 +13,7 @@ import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.* import net.pantasystem.milktea.common.text.UrlPatternChecker import net.pantasystem.milktea.common_android.eventbus.EventBus +import net.pantasystem.milktea.common_android_ui.account.viewmodel.AccountViewModelUiStateHelper import net.pantasystem.milktea.common_viewmodel.UserViewData import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository @@ -40,6 +41,7 @@ import net.pantasystem.milktea.model.notes.reservation.NoteReservationPostExecut import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.model.setting.RememberVisibility import net.pantasystem.milktea.model.user.User +import net.pantasystem.milktea.model.user.UserDataSource import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewDataCache import net.pantasystem.milktea.worker.note.CreateNoteWorkerExecutor import java.util.* @@ -49,7 +51,8 @@ import javax.inject.Inject class NoteEditorViewModel @Inject constructor( loggerFactory: Logger.Factory, planeNoteViewDataCacheFactory: PlaneNoteViewDataCache.Factory, - accountStore: AccountStore, + userDataSource: UserDataSource, + private val accountStore: AccountStore, private val getAllMentionUsersUseCase: GetAllMentionUsersUseCase, private val filePropertyDataSource: FilePropertyDataSource, private val instanceInfoService: InstanceInfoService, @@ -313,6 +316,14 @@ class NoteEditorViewModel @Inject constructor( emit(null) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + val accountUiState = AccountViewModelUiStateHelper( + currentAccount, + accountStore, + userDataSource, + instanceInfoService, + viewModelScope, + ).uiState + private val _fileSizeInvalidEvent = MutableSharedFlow(extraBufferCapacity = 10) val fileSizeInvalidEvent = _fileSizeInvalidEvent.asSharedFlow() @@ -327,25 +338,6 @@ class NoteEditorViewModel @Inject constructor( var focusType: NoteEditorFocusEditTextType = NoteEditorFocusEditTextType.Text - init { - accountStore.observeCurrentAccount.filterNotNull().map { - it to noteEditorSwitchAccountExecutor( - currentAccount.value, - noteEditorSendToState.value, - it - ) - }.onEach { (account, result) -> - if (account.accountId != currentAccount.value?.accountId && currentAccount.value != null) { - savedStateHandle.setReplyId(result.replyId) - savedStateHandle.setRenoteId(result.renoteId) - savedStateHandle.setChannelId(result.channelId) - } - if (currentAccount.value != null) { - savedStateHandle.setVisibility(null) - } - currentAccount.value = account - }.launchIn(viewModelScope + Dispatchers.IO) - } fun setRenoteTo(noteId: Note.Id?) { savedStateHandle.setRenoteId(noteId) @@ -772,10 +764,55 @@ class NoteEditorViewModel @Inject constructor( }.getOrElse { false } } + fun setAccountId(accountId: Long?) { + viewModelScope.launch { + (accountId?.let { + accountRepository.get(accountId) + } ?: accountRepository.getCurrentAccount()).onSuccess { + setAccount(it) + }.onFailure { + logger.error("アカウントの取得に失敗した", it) + } + } + } + + fun setAccountIdAndSwitchCurrentAccount(accountId: Long?) { + viewModelScope.launch { + (accountId?.let { + accountRepository.get(accountId) + } ?: accountRepository.getCurrentAccount()).onSuccess { + setAccount(it) + accountStore.setCurrent(it) + }.onFailure { + logger.error("アカウントの取得に失敗した", it) + } + } + } + private fun setUpUserViewData(userId: User.Id): UserViewData { return userViewDataFactory.create(userId, viewModelScope, dispatcher) } + private suspend fun setAccount(account: Account) = runCancellableCatching { + val result = noteEditorSwitchAccountExecutor( + currentAccount.value, + noteEditorSendToState.value, + account, + ) + + if (account.accountId != currentAccount.value?.accountId && currentAccount.value != null) { + savedStateHandle.setReplyId(result.replyId) + savedStateHandle.setRenoteId(result.renoteId) + savedStateHandle.setChannelId(result.channelId) + } + if (currentAccount.value != null) { + savedStateHandle.setVisibility(null) + } + logger.debug { + "currentAccount:${account.userName}@${account.getHost()}" + } + currentAccount.value = account + } } From e38f50562b26b4fc6d0b699ff6d8e5a580c3e549 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 20:35:27 +0900 Subject: [PATCH 258/432] =?UTF-8?q?feat:=20=E3=83=8E=E3=83=BC=E3=83=88?= =?UTF-8?q?=E7=B7=A8=E9=9B=86=E7=94=BB=E9=9D=A2=E3=82=88=E3=81=86=E3=81=AE?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E5=88=87=E3=82=8A?= =?UTF-8?q?=E6=9B=BF=E3=81=88=E3=83=80=E3=82=A4=E3=82=A2=E3=83=AD=E3=82=B0?= =?UTF-8?q?=E3=82=92=E5=AE=9F=E8=A3=85=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/NoteEditorSwitchAccountDialog.kt | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/account/NoteEditorSwitchAccountDialog.kt diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/account/NoteEditorSwitchAccountDialog.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/account/NoteEditorSwitchAccountDialog.kt new file mode 100644 index 0000000000..74b671b12b --- /dev/null +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/account/NoteEditorSwitchAccountDialog.kt @@ -0,0 +1,69 @@ +package net.pantasystem.milktea.note.editor.account + +import android.app.Dialog +import android.os.Bundle +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.platform.ComposeView +import androidx.fragment.app.activityViewModels +import com.google.android.material.bottomsheet.BottomSheetDialogFragment +import com.google.android.material.composethemeadapter.MdcTheme +import dagger.hilt.android.AndroidEntryPoint +import net.pantasystem.milktea.common_android_ui.account.AccountSwitchingDialogLayout +import net.pantasystem.milktea.common_navigation.* +import net.pantasystem.milktea.note.editor.viewmodel.NoteEditorViewModel +import javax.inject.Inject + +@AndroidEntryPoint +class NoteEditorSwitchAccountDialog : BottomSheetDialogFragment() { + @Inject + lateinit var authorizationNavigation: AuthorizationNavigation + + @Inject + lateinit var userDetailNavigation: UserDetailNavigation + + @Inject + lateinit var accountSettingNavigation: AccountSettingNavigation + + val viewModel: NoteEditorViewModel by activityViewModels() + + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return super.onCreateDialog(savedInstanceState).apply { + val view = ComposeView(requireContext()).apply { + setContent { + MdcTheme { + val uiState by viewModel.accountUiState.collectAsState() + AccountSwitchingDialogLayout( + uiState = uiState, + onSettingButtonClicked = { + startActivity(accountSettingNavigation.newIntent(Unit)) + dismiss() + }, + onAvatarIconClicked = { accountInfo -> + startActivity( + userDetailNavigation.newIntent(UserDetailNavigationArgs.UserName(accountInfo.user?.let { + "@${it.userName}@${it.host}" + } ?: "@${accountInfo.account.userName}@${accountInfo.account.getHost()}")) + ) + dismiss() + }, + onAccountClicked = { + viewModel.setAccountIdAndSwitchCurrentAccount(it.account.accountId) + dismiss() + }, + onAddAccountButtonClicked = { + requireActivity().startActivity(authorizationNavigation.newIntent( + AuthorizationArgs.New)) + dismiss() + } + ) + } + } + } + setContentView(view) + } + } + + +} \ No newline at end of file From 4c8a1fdb731b7508a71e98afc7cc1ca87e5f0fc2 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 20:35:50 +0900 Subject: [PATCH 259/432] =?UTF-8?q?feat:=20=E7=8F=BE=E5=9C=A8=E3=81=AE?= =?UTF-8?q?=E3=82=BF=E3=83=96=E3=81=AE=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=81=A7=E7=B7=A8=E9=9B=86=E7=94=BB=E9=9D=A2=E3=81=8C?= =?UTF-8?q?=E9=96=8B=E3=81=8B=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/main/FabClickHandler.kt | 2 +- .../milktea/note/NoteEditorActivity.kt | 12 +++++++++++- .../milktea/note/editor/NoteEditorFragment.kt | 17 +++++++++++++++-- 3 files changed, 27 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt b/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt index 40e7f422fb..38ee5f1d1d 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt @@ -27,7 +27,7 @@ internal class FabClickHandler( is CurrentPageType.Page -> { when (val suitableType = type.pageable.suitableType()) { is SuitableType.Other -> { - startActivity(Intent(this, NoteEditorActivity::class.java)) + startActivity(NoteEditorActivity.newBundle(this, accountId = type.accountId)) } is SuitableType.Gallery -> { val intent = Intent(this, GalleryPostsActivity::class.java) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/NoteEditorActivity.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/NoteEditorActivity.kt index 5fb7afd634..019cef1285 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/NoteEditorActivity.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/NoteEditorActivity.kt @@ -33,6 +33,7 @@ class NoteEditorActivity : AppCompatActivity() { private const val EXTRA_MENTIONS = "EXTRA_MENTIONS" private const val EXTRA_CHANNEL_ID = "EXTRA_CHANNEL_ID" + private const val EXTRA_SPECIFIED_ACCOUNT_ID = "EXTRA_SPECIFIED_ACCOUNT_ID" fun newBundle( context: Context, @@ -41,6 +42,7 @@ class NoteEditorActivity : AppCompatActivity() { draftNoteId: Long? = null, mentions: List? = null, channelId: Channel.Id? = null, + accountId: Long? = null, ): Intent { return Intent(context, NoteEditorActivity::class.java).apply { replyTo?.let { @@ -67,6 +69,10 @@ class NoteEditorActivity : AppCompatActivity() { putExtra(EXTRA_ACCOUNT_ID, it.accountId) } + accountId?.let { + putExtra(EXTRA_SPECIFIED_ACCOUNT_ID, it) + } + } } } @@ -125,6 +131,9 @@ class NoteEditorActivity : AppCompatActivity() { if (it == -1L) null else it } + val specifiedAccountId = intent.getLongExtra(EXTRA_SPECIFIED_ACCOUNT_ID, -1).takeIf { + it > 0 + } if (savedInstanceState == null) { val mentions = intent.getStringArrayExtra(EXTRA_MENTIONS)?.toList() val fragment = NoteEditorFragment.newInstance( @@ -133,7 +142,8 @@ class NoteEditorActivity : AppCompatActivity() { draftNoteId = draftNoteId, mentions = mentions, channelId = channelId, - text = text + text = text, + specifiedAccountId = specifiedAccountId, ) val ft = supportFragmentManager.beginTransaction() ft.replace(R.id.fragmentBase, fragment) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index 1537f78270..4ffc4524d5 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -40,7 +40,6 @@ import net.pantasystem.milktea.common_android.ui.Activities import net.pantasystem.milktea.common_android.ui.listview.applyFlexBoxLayout import net.pantasystem.milktea.common_android.ui.putActivity import net.pantasystem.milktea.common_android.ui.text.CustomEmojiTokenizer -import net.pantasystem.milktea.common_android_ui.account.AccountSwitchingDialog import net.pantasystem.milktea.common_android_ui.account.viewmodel.AccountViewModel import net.pantasystem.milktea.common_android_ui.confirm.ConfirmDialog import net.pantasystem.milktea.common_navigation.* @@ -59,6 +58,7 @@ import net.pantasystem.milktea.note.DraftNotesActivity import net.pantasystem.milktea.note.R import net.pantasystem.milktea.note.databinding.FragmentNoteEditorBinding import net.pantasystem.milktea.note.databinding.ViewNoteEditorToolbarBinding +import net.pantasystem.milktea.note.editor.account.NoteEditorSwitchAccountDialog import net.pantasystem.milktea.note.editor.file.EditFileCaptionDialog import net.pantasystem.milktea.note.editor.file.EditFileNameDialog import net.pantasystem.milktea.note.editor.viewmodel.NoteEditorFocusEditTextType @@ -81,6 +81,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti private const val EXTRA_MENTIONS = "EXTRA_MENTIONS" private const val EXTRA_CHANNEL_ID = "EXTRA_CHANNEL_ID" private const val EXTRA_TEXT = "EXTRA_TEXT" + private const val EXTRA_SPECIFIED_ACCOUNT_ID = "EXTRA_SPECIFIED_ACCOUNT_ID" fun newInstance( replyTo: Note.Id? = null, @@ -89,6 +90,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti mentions: List? = null, channelId: Channel.Id? = null, text: String? = null, + specifiedAccountId: Long? = null, ): NoteEditorFragment { return NoteEditorFragment().apply { arguments = Bundle().apply { @@ -115,6 +117,10 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti if (text != null) { putString(EXTRA_TEXT, text) } + + if (specifiedAccountId != null) { + putLong(EXTRA_SPECIFIED_ACCOUNT_ID, specifiedAccountId) + } } } } @@ -208,6 +214,11 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti requireArguments().getString(EXTRA_TEXT, null) } + private val specifiedAccountId by lazy(LazyThreadSafetyMode.NONE) { + requireArguments().getLong(EXTRA_SPECIFIED_ACCOUNT_ID, -1).takeIf { + it > 0 + } + } @OptIn(ExperimentalCoroutinesApi::class) override fun onViewCreated(view: View, savedInstanceState: Bundle?) { @@ -247,7 +258,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti noteEditorToolbar.viewModel = noteEditorViewModel accountViewModel.switchAccountEvent.onEach { - AccountSwitchingDialog().show(childFragmentManager, "tag") + NoteEditorSwitchAccountDialog().show(childFragmentManager, "tag") }.flowWithLifecycle( viewLifecycleOwner.lifecycle, Lifecycle.State.RESUMED ).launchIn(viewLifecycleOwner.lifecycleScope) @@ -440,6 +451,8 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti onSelect(it) } + noteEditorViewModel.setAccountId(specifiedAccountId) + binding.addAddress.setOnClickListener { startSearchAndSelectUser() } From eb28b391095b55e0e2c35ed4bffbc964c86f444f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 20:38:45 +0900 Subject: [PATCH 260/432] =?UTF-8?q?feat:=20=E7=8F=BE=E5=9C=A8=E3=81=AE?= =?UTF-8?q?=E3=82=BF=E3=83=96=E3=81=AE=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=81=A7=E7=B7=A8=E9=9B=86=E7=94=BB=E9=9D=A2=E3=81=8C?= =?UTF-8?q?=E9=96=8B=E3=81=8B=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/editor/SimpleEditorFragment.kt | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/SimpleEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/SimpleEditorFragment.kt index aaab2ccf5f..e12aee8db9 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/SimpleEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/SimpleEditorFragment.kt @@ -27,6 +27,8 @@ import net.pantasystem.milktea.common_android.ui.listview.applyFlexBoxLayout import net.pantasystem.milktea.common_android.ui.text.CustomEmojiTokenizer import net.pantasystem.milktea.common_android_ui.account.viewmodel.AccountViewModel import net.pantasystem.milktea.common_navigation.* +import net.pantasystem.milktea.common_viewmodel.CurrentPageType +import net.pantasystem.milktea.common_viewmodel.CurrentPageableTimelineViewModel import net.pantasystem.milktea.model.drive.DriveFileRepository import net.pantasystem.milktea.model.drive.FileProperty import net.pantasystem.milktea.model.drive.FilePropertyDataSource @@ -63,6 +65,8 @@ class SimpleEditorFragment : Fragment(R.layout.fragment_simple_editor), SimpleEd val accountViewModel: AccountViewModel by activityViewModels() val mViewModel: NoteEditorViewModel by activityViewModels() + private val currentPageableTimelineViewModel: CurrentPageableTimelineViewModel by activityViewModels() + private val mBinding: FragmentSimpleEditorBinding by dataBinding() override val isShowEditorMenu: MutableLiveData = MutableLiveData(false) @@ -253,6 +257,22 @@ class SimpleEditorFragment : Fragment(R.layout.fragment_simple_editor), SimpleEd emojiSelectionViewModel.selectedEmojiName.observe(viewLifecycleOwner, (::onSelect)) emojiSelectionViewModel.selectedEmoji.observe(viewLifecycleOwner, (::onSelect)) + + viewLifecycleOwner.lifecycleScope.launch { + accountViewModel.currentAccount.collect { + viewModel.setAccountId(it?.accountId) + } + } + viewLifecycleOwner.lifecycleScope.launch { + currentPageableTimelineViewModel.currentType.collect { + when(it) { + CurrentPageType.Account -> Unit + is CurrentPageType.Page -> { + viewModel.setAccountId(it.accountId) + } + } + } + } } From b2937fe9d8f670fd7cbbedf5e3577d39faea1b87 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 20:45:01 +0900 Subject: [PATCH 261/432] =?UTF-8?q?feat:=20=E9=81=B8=E6=8A=9E=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=9F=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=81=AE=E7=8A=B6=E6=85=8B=E3=81=A7=E3=83=89=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=83=96=E3=81=8C=E9=96=8B=E3=81=8B=E3=82=8C=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/editor/NoteEditorFragment.kt | 4 +- .../editor/viewmodel/NoteEditorViewModel.kt | 49 ++++++++++--------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index 4ffc4524d5..6824a0ac43 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -276,7 +276,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti ).launchIn(viewLifecycleOwner.lifecycleScope) - accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { + accountViewModel.currentAccount.filterNotNull().flatMapLatest { metaRepository.observe(it.normalizedInstanceUri) }.mapNotNull { it?.emojis @@ -654,7 +654,7 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti val intent = driveNavigation.newIntent( DriveNavigationArgs( selectableFileMaxSize = selectableMaxSize, - accountId = accountStore.currentAccountId, + accountId = noteEditorViewModel.currentAccount.value?.accountId, ) ) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt index d4d1342f26..082b609524 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt @@ -79,7 +79,8 @@ class NoteEditorViewModel @Inject constructor( private val logger = loggerFactory.create("NoteEditorViewModel") - private val currentAccount = MutableStateFlow(null) + private val _currentAccount = MutableStateFlow(null) + val currentAccount = _currentAccount.asStateFlow() val text = savedStateHandle.getStateFlow(NoteEditorSavedStateKey.Text.name, null) @@ -94,7 +95,7 @@ class NoteEditorViewModel @Inject constructor( ) @OptIn(ExperimentalCoroutinesApi::class) - val instanceInfoType = currentAccount.filterNotNull().flatMapLatest { + val instanceInfoType = _currentAccount.filterNotNull().flatMapLatest { instanceInfoService.observe(it.normalizedInstanceUri) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) @@ -146,7 +147,7 @@ class NoteEditorViewModel @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) val maxTextLength = - currentAccount.filterNotNull().flatMapLatest { account -> + _currentAccount.filterNotNull().flatMapLatest { account -> instanceInfoService.observe(account.normalizedInstanceUri).filterNotNull() .map { meta -> meta.maxNoteTextLength @@ -158,16 +159,16 @@ class NoteEditorViewModel @Inject constructor( ) - val enableFeatures = currentAccount.filterNotNull().map { + val enableFeatures = _currentAccount.filterNotNull().map { featureEnables.enableFeatures(it.normalizedInstanceUri) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptySet()) - val maxFileCount = currentAccount.filterNotNull().mapNotNull { + val maxFileCount = _currentAccount.filterNotNull().mapNotNull { instanceInfoService.find(it.normalizedInstanceUri).getOrNull()?.maxFileCount }.stateIn(viewModelScope + Dispatchers.IO, started = SharingStarted.Eagerly, initialValue = 4) @OptIn(ExperimentalCoroutinesApi::class) - val instanceInfo = currentAccount.filterNotNull().flatMapLatest { + val instanceInfo = _currentAccount.filterNotNull().flatMapLatest { instanceInfoRepository.observeByHost(it.getHost()) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) @@ -176,7 +177,7 @@ class NoteEditorViewModel @Inject constructor( NoteEditorSavedStateKey.Visibility.name, null ) - val visibility = combine(_visibility, currentAccount.filterNotNull().map { + val visibility = combine(_visibility, _currentAccount.filterNotNull().map { localConfigRepository.getRememberVisibility(it.accountId).getOrElse { RememberVisibility.None } @@ -231,7 +232,7 @@ class NoteEditorViewModel @Inject constructor( }.stateIn(viewModelScope + Dispatchers.IO, started = SharingStarted.Lazily, initialValue = 1500) @OptIn(ExperimentalCoroutinesApi::class) - val channels = currentAccount.filterNotNull().flatMapLatest { + val channels = _currentAccount.filterNotNull().flatMapLatest { suspend { channelRepository.findFollowedChannels(it.accountId).onFailure { logger.error("load channel error", it) @@ -248,7 +249,7 @@ class NoteEditorViewModel @Inject constructor( @FlowPreview @ExperimentalCoroutinesApi val currentUser: StateFlow = - currentAccount.filterNotNull().map { + _currentAccount.filterNotNull().map { val userId = User.Id(it.accountId, it.remoteId) userViewDataFactory.create( userId, @@ -289,7 +290,7 @@ class NoteEditorViewModel @Inject constructor( noteEditorSendToState, filePreviewSources, poll, - currentAccount, + _currentAccount, ) { formState, sendToState, files, poll, account -> NoteEditorUiState( formState = formState, @@ -305,7 +306,7 @@ class NoteEditorViewModel @Inject constructor( }.asLiveData() private val cache = planeNoteViewDataCacheFactory.create({ - requireNotNull(currentAccount.value) + requireNotNull(_currentAccount.value) }, viewModelScope) val replyTo = replyId.map { id -> @@ -317,7 +318,7 @@ class NoteEditorViewModel @Inject constructor( }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) val accountUiState = AccountViewModelUiStateHelper( - currentAccount, + _currentAccount, accountStore, userDataSource, instanceInfoService, @@ -386,7 +387,7 @@ class NoteEditorViewModel @Inject constructor( currentAccount = account ) }.onSuccess { note -> - currentAccount.value = note.currentAccount + _currentAccount.value = note.currentAccount savedStateHandle.applyBy(note) } } @@ -430,7 +431,7 @@ class NoteEditorViewModel @Inject constructor( fun post() { - currentAccount.value?.let { account -> + _currentAccount.value?.let { account -> viewModelScope.launch { val reservationPostingAt = savedStateHandle.getNoteEditingUiState( @@ -464,7 +465,7 @@ class NoteEditorViewModel @Inject constructor( } fun toggleNsfw(appFile: AppFile) { - if (currentAccount.value?.instanceType == Account.InstanceType.MASTODON) { + if (_currentAccount.value?.instanceType == Account.InstanceType.MASTODON) { return } when (appFile) { @@ -486,7 +487,7 @@ class NoteEditorViewModel @Inject constructor( fun toggleSensitive() { val sensitive = savedStateHandle.getSensitive() - if (currentAccount.value?.instanceType == Account.InstanceType.MASTODON) { + if (_currentAccount.value?.instanceType == Account.InstanceType.MASTODON) { viewModelScope.launch { savedStateHandle.setFiles( savedStateHandle.getFiles().mapNotNull { appFile -> @@ -559,7 +560,7 @@ class NoteEditorViewModel @Inject constructor( file ) savedStateHandle.setFiles(files) - val account = currentAccount.value ?: return@launch + val account = _currentAccount.value ?: return@launch val localFile = when (file) { is AppFile.Local -> file is AppFile.Remote -> return@launch @@ -713,7 +714,7 @@ class NoteEditorViewModel @Inject constructor( return } viewModelScope.launch { - when (val account = currentAccount.value) { + when (val account = _currentAccount.value) { null -> Result.failure(UnauthorizedException()) else -> Result.success(account) }.mapCancellableCatching { account -> @@ -728,7 +729,7 @@ class NoteEditorViewModel @Inject constructor( fun onPastePostUrl(text: String, start: Int, beforeText: String, count: Int) = viewModelScope.launch { val urlText = text.substring(start, start + count) - val ca = currentAccount.value ?: return@launch + val ca = _currentAccount.value ?: return@launch val canQuote = canQuote() if (!canQuote) { @@ -758,7 +759,7 @@ class NoteEditorViewModel @Inject constructor( } suspend fun canQuote(): Boolean { - val ca = currentAccount.value ?: return false + val ca = _currentAccount.value ?: return false return instanceInfoService.find(ca.normalizedInstanceUri).map { it.canQuote }.getOrElse { false } @@ -795,23 +796,23 @@ class NoteEditorViewModel @Inject constructor( private suspend fun setAccount(account: Account) = runCancellableCatching { val result = noteEditorSwitchAccountExecutor( - currentAccount.value, + _currentAccount.value, noteEditorSendToState.value, account, ) - if (account.accountId != currentAccount.value?.accountId && currentAccount.value != null) { + if (account.accountId != _currentAccount.value?.accountId && _currentAccount.value != null) { savedStateHandle.setReplyId(result.replyId) savedStateHandle.setRenoteId(result.renoteId) savedStateHandle.setChannelId(result.channelId) } - if (currentAccount.value != null) { + if (_currentAccount.value != null) { savedStateHandle.setVisibility(null) } logger.debug { "currentAccount:${account.userName}@${account.getHost()}" } - currentAccount.value = account + _currentAccount.value = account } } From 001b5c3b50ebfe866ad087ff1c93b460de4137ea Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 21:06:05 +0900 Subject: [PATCH 262/432] =?UTF-8?q?feat:=20=E7=8F=BE=E5=9C=A8=E3=81=AE?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE=E3=82=A4?= =?UTF-8?q?=E3=83=B3=E3=82=B9=E3=82=BF=E3=83=B3=E3=82=B9=E3=81=AE=E3=82=AB?= =?UTF-8?q?=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=8C?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/emojis/CustomEmojiPickerDialog.kt | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt index f43a810271..86f040b74b 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/emojis/CustomEmojiPickerDialog.kt @@ -16,6 +16,16 @@ import javax.inject.Inject @AndroidEntryPoint class CustomEmojiPickerDialog : BottomSheetDialogFragment(), EmojiPickerFragment.OnEmojiSelectedListener{ + companion object { + fun newInstance(accountId: Long?): CustomEmojiPickerDialog { + return CustomEmojiPickerDialog().apply { + arguments = Bundle().apply { + putLong("ACCOUNT_ID", accountId ?: -1) + } + } + } + } + private var mSelectionViewModel: EmojiSelectionViewModel? = null @Inject @@ -24,6 +34,12 @@ class CustomEmojiPickerDialog : BottomSheetDialogFragment(), EmojiPickerFragment @Inject lateinit var metaRepository: MetaRepository + private val accountId: Long? by lazy(LazyThreadSafetyMode.NONE) { + requireArguments().getLong("ACCOUNT_ID").takeIf { + it > 0 + } + } + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -36,7 +52,7 @@ class CustomEmojiPickerDialog : BottomSheetDialogFragment(), EmojiPickerFragment super.onViewCreated(view, savedInstanceState) if (savedInstanceState == null) { childFragmentManager.beginTransaction().also { - it.add(R.id.fragmentBaseContainer, EmojiPickerFragment()) + it.add(R.id.fragmentBaseContainer, EmojiPickerFragment.newInstance(accountId)) }.commit() } } From 641c6c8244578fcb1e224e7201329616b29a48c0 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 21:08:29 +0900 Subject: [PATCH 263/432] =?UTF-8?q?feat:=20=E3=83=A6=E3=83=BC=E3=82=B6?= =?UTF-8?q?=E6=A4=9C=E7=B4=A2=E7=94=BB=E9=9D=A2=E3=81=AB=E7=8F=BE=E5=9C=A8?= =?UTF-8?q?=E3=81=AE=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AB?= =?UTF-8?q?=E7=B4=90=E3=81=A5=E3=81=84=E3=81=9F=E3=83=87=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../SearchAndSelectUserNavigation.kt | 3 ++- .../milktea/note/editor/NoteEditorFragment.kt | 11 ++++++--- .../activity/SearchAndSelectUserActivity.kt | 7 ++++-- .../user/search/SearchUserViewModel.kt | 23 +++++++++++++++---- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchAndSelectUserNavigation.kt b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchAndSelectUserNavigation.kt index 7f27a22fda..997aa856f9 100644 --- a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchAndSelectUserNavigation.kt +++ b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/SearchAndSelectUserNavigation.kt @@ -18,7 +18,8 @@ interface SearchAndSelectUserNavigation : ActivityNavigation = emptyList() + val selectedUserIds: List = emptyList(), + val accountId: Long? = null, ) data class ChangedDiffResult( diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt index 6824a0ac43..1f31a11452 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/NoteEditorFragment.kt @@ -355,7 +355,9 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti }, onSelectEmojiButtonClicked = { binding.cw.isFocused - CustomEmojiPickerDialog().show(childFragmentManager, "Editor") + CustomEmojiPickerDialog.newInstance( + uiState.currentAccount?.accountId + ).show(childFragmentManager, "Editor") }, onToggleCwButtonClicked = { noteEditorViewModel.changeCwEnabled() @@ -710,7 +712,8 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti val intent = searchAndUserNavigation.newIntent( SearchAndSelectUserNavigationArgs( - selectedUserIds = selectedUserIds + selectedUserIds = selectedUserIds, + accountId = noteEditorViewModel.currentAccount.value?.accountId, ) ) @@ -720,7 +723,9 @@ class NoteEditorFragment : Fragment(R.layout.fragment_note_editor), EmojiSelecti private fun startMentionToSearchAndSelectUser() { - val intent = searchAndUserNavigation.newIntent(SearchAndSelectUserNavigationArgs()) + val intent = searchAndUserNavigation.newIntent(SearchAndSelectUserNavigationArgs( + accountId = noteEditorViewModel.currentAccount.value?.accountId, + )) selectMentionToUserResult.launch(intent) } diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/SearchAndSelectUserActivity.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/SearchAndSelectUserActivity.kt index f795ee7842..e9dea09b92 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/SearchAndSelectUserActivity.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/SearchAndSelectUserActivity.kt @@ -30,7 +30,8 @@ class SearchAndSelectUserNavigationImpl @Inject constructor( return SearchAndSelectUserActivity.newIntent( activity, args.selectableMaximumSize, - args.selectedUserIds + args.selectedUserIds, + args.accountId, ) } } @@ -50,11 +51,13 @@ class SearchAndSelectUserActivity : AppCompatActivity() { fun newIntent( context: Context, selectableMaximumSize: Int = Int.MAX_VALUE, - selectedUserIds: List = emptyList() + selectedUserIds: List = emptyList(), + accountId: Long? = null, ): Intent { return Intent(context, SearchAndSelectUserActivity::class.java).apply { putExtra(EXTRA_SELECTABLE_MAXIMUM_SIZE, selectableMaximumSize) putExtra(EXTRA_SELECTED_USER_IDS, ArrayList(selectedUserIds)) + putExtra(SearchUserViewModel.EXTRA_ACCOUNT_ID, accountId) } } } diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/search/SearchUserViewModel.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/search/SearchUserViewModel.kt index 66a3c924ac..972f55fcf5 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/search/SearchUserViewModel.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/search/SearchUserViewModel.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.user.search +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -25,16 +26,31 @@ class SearchUserViewModel @Inject constructor( loggerFactory: Logger.Factory, private val userRepository: UserRepository, userDataSource: UserDataSource, + savedStateHandle: SavedStateHandle, ) : ViewModel() { + companion object { + const val EXTRA_ACCOUNT_ID = "SearchUserViewModel.EXTRA_ACCOUNT_ID" + } + private val logger = loggerFactory.create("SearchUserViewModel") private val searchUserRequests = MutableStateFlow(SearchUser("", null)) + @OptIn(ExperimentalCoroutinesApi::class) + private val account = savedStateHandle.getStateFlow(EXTRA_ACCOUNT_ID, null).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(it) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + @OptIn(ExperimentalCoroutinesApi::class) - private val syncByUserNameLoadingState = accountStore.observeCurrentAccount.filterNotNull() + private val syncByUserNameLoadingState = account.filterNotNull() .flatMapLatest { account -> searchUserRequests.flatMapLatest { query -> suspend { @@ -72,7 +88,6 @@ class SearchUserViewModel @Inject constructor( ) ) - private val account = accountStore.observeCurrentAccount.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) val uiState = combine(searchState, searchUserRequests, account) { state, request, a -> SearchUserUiState(request, state, a) @@ -91,7 +106,7 @@ class SearchUserViewModel @Inject constructor( (it.content as? StateContent.Exist)?.rawContent ?: emptyList() }.flatMapLatest { ids -> - userDataSource.observeIn(accountStore.currentAccountId!!, ids.map { it.id }).map { users -> + userDataSource.observeIn(account.value?.accountId!!, ids.map { it.id }).map { users -> users.mapNotNull { user -> user as? User.Detail? } @@ -100,7 +115,7 @@ class SearchUserViewModel @Inject constructor( logger.error("observe error", it) }.stateIn(viewModelScope, SharingStarted.Lazily, emptyList()) - val currentAccount = accountStore.observeCurrentAccount.stateIn( + val currentAccount = account.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), null From e7732d67d1a91fadfbc9140e3e826f792f996bda Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 28 May 2023 22:08:04 +0900 Subject: [PATCH 264/432] =?UTF-8?q?feat:=20=E5=A4=96=E9=83=A8=E3=81=8B?= =?UTF-8?q?=E3=82=89Account=E3=81=AEId=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/activity/UserDetailActivity.kt | 10 +++++- .../user/viewmodel/UserDetailViewModel.kt | 32 ++++++++++++------- .../main/res/layout/activity_user_detail.xml | 4 +-- 3 files changed, 31 insertions(+), 15 deletions(-) diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt index b569a753b8..9ed6945999 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/activity/UserDetailActivity.kt @@ -476,6 +476,7 @@ class UserTimelinePagerAdapterV2( Pageable.Gallery.User(tab.userId.id), ) is UserDetailTabType.Media -> pageableFragmentFactory.create( + tab.userId.accountId, Pageable.UserTimeline( tab.userId.id, withFiles = true @@ -484,46 +485,53 @@ class UserTimelinePagerAdapterV2( is UserDetailTabType.PinNote -> userPinnedNotesFragmentFactory.create(tab.userId) is UserDetailTabType.Reactions -> UserReactionsFragment.newInstance(tab.userId) is UserDetailTabType.UserTimeline -> pageableFragmentFactory.create( + tab.userId.accountId, Pageable.UserTimeline( tab.userId.id, includeReplies = false ) ) is UserDetailTabType.UserTimelineWithReplies -> pageableFragmentFactory.create( + tab.userId.accountId, Pageable.UserTimeline( tab.userId.id, includeReplies = true ) ) is UserDetailTabType.MastodonMedia -> pageableFragmentFactory.create( + tab.userId.accountId, Pageable.Mastodon.UserTimeline( tab.userId.id, isOnlyMedia = true, ) ) is UserDetailTabType.MastodonUserTimeline -> pageableFragmentFactory.create( + tab.userId.accountId, Pageable.Mastodon.UserTimeline( tab.userId.id, excludeReplies = true, ) ) is UserDetailTabType.MastodonUserTimelineWithReplies -> pageableFragmentFactory.create( + tab.userId.accountId, Pageable.Mastodon.UserTimeline( tab.userId.id, excludeReplies = false, ) ) is UserDetailTabType.MastodonUserTimelineOnlyPosts -> pageableFragmentFactory.create( + tab.userId.accountId, Pageable.Mastodon.UserTimeline( tab.userId.id, excludeReblogs = true, ) ) is UserDetailTabType.UserTimelineOnlyPosts -> pageableFragmentFactory.create( + tab.userId.accountId, Pageable.UserTimeline( tab.userId.id, includeMyRenotes = false, - ) + ), ) } diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt index 2beb67fc12..8942672e17 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/viewmodel/UserDetailViewModel.kt @@ -23,7 +23,6 @@ import net.pantasystem.milktea.common_android.eventbus.EventBus import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository -import net.pantasystem.milktea.model.account.CurrentAccountWatcher import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.account.page.PageableTemplate import net.pantasystem.milktea.model.instance.FeatureEnables @@ -63,7 +62,16 @@ class UserDetailViewModel @AssistedInject constructor( companion object; private val logger = loggerFactory.create("UserDetailViewModel") - private val accountWatcher = CurrentAccountWatcher(userId?.accountId, accountRepository) + private val currentAccountId = MutableStateFlow(userId?.accountId) + + @OptIn(ExperimentalCoroutinesApi::class) + val currentAccount = currentAccountId.flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(it) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) private val _errors = MutableSharedFlow(extraBufferCapacity = 100) val errors = _errors.asSharedFlow() @@ -75,7 +83,7 @@ class UserDetailViewModel @AssistedInject constructor( userDataSource.observe(userId) } fqdnUserName != null -> { - accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { + currentAccount.filterNotNull().flatMapLatest { userDataSource.observe(it.accountId, fqdnUserName) } } @@ -90,14 +98,9 @@ class UserDetailViewModel @AssistedInject constructor( }.stateIn(viewModelScope, SharingStarted.Lazily, null) val user = userState.asLiveData() - @OptIn(ExperimentalCoroutinesApi::class) - val account = accountWatcher.account.catch { - logger.error("Accountの取得に失敗", it) - }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) - - val isMine = combine(userState, accountStore.state) { userState, accountState -> - userState?.id?.id == accountState.currentAccount?.remoteId + val isMine = combine(userState, currentAccount) { userState, account -> + userState?.id?.id == account?.remoteId }.stateIn(viewModelScope, SharingStarted.Lazily, false) val birthday = userState.map { @@ -113,7 +116,7 @@ class UserDetailViewModel @AssistedInject constructor( }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) val tabTypes = combine( - accountStore.observeCurrentAccount.filterNotNull(), userState.filterNotNull() + currentAccount.filterNotNull(), userState.filterNotNull() ) { account, user -> val isEnableGallery = featureEnables.isEnable(account.normalizedInstanceUri, FeatureType.Gallery) @@ -164,6 +167,11 @@ class UserDetailViewModel @AssistedInject constructor( "userIdかfqdnUserNameのいずれかが指定されている必要があります。" } sync() + accountStore.observeCurrentAccount.onEach { + if (userId == null) { + currentAccountId.value = it?.accountId + } + }.launchIn(viewModelScope) } @@ -340,7 +348,7 @@ class UserDetailViewModel @AssistedInject constructor( return userId } - val account = accountWatcher.getAccount() + val account = currentAccount.value ?: accountRepository.getCurrentAccount().getOrThrow() if (fqdnUserName != null) { val (userName, host) = Acct(fqdnUserName).let { it.userName to it.host diff --git a/modules/features/user/src/main/res/layout/activity_user_detail.xml b/modules/features/user/src/main/res/layout/activity_user_detail.xml index ded8dd58e4..eccd072e21 100644 --- a/modules/features/user/src/main/res/layout/activity_user_detail.xml +++ b/modules/features/user/src/main/res/layout/activity_user_detail.xml @@ -30,7 +30,7 @@ android:layout_height="wrap_content" android:layout_marginTop="?attr/actionBarSize" app:user="@{userViewModel.user}" - app:account="@{userViewModel.account}" + app:account="@{userViewModel.currentAccount}" app:mainNameView="@{mainName}" app:subNameView="@{subName}" > @@ -190,7 +190,7 @@ android:layout_height="wrap_content" android:layout_alignStart="@id/avatarIcon" app:sourceText="@{userViewModel.user.info.description}" - app:account="@{userViewModel.account}" + app:account="@{userViewModel.currentAccount}" app:host="@{userViewModel.user.host}" app:emojis="@{userViewModel.user.emojis}" tools:text="awoijfoiwaehfoaiwehfoiawjefoiawjefiojawioefjioawhfoiawehfoiawef" From dc5c832630fa907b87b7ddae38aed0da0cef4b76 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 19:40:55 +0900 Subject: [PATCH 265/432] =?UTF-8?q?feat:=20=E4=BB=96=E3=82=A2=E3=82=AB?= =?UTF-8?q?=E3=82=A6=E3=83=B3=E3=83=88=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=82=BF=E3=83=96=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=99?= =?UTF-8?q?=E3=82=8B=E6=A9=9F=E8=83=BD=E3=81=A7=E6=AD=A3=E3=81=97=E3=81=8F?= =?UTF-8?q?=E5=8B=95=E4=BD=9C=E3=81=97=E3=81=AA=E3=81=84=E9=A0=85=E7=9B=AE?= =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../viewmodel/page/PageCandidateGenerator.kt | 113 +++++++++++------- .../viewmodel/page/PageSettingViewModel.kt | 2 +- 2 files changed, 72 insertions(+), 43 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt index 30eaf058c6..95905d2c7b 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt @@ -14,137 +14,164 @@ class PageCandidateGenerator @Inject constructor( private val nodeInfoRepository: NodeInfoRepository, ) { - suspend fun createPageCandidates(account: Account): List { - val nodeInfo = nodeInfoRepository.find(account.getHost()).getOrNull() + suspend fun createPageCandidates( + related: Account, + currentAccount: Account?, + ): List { + val nodeInfo = nodeInfoRepository.find(related.getHost()).getOrNull() val version = nodeInfo?.type?.getVersion() ?: Version("0") val isCalckey = nodeInfo?.type is NodeInfo.SoftwareType.Misskey.Calckey - return when (account.instanceType) { + + val isSameAccount = related.accountId == currentAccount?.accountId || currentAccount == null + val restrictionTypes = setOf( + PageType.ANTENNA, + PageType.NOTIFICATION, + PageType.USER_LIST, + PageType.CHANNEL_TIMELINE, + PageType.CLIP_NOTES, + PageType.SEARCH, + PageType.SEARCH, + PageType.SEARCH_HASH, + PageType.USER, + PageType.DETAIL, + PageType.GALLERY_FEATURED, + PageType.GALLERY_POPULAR, + PageType.GALLERY_POSTS, + PageType.MY_GALLERY_POSTS, + PageType.I_LIKED_GALLERY_POSTS, + PageType.USERS_GALLERY_POSTS, + PageType.MASTODON_LIST_TIMELINE + ) + return when (related.instanceType) { Account.InstanceType.MISSKEY -> { listOfNotNull( PageCandidate( - account, + related, PageType.HOME, StringSource(R.string.home_timeline) ), PageCandidate( - account, + related, PageType.LOCAL, StringSource(R.string.local_timeline) ), PageCandidate( - account, + related, PageType.SOCIAL, StringSource(R.string.hybrid_timeline) ), PageCandidate( - account, + related, PageType.GLOBAL, StringSource(R.string.global_timeline) ), if (isCalckey) PageCandidate( - account, + related, PageType.CALCKEY_RECOMMENDED_TIMELINE, StringSource(R.string.calckey_recomended_timeline) ) else null, if (version >= Version("12")) PageCandidate( - account, + related, PageType.ANTENNA, StringSource(R.string.antenna) ) else null, PageCandidate( - account, + related, PageType.NOTIFICATION, StringSource(R.string.notification) ), PageCandidate( - account, + related, PageType.USER_LIST, StringSource(R.string.user_list) ), PageCandidate( - account, + related, PageType.MENTION, StringSource(R.string.mention) ), PageCandidate( - account, + related, PageType.FAVORITE, StringSource(R.string.favorite) ), if (version >= Version("12")) PageCandidate( - account, + related, PageType.CHANNEL_TIMELINE, StringSource(R.string.channel) ) else null, if (version >= Version("12")) PageCandidate( - account, + related, PageType.CLIP_NOTES, StringSource(R.string.clip) ) else null, PageCandidate( - account, + related, PageType.SEARCH, StringSource(R.string.search) ), PageCandidate( - account, + related, PageType.SEARCH_HASH, StringSource(R.string.tag) ), PageCandidate( - account, + related, PageType.FEATURED, StringSource(R.string.featured) ), PageCandidate( - account, + related, PageType.USER, StringSource(R.string.user) ), PageCandidate( - account, + related, PageType.DETAIL, StringSource(R.string.detail) ), ) + if (version >= Version("12.75.0")) { listOf( PageCandidate( - account, + related, PageType.GALLERY_FEATURED, - StringSource(R.string.featured) + StringSource("(") + StringSource(R.string.gallery) + StringSource(")") + StringSource(R.string.featured) + StringSource("(") + StringSource(R.string.gallery) + StringSource( + ")" + ) ), PageCandidate( - account, + related, PageType.GALLERY_POPULAR, StringSource(R.string.popular_posts) + StringSource("(") + StringSource( - R.string.gallery) + StringSource(")") + R.string.gallery + ) + StringSource(")") ), PageCandidate( - account, + related, PageType.GALLERY_POSTS, StringSource(R.string.gallery), ), PageCandidate( - account, + related, PageType.MY_GALLERY_POSTS, - StringSource(R.string.my_posts) + StringSource("(") + StringSource(R.string.gallery) + StringSource(")"), + StringSource(R.string.my_posts) + StringSource("(") + StringSource(R.string.gallery) + StringSource( + ")" + ), ), PageCandidate( - account, + related, PageType.USERS_GALLERY_POSTS, StringSource(R.string.gallery) + StringSource("(User)") ), PageCandidate( - account, + related, PageType.I_LIKED_GALLERY_POSTS, - StringSource(R.string.my_liking) + StringSource("(") + StringSource(R.string.gallery) + StringSource(")"), + StringSource(R.string.my_liking) + StringSource("(") + StringSource(R.string.gallery) + StringSource( + ")" + ), ), - PageCandidate( - account, - PageType.MY_GALLERY_POSTS, - StringSource(R.string.my_posts) + StringSource("(") + StringSource(R.string.gallery) + StringSource(")"), + ) - ) } else { emptyList() } @@ -152,43 +179,45 @@ class PageCandidateGenerator @Inject constructor( Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> { listOf( PageCandidate( - account, + related, PageType.MASTODON_HOME_TIMELINE, StringSource(R.string.home_timeline) ), PageCandidate( - account, + related, PageType.MASTODON_LOCAL_TIMELINE, StringSource(R.string.local_timeline) ), PageCandidate( - account, + related, PageType.MASTODON_PUBLIC_TIMELINE, StringSource(R.string.global_timeline), ), PageCandidate( - account, + related, PageType.NOTIFICATION, StringSource(R.string.notification) ), PageCandidate( - account, + related, PageType.FAVORITE, StringSource(R.string.favorite) ), // PageType.MASTODON_HASHTAG_TIMELINE, PageCandidate( - account, + related, PageType.MASTODON_LIST_TIMELINE, StringSource(R.string.list), ), PageCandidate( - account, + related, PageType.MASTODON_BOOKMARK_TIMELINE, StringSource(R.string.bookmark) ) ) } + }.filter { + isSameAccount || !restrictionTypes.contains(it.type) } } diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt index 0b13a1f1a9..2164b43430 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt @@ -55,7 +55,7 @@ class PageSettingViewModel @Inject constructor( PageCandidateGroup( ca, it, - pageCandidateGenerator.createPageCandidates(it) + pageCandidateGenerator.createPageCandidates(it, ca) ) } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) From 2d37ef1d79414bf577fbd824665b41327f958867 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 19:59:36 +0900 Subject: [PATCH 266/432] =?UTF-8?q?feat:=20=E4=BB=BB=E6=84=8F=E3=81=AE?= =?UTF-8?q?=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=82=92=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/channel/ChannelViewModel.kt | 52 +++++++++++++++++-- 1 file changed, 47 insertions(+), 5 deletions(-) diff --git a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt index 7501d3d537..dc8f054098 100644 --- a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt +++ b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.channel +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -14,6 +15,7 @@ import net.pantasystem.milktea.common.paginator.PreviousPagingController import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.data.infrastructure.channel.ChannelListType import net.pantasystem.milktea.data.infrastructure.channel.ChannelPagingModel +import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.account.page.newPage @@ -28,8 +30,14 @@ class ChannelViewModel @Inject constructor( private val accountRepository: AccountRepository, channelPagingModelFactory: ChannelPagingModel.Factory, loggerFactory: Logger.Factory, + private val savedStateHandle: SavedStateHandle, ) : ViewModel() { + companion object { + const val EXTRA_SPECIFIED_ACCOUNT_ID = "ChannelViewModel.EXTRA_SPECIFIED_ACCOUNT_ID" + const val EXTRA_ADD_TAB_TO_ACCOUNT_ID = "ChannelViewModel.EXTRA_ADD_TAB_TO_ACCOUNT_ID" + } + val logger: Logger by lazy { loggerFactory.create("ChannelViewModel") } @@ -37,16 +45,16 @@ class ChannelViewModel @Inject constructor( private val featuredChannelPagingModel = channelPagingModelFactory.create(ChannelListType.FEATURED) { - accountRepository.getCurrentAccount().getOrThrow() + getAccount() } private val followedChannelPagingModel = channelPagingModelFactory.create(ChannelListType.FOLLOWED) { - accountRepository.getCurrentAccount().getOrThrow() + getAccount() } private val ownedChannelPagingModel = channelPagingModelFactory.create(ChannelListType.OWNED) { - accountRepository.getCurrentAccount().getOrThrow() + getAccount() } val uiState = combine( @@ -107,11 +115,13 @@ class ChannelViewModel @Inject constructor( fun toggleTab(channelId: Channel.Id) { viewModelScope.launch { runCancellableCatching { - val account = accountRepository.get(channelId.accountId).getOrThrow() + val account = getAddTabToAccount() val channel = channelRepository.findOne(channelId).getOrThrow() val page = account.newPage( Pageable.ChannelTimeline(channelId = channelId.channelId), - channel.name + channel.name, + ).copy( + attachedAccountId = getSpecifiedAccountId(), ) val first = account.pages.firstOrNull { (it.pageable() as? Pageable.ChannelTimeline)?.channelId == channelId.channelId } @@ -123,6 +133,38 @@ class ChannelViewModel @Inject constructor( } } } +// +// fun setSpecifiedAccountId(accountId: Long) { +// savedStateHandle[EXTRA_SPECIFIED_ACCOUNT_ID] = accountId +// } +// +// fun setAddTabToAccountId(accountId: Long) { +// savedStateHandle[EXTRA_ADD_TAB_TO_ACCOUNT_ID] = accountId +// } +// + private fun getSpecifiedAccountId(): Long? { + return savedStateHandle[EXTRA_SPECIFIED_ACCOUNT_ID] + } + + private fun getAddToTabAccountId(): Long? { + return savedStateHandle[EXTRA_ADD_TAB_TO_ACCOUNT_ID] + } + + private suspend fun getAccount(): Account { + val accountId = getSpecifiedAccountId() + if (accountId != null) { + return accountRepository.get(accountId).getOrThrow() + } + return accountRepository.getCurrentAccount().getOrThrow() + } + + private suspend fun getAddTabToAccount(): Account { + val accountId = getAddToTabAccountId() + if (accountId != null) { + return accountRepository.get(accountId).getOrThrow() + } + return accountRepository.getCurrentAccount().getOrThrow() + } } data class ChannelListUiState( From a4ac7c76740b285f8f7120337456fb7c6d76ce3c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 20:14:43 +0900 Subject: [PATCH 267/432] =?UTF-8?q?feat:=20=E5=88=A4=E5=AE=9A=E6=9D=A1?= =?UTF-8?q?=E4=BB=B6=E3=81=AA=E3=81=A9=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/channel/ChannelListStatePage.kt | 10 +- .../milktea/channel/ChannelScreen.kt | 1 - .../milktea/channel/ChannelViewModel.kt | 118 +++++++++++++++--- 3 files changed, 104 insertions(+), 25 deletions(-) diff --git a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelListStatePage.kt b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelListStatePage.kt index 5c909b61cc..4991d33b62 100644 --- a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelListStatePage.kt +++ b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelListStatePage.kt @@ -14,12 +14,10 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import net.pantasystem.milktea.common.PageableState import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.data.infrastructure.channel.ChannelListType -import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.channel.Channel @Composable fun ChannelListStateScreen( - account: Account, uiState: ChannelListUiState, listType: ChannelListType, viewModel: ChannelViewModel, @@ -48,11 +46,9 @@ fun ChannelListStateScreen( is StateContent.Exist -> { items(content.rawContent.size) { index -> val channel = content.rawContent[index] - val isPaged = - account.pages.any { it.pageParams.channelId == channel.id.channelId } ChannelCard( - channel = channel, - isPaged = isPaged, + channel = channel.channel, + isPaged = channel.isAddedTab, onAction = { when (it) { is ChannelCardAction.OnToggleTabButtonClicked -> { @@ -65,7 +61,7 @@ fun ChannelListStateScreen( viewModel.follow(it.channel.id) } is ChannelCardAction.OnClick -> { - navigateToDetailView.invoke(channel.id) + navigateToDetailView.invoke(channel.channel.id) } } } diff --git a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelScreen.kt b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelScreen.kt index c95c843060..ae1088686f 100644 --- a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelScreen.kt +++ b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelScreen.kt @@ -88,7 +88,6 @@ fun ChannelScreen( HorizontalPager(state = pagerState, modifier = Modifier.padding(padding)) { ChannelListStateScreen( listType = channelTypeWithTitleList[pagerState.currentPage].type, - account = currentAccount!!, viewModel = channelViewModel, navigateToDetailView = onNavigateChannelDetail, uiState = uiState diff --git a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt index dc8f054098..de2fca8d8f 100644 --- a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt +++ b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt @@ -4,9 +4,8 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.Logger @@ -57,15 +56,43 @@ class ChannelViewModel @Inject constructor( getAccount() } + @OptIn(ExperimentalCoroutinesApi::class) + private val currentAccount = savedStateHandle.getStateFlow( + EXTRA_SPECIFIED_ACCOUNT_ID, + null + ).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(accountId) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + @OptIn(ExperimentalCoroutinesApi::class) + private val tabToAddAccount = savedStateHandle.getStateFlow( + EXTRA_ADD_TAB_TO_ACCOUNT_ID, + null + ).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(it) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + val uiState = combine( featuredChannelPagingModel.observeChannels(), followedChannelPagingModel.observeChannels(), - ownedChannelPagingModel.observeChannels() - ) { featured, followed, owned -> - ChannelListUiState( - featuredChannels = featured, - followedChannels = followed, - ownedChannels = owned, + ownedChannelPagingModel.observeChannels(), + currentAccount, + tabToAddAccount, + ) { featured, followed, owned, currentAccount, tabToAddAccount -> + ChannelListUiState.from( + featured = featured, + followed = followed, + owned = owned, + currentAccount = currentAccount, + tabToAddAccount = tabToAddAccount, ) }.stateIn( viewModelScope, @@ -74,10 +101,9 @@ class ChannelViewModel @Inject constructor( ) - fun clearAndLoad(type: ChannelListType) { viewModelScope.launch { - val model = when(type) { + val model = when (type) { ChannelListType.OWNED -> ownedChannelPagingModel ChannelListType.FOLLOWED -> followedChannelPagingModel ChannelListType.FEATURED -> featuredChannelPagingModel @@ -133,7 +159,8 @@ class ChannelViewModel @Inject constructor( } } } -// + + // // fun setSpecifiedAccountId(accountId: Long) { // savedStateHandle[EXTRA_SPECIFIED_ACCOUNT_ID] = accountId // } @@ -168,12 +195,65 @@ class ChannelViewModel @Inject constructor( } data class ChannelListUiState( - val featuredChannels: PageableState> = PageableState.Loading.Init(), - val followedChannels: PageableState> = PageableState.Loading.Init(), - val ownedChannels: PageableState> = PageableState.Loading.Init(), + val currentAccount: Account? = null, + val featuredChannels: PageableState> = PageableState.Loading.Init(), + val followedChannels: PageableState> = PageableState.Loading.Init(), + val ownedChannels: PageableState> = PageableState.Loading.Init(), ) { - fun getByType(type: ChannelListType): PageableState> { - return when(type) { + + companion object { + fun from( + featured: PageableState>, + followed: PageableState>, + owned: PageableState>, + currentAccount: Account?, + tabToAddAccount: Account?, + ): ChannelListUiState { + return ChannelListUiState( + currentAccount = currentAccount, + featuredChannels = featured.convert { list -> + list.map { channel -> + ChannelListItem( + channel, + isAddedTab = tabToAddAccount?.pages?.any { page -> + page.pageParams.channelId == channel.id.channelId + && channel.id.accountId == ( + page.attachedAccountId ?: page.accountId) + } ?: false + ) + } + }, + followedChannels = followed.convert { list -> + list.map { channel -> + ChannelListItem( + channel, + isAddedTab = tabToAddAccount?.pages?.any { page -> + page.pageParams.channelId == channel.id.channelId + && channel.id.accountId == ( + page.attachedAccountId ?: page.accountId) + } ?: false + ) + } + }, + ownedChannels = owned.convert { list -> + list.map { channel -> + ChannelListItem( + channel, + isAddedTab = tabToAddAccount?.pages?.any { page -> + page.pageParams.channelId == channel.id.channelId + && channel.id.accountId == ( + page.attachedAccountId ?: page.accountId) + } ?: false + ) + + } + }, + ) + } + } + + fun getByType(type: ChannelListType): PageableState> { + return when (type) { ChannelListType.OWNED -> ownedChannels ChannelListType.FOLLOWED -> followedChannels ChannelListType.FEATURED -> featuredChannels @@ -181,3 +261,7 @@ data class ChannelListUiState( } } +data class ChannelListItem( + val channel: Channel, + val isAddedTab: Boolean, +) From 3167a7b3cbe0d12f871b4376ccef3329dd5a531d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 20:24:20 +0900 Subject: [PATCH 268/432] =?UTF-8?q?feat:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_navigation/ChannelNavigation.kt | 6 +++++- .../net/pantasystem/milktea/channel/ChannelActivity.kt | 8 ++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/ChannelNavigation.kt b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/ChannelNavigation.kt index 21ac8749d6..7dc2ce54be 100644 --- a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/ChannelNavigation.kt +++ b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/ChannelNavigation.kt @@ -2,6 +2,10 @@ package net.pantasystem.milktea.common_navigation import net.pantasystem.milktea.model.channel.Channel -interface ChannelNavigation : ActivityNavigation +interface ChannelNavigation : ActivityNavigation +data class ChannelNavigationArgs( + val specifiedAccountId: Long? = null, + val addTabToAccountId: Long? = null, +) interface ChannelDetailNavigation : ActivityNavigation \ No newline at end of file diff --git a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelActivity.kt b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelActivity.kt index c27670e2ba..6d4b416564 100644 --- a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelActivity.kt +++ b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelActivity.kt @@ -24,6 +24,7 @@ import net.pantasystem.milktea.common.ui.ApplyTheme import net.pantasystem.milktea.common_android_ui.PageableFragmentFactory import net.pantasystem.milktea.common_navigation.ChannelDetailNavigation import net.pantasystem.milktea.common_navigation.ChannelNavigation +import net.pantasystem.milktea.common_navigation.ChannelNavigationArgs import net.pantasystem.milktea.common_viewmodel.confirm.ConfirmViewModel import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.channel.Channel @@ -129,8 +130,11 @@ class ChannelActivity : AppCompatActivity() { class ChannelNavigationImpl @Inject constructor(val activity: Activity) : ChannelNavigation { - override fun newIntent(args: Unit): Intent { - return Intent(activity, ChannelActivity::class.java) + override fun newIntent(args: ChannelNavigationArgs): Intent { + return Intent(activity, ChannelActivity::class.java).also { intent -> + intent.putExtra(ChannelViewModel.EXTRA_SPECIFIED_ACCOUNT_ID, args.specifiedAccountId) + intent.putExtra(ChannelViewModel.EXTRA_ADD_TAB_TO_ACCOUNT_ID, args.addTabToAccountId) + } } } From 5ad4c850fbd8744ab91df50a7ea1b790b04a7a32 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 20:24:55 +0900 Subject: [PATCH 269/432] =?UTF-8?q?feat:=20=E9=81=B7=E7=A7=BB=E6=99=82?= =?UTF-8?q?=E3=81=AB=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE?= =?UTF-8?q?=E6=83=85=E5=A0=B1=E3=82=92=E6=B8=A1=E3=81=99=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/setting/activities/PageSettingActivity.kt | 9 +++++++-- .../setting/viewmodel/page/PageCandidateGenerator.kt | 1 - .../setting/viewmodel/page/PageSettingViewModel.kt | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt index 726f6d2274..05f7264fbd 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt @@ -66,7 +66,7 @@ class PageSettingActivity : AppCompatActivity() { } // mPageSettingViewModel.pageAddedEvent.observe(this) { pt -> - when (pt) { + when (pt.type) { PageType.SEARCH, PageType.SEARCH_HASH, PageType.MASTODON_HASHTAG_TIMELINE -> startActivity( searchNavigation.newIntent(SearchNavType.SearchScreen()) ) @@ -99,7 +99,12 @@ class PageSettingActivity : AppCompatActivity() { launchSearchAndSelectUserForAddGalleryTab.launch(intent) } PageType.CHANNEL_TIMELINE -> { - val intent = channelNavigation.newIntent(Unit) + val intent = channelNavigation.newIntent( + ChannelNavigationArgs( + specifiedAccountId = pt.relatedAccount.accountId, + addTabToAccountId = mPageSettingViewModel.account.value?.accountId + ) + ) startActivity(intent) } else -> { diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt index 95905d2c7b..4974ec66f6 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt @@ -27,7 +27,6 @@ class PageCandidateGenerator @Inject constructor( PageType.ANTENNA, PageType.NOTIFICATION, PageType.USER_LIST, - PageType.CHANNEL_TIMELINE, PageType.CLIP_NOTES, PageType.SEARCH, PageType.SEARCH, diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt index 2164b43430..c10416efdc 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt @@ -37,7 +37,7 @@ class PageSettingViewModel @Inject constructor( val account = accountStore.observeCurrentAccount.stateIn(viewModelScope, SharingStarted.Eagerly, null) - val pageAddedEvent = EventBus() + val pageAddedEvent = EventBus() val pageOnActionEvent = EventBus() @@ -177,7 +177,7 @@ class PageSettingViewModel @Inject constructor( override fun add(type: PageCandidate) { - pageAddedEvent.event = type.type + pageAddedEvent.event = type val name = pageTypeNameMap.get(type.type) when (type.type) { PageType.GLOBAL -> { From 1f62a5981a8a2b35f73883e338034139f95147d3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 20:29:04 +0900 Subject: [PATCH 270/432] =?UTF-8?q?fix:=20=E8=A9=B3=E7=B4=B0=E7=94=BB?= =?UTF-8?q?=E9=9D=A2=E3=81=8C=E8=A1=A8=E7=A4=BA=E3=81=95=E3=82=8C=E3=81=AA?= =?UTF-8?q?=E3=81=84=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/channel/ChannelActivity.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelActivity.kt b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelActivity.kt index 6d4b416564..f99fb9c180 100644 --- a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelActivity.kt +++ b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelActivity.kt @@ -108,12 +108,14 @@ class ChannelActivity : AppCompatActivity() { startActivity( NoteEditorActivity.newBundle( this@ChannelActivity, - channelId = it + channelId = it, + accountId = it.accountId, ) ) }, onUpdateFragment = { id, layout, channelId -> val fragment = pageableFragmentFactory.create( + channelId.accountId, Pageable.ChannelTimeline(channelId.channelId) ) val ft = supportFragmentManager.beginTransaction() From 4933bd521bd8bb0b37f19656ae472bc619db7202 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 20:55:39 +0900 Subject: [PATCH 271/432] =?UTF-8?q?feat:=20acct=E3=82=92=E5=8F=96=E5=BE=97?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/model/account/Account.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt index 7018d4627c..3250d5a892 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt @@ -56,6 +56,10 @@ data class Account( } } + fun getAcct(): String { + return "@$userName@${getHost()}}" + } + } internal object UrlHelper { From 6156f735f237ecfc4e6c8617a61eac1ec3a85257 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 20:56:41 +0900 Subject: [PATCH 272/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=83=96=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B=E5=85=88=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=82=92=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../userlist/viewmodel/ListListViewModel.kt | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt index a9fe1c8e84..d20ca6f6a1 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.userlist.viewmodel +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel @@ -26,16 +27,45 @@ class ListListViewModel @Inject constructor( private val userListRepository: UserListRepository, private val userRepository: UserRepository, private val toggleAddToTabUseCase: UserListTabToggleAddToTabUseCase, + private val savedStateHandle: SavedStateHandle, ) : ViewModel() { + companion object { + const val EXTRA_SPECIFIED_ACCOUNT_ID = "ListListViewModel.EXTRA_SPECIFIED_ACCOUNT_ID" + const val EXTRA_ADD_TAB_TO_ACCOUNT_ID = "ListListViewModel.EXTRA_ADD_TAB_TO_ACCOUNT_ID" + } + + + private val currentAccount = savedStateHandle.getStateFlow( + EXTRA_SPECIFIED_ACCOUNT_ID, + null + ).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(it) + ?: state.currentAccount + } + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + private val addTabToAccount = savedStateHandle.getStateFlow( + EXTRA_ADD_TAB_TO_ACCOUNT_ID, + null + ).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { accountId -> + state.get(accountId) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) private val userListsFlow = - accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { account -> + currentAccount.filterNotNull().flatMapLatest { account -> userListRepository.observeByAccountId(account.accountId) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) private val userListsSyncState = - accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { + currentAccount.filterNotNull().flatMapLatest { suspend { userListRepository.syncByAccountId(it.accountId).getOrThrow() }.asLoadingStateFlow() @@ -48,17 +78,19 @@ class ListListViewModel @Inject constructor( private val addTargetUserId = MutableStateFlow(null) val uiState = combine( - accountStore.observeCurrentAccount, + currentAccount, + addTabToAccount, userListsFlow, userListsSyncState, addTargetUserId - ) { ac, userLists, syncState, addUser -> + ) { ac, addTabToAccount, userLists, syncState, addUser -> UserListsUiState( userLists.map { userList -> UserListBindingModel( userList, - ac?.pages?.any { + addTabToAccount?.pages?.any { it.pageParams.listId == userList.userList.id.userListId + || userList.userList.id.accountId == (it.attachedAccountId ?: it.accountId) } ?: false, isTargetUserAdded = userList.userList.userIds.any { id -> id == addUser @@ -106,7 +138,7 @@ class ListListViewModel @Inject constructor( fun toggleTab(userList: UserList?) { userList?.let { ul -> viewModelScope.launch { - toggleAddToTabUseCase(ul.id).onFailure { + toggleAddToTabUseCase(ul.id, savedStateHandle[EXTRA_ADD_TAB_TO_ACCOUNT_ID]).onFailure { logger.error("タブtoggle処理失敗", e = it) } } From eb5d03a4026917b51b7e91030c39ed0b38406067 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 20:56:43 +0900 Subject: [PATCH 273/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=83=96=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B=E5=85=88=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=82=92=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/list/UserListTabToggleAddToTabUseCase.kt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/list/UserListTabToggleAddToTabUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/list/UserListTabToggleAddToTabUseCase.kt index a17c41c362..2a9cc6afa9 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/list/UserListTabToggleAddToTabUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/list/UserListTabToggleAddToTabUseCase.kt @@ -17,20 +17,24 @@ class UserListTabToggleAddToTabUseCase @Inject constructor( private val accountService: AccountService, ): UseCase { - suspend operator fun invoke(listId: UserList.Id) = runCancellableCatching { - val account = accountRepository.get(listId.accountId) + suspend operator fun invoke(listId: UserList.Id, addTabToAccountId: Long? = null) = runCancellableCatching { + val account = accountRepository.get(addTabToAccountId ?: listId.accountId) .getOrThrow() - val page = account.pages.firstOrNull { - it.pageParams.listId == listId.userListId + val page = account.pages.firstOrNull { page -> + page.pageParams.listId == listId.userListId + && listId.accountId == (page.attachedAccountId ?: page.accountId) } + val relatedAccount = accountRepository.get(listId.accountId).getOrThrow() + if (page == null) { val userList = userListRepository.findOne(listId) accountService.add( Page( account.accountId, - userList.name, + if (addTabToAccountId == null) userList.name else "${userList.name}(${relatedAccount.getAcct()})", weight = -1, + attachedAccountId = if (addTabToAccountId == null) null else relatedAccount.accountId, pageable = when(account.instanceType) { Account.InstanceType.MISSKEY -> Pageable.UserListTimeline( listId.userListId From 80397d0074fc3aa14977fa4415684fb69b822752 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 21:02:38 +0900 Subject: [PATCH 274/432] fix --- .../milktea/userlist/viewmodel/ListListViewModel.kt | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt index d20ca6f6a1..32989ac5f8 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt @@ -18,7 +18,6 @@ import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserRepository import javax.inject.Inject -@ExperimentalCoroutinesApi @HiltViewModel class ListListViewModel @Inject constructor( val accountStore: AccountStore, @@ -36,6 +35,7 @@ class ListListViewModel @Inject constructor( } + @OptIn(ExperimentalCoroutinesApi::class) private val currentAccount = savedStateHandle.getStateFlow( EXTRA_SPECIFIED_ACCOUNT_ID, null @@ -48,6 +48,7 @@ class ListListViewModel @Inject constructor( } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + @OptIn(ExperimentalCoroutinesApi::class) private val addTabToAccount = savedStateHandle.getStateFlow( EXTRA_ADD_TAB_TO_ACCOUNT_ID, null @@ -59,11 +60,13 @@ class ListListViewModel @Inject constructor( } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + @OptIn(ExperimentalCoroutinesApi::class) private val userListsFlow = currentAccount.filterNotNull().flatMapLatest { account -> userListRepository.observeByAccountId(account.accountId) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) + @OptIn(ExperimentalCoroutinesApi::class) private val userListsSyncState = currentAccount.filterNotNull().flatMapLatest { suspend { @@ -90,7 +93,7 @@ class ListListViewModel @Inject constructor( userList, addTabToAccount?.pages?.any { it.pageParams.listId == userList.userList.id.userListId - || userList.userList.id.accountId == (it.attachedAccountId ?: it.accountId) + && userList.userList.id.accountId == (it.attachedAccountId ?: it.accountId) } ?: false, isTargetUserAdded = userList.userList.userIds.any { id -> id == addUser From 8a2f5bfbf1cd8b153aa62c1f43a010f780a4178d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 21:03:09 +0900 Subject: [PATCH 275/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=83=96=E3=81=AB?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B=E5=85=88=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AEId=E3=81=A8?= =?UTF-8?q?=E9=96=8B=E3=81=8F=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=81=AEId=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_navigation/UserListNavigation.kt | 6 +++- .../milktea/userlist/ListListActivity.kt | 32 +++++++++++++------ 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/UserListNavigation.kt b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/UserListNavigation.kt index 060bbbf682..6733b25e02 100644 --- a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/UserListNavigation.kt +++ b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/UserListNavigation.kt @@ -4,4 +4,8 @@ import net.pantasystem.milktea.model.user.User interface UserListNavigation : ActivityNavigation -data class UserListArgs(val userId: User.Id? = null) \ No newline at end of file +data class UserListArgs( + val userId: User.Id? = null, + val specifiedAccountId: Long? = null, + val addTabToAccountId: Long? = null, +) \ No newline at end of file diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt index 082c8c3154..01b9c438e6 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt @@ -25,17 +25,26 @@ import javax.inject.Inject @AndroidEntryPoint class ListListActivity : AppCompatActivity() { - companion object{ + companion object { private const val EXTRA_ADD_USER_ID = "jp.panta.misskeyandroidclient.extra.ADD_USER_ID" - private const val EXTRA_ACCOUNT_ID = "jp.panta.misskeyandroidclient.extra.ADD_USERS_ACCOUNT_ID" - - fun newInstance(context: Context, addUserId: User.Id?): Intent { + private const val EXTRA_ACCOUNT_ID = + "jp.panta.misskeyandroidclient.extra.ADD_USERS_ACCOUNT_ID" + + fun newInstance( + context: Context, + addUserId: User.Id?, + specifiedAccountId: Long? = null, + addTabToAccountId: Long? = null, + ): Intent { return Intent(context, ListListActivity::class.java).apply { addUserId?.let { putExtra(EXTRA_ADD_USER_ID, addUserId.id) putExtra(EXTRA_ACCOUNT_ID, addUserId.accountId) } + putExtra(ListListViewModel.EXTRA_SPECIFIED_ACCOUNT_ID, specifiedAccountId) + putExtra(ListListViewModel.EXTRA_ADD_TAB_TO_ACCOUNT_ID, addTabToAccountId) + } } } @@ -53,7 +62,7 @@ class ListListActivity : AppCompatActivity() { private val addUserId: User.Id? by lazy { val addUserIdSt = intent.getStringExtra(EXTRA_ADD_USER_ID) - val addUserAccountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, - 1L) + val addUserAccountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1L) if (addUserIdSt == null || addUserAccountId == -1L) { null } else { @@ -73,7 +82,7 @@ class ListListActivity : AppCompatActivity() { val uiState by mListListViewModel.uiState.collectAsState() MdcTheme { UserListCardScreen(uiState = uiState, onAction = { action -> - when(action) { + when (action) { UserListCardScreenAction.OnNavigateUp -> { finish() } @@ -98,14 +107,17 @@ class ListListActivity : AppCompatActivity() { } - - } class UserListNavigationImpl @Inject constructor( - val activity: Activity + val activity: Activity, ) : UserListNavigation { override fun newIntent(args: UserListArgs): Intent { - return ListListActivity.newInstance(activity, args.userId) + return ListListActivity.newInstance( + activity, + args.userId, + specifiedAccountId = args.specifiedAccountId, + addTabToAccountId = args.addTabToAccountId, + ) } } From cbbcb028171e936d7b70f7de4edcc26878b80574 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 21:03:30 +0900 Subject: [PATCH 276/432] =?UTF-8?q?feat:=20=E4=BB=96=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE=E3=83=AA=E3=82=B9?= =?UTF-8?q?=E3=83=88=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/setting/activities/PageSettingActivity.kt | 5 ++++- .../milktea/setting/viewmodel/page/PageCandidateGenerator.kt | 2 -- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt index 05f7264fbd..8518b37b51 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt @@ -80,7 +80,10 @@ class PageSettingActivity : AppCompatActivity() { launchSearchAndSelectUserForAddUserTimelineTab.launch(intent) } PageType.USER_LIST, PageType.MASTODON_LIST_TIMELINE -> startActivity( - userListNavigation.newIntent(UserListArgs()) + userListNavigation.newIntent(UserListArgs( + specifiedAccountId = pt.relatedAccount.accountId, + addTabToAccountId = mPageSettingViewModel.account.value?.accountId + )) ) PageType.DETAIL -> startActivity(searchNavigation.newIntent(SearchNavType.SearchScreen())) PageType.ANTENNA -> startActivity(antennaNavigation.newIntent(Unit)) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt index 4974ec66f6..b93b24a650 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt @@ -26,7 +26,6 @@ class PageCandidateGenerator @Inject constructor( val restrictionTypes = setOf( PageType.ANTENNA, PageType.NOTIFICATION, - PageType.USER_LIST, PageType.CLIP_NOTES, PageType.SEARCH, PageType.SEARCH, @@ -39,7 +38,6 @@ class PageCandidateGenerator @Inject constructor( PageType.MY_GALLERY_POSTS, PageType.I_LIKED_GALLERY_POSTS, PageType.USERS_GALLERY_POSTS, - PageType.MASTODON_LIST_TIMELINE ) return when (related.instanceType) { Account.InstanceType.MISSKEY -> { From edf89580d1ba20894a4bb9902a680ac6a3ae2670 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 21:06:49 +0900 Subject: [PATCH 277/432] fix --- .../net/pantasystem/milktea/channel/ChannelViewModel.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt index de2fca8d8f..9cf33254e3 100644 --- a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt +++ b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelViewModel.kt @@ -143,11 +143,17 @@ class ChannelViewModel @Inject constructor( runCancellableCatching { val account = getAddTabToAccount() val channel = channelRepository.findOne(channelId).getOrThrow() + val relatedAccount = accountRepository.get(channel.id.accountId).getOrThrow() val page = account.newPage( Pageable.ChannelTimeline(channelId = channelId.channelId), channel.name, ).copy( attachedAccountId = getSpecifiedAccountId(), + title = if (account.accountId == relatedAccount.accountId) { + channel.name + } else { + "${channel.name}(${relatedAccount.getAcct()})" + } ) val first = account.pages.firstOrNull { (it.pageable() as? Pageable.ChannelTimeline)?.channelId == channelId.channelId } From 60790442e195519e29450cbdecbb544339c89e9f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 22:14:53 +0900 Subject: [PATCH 278/432] =?UTF-8?q?feat:=20=E8=A9=B3=E7=B4=B0=E7=94=BB?= =?UTF-8?q?=E9=9D=A2=E3=81=A7=E3=82=82=E5=89=8D=E3=81=AE=E3=82=A2=E3=82=AB?= =?UTF-8?q?=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE=E7=8A=B6=E6=85=8B=E3=81=8C?= =?UTF-8?q?=E5=8F=8D=E6=98=A0=E3=81=95=E3=82=8C=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/userlist/ListListActivity.kt | 6 +- .../userlist/UserListDetailActivity.kt | 26 ++--- .../userlist/compose/UserListDetailScreen.kt | 1 + .../userlist/viewmodel/ListListViewModel.kt | 7 +- .../viewmodel/UserListDetailViewModel.kt | 101 ++++++++++-------- 5 files changed, 80 insertions(+), 61 deletions(-) diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt index 01b9c438e6..d46664eb44 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/ListListActivity.kt @@ -90,7 +90,11 @@ class ListListActivity : AppCompatActivity() { mListListViewModel.toggleTab(action.userList) } is UserListCardScreenAction.OnUserListCardClicked -> { - val intent = UserListDetailActivity.newIntent(this, action.userList.id) + val intent = UserListDetailActivity.newIntent( + this, + action.userList.id, + mListListViewModel.getAddTabToAccountId() + ) startActivity(intent) } is UserListCardScreenAction.OnToggleAddUser -> { diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/UserListDetailActivity.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/UserListDetailActivity.kt index ce164a39d1..0490dfdae7 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/UserListDetailActivity.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/UserListDetailActivity.kt @@ -33,23 +33,21 @@ class UserListDetailActivity : AppCompatActivity(), UserListEditorDialog.OnSubmi companion object { private const val TAG = "UserListDetailActivity" - private const val EXTRA_LIST_ID = "jp.panta.misskeyandroidclient.EXTRA_LIST_ID" const val ACTION_SHOW = "ACTION_SHOW" const val ACTION_EDIT_NAME = "ACTION_EDIT_NAME" - fun newIntent(context: Context, listId: UserList.Id): Intent { + fun newIntent(context: Context, listId: UserList.Id, addTabToAccountId: Long? = null): Intent { return Intent(context, UserListDetailActivity::class.java).apply { - putExtra(EXTRA_LIST_ID, listId) + putExtra(UserListDetailViewModel.EXTRA_LIST_ID, listId) + putExtra(UserListDetailViewModel.EXTRA_ADD_TAB_TO_ACCOUNT_ID, addTabToAccountId) } } } - @Inject - lateinit var assistedFactory: UserListDetailViewModel.ViewModelAssistedFactory @Inject lateinit var settingStore: SettingStore @@ -69,12 +67,7 @@ class UserListDetailActivity : AppCompatActivity(), UserListEditorDialog.OnSubmi @Inject lateinit var applyMenuTint: ApplyMenuTint - private val listId by lazy { - intent.getSerializableExtra(EXTRA_LIST_ID) as UserList.Id - } - private val mUserListDetailViewModel: UserListDetailViewModel by viewModels { - UserListDetailViewModel.provideFactory(assistedFactory, listId) - } + private val mUserListDetailViewModel: UserListDetailViewModel by viewModels() val notesViewModel by viewModels() @@ -99,8 +92,8 @@ class UserListDetailActivity : AppCompatActivity(), UserListEditorDialog.OnSubmi val account by mUserListDetailViewModel.account.collectAsState() UserListDetailScreen( - listId = listId, - userList = userList, + listId = mUserListDetailViewModel.getUserListId(), + userList = userList?.userList, users = users, isAddedTab = isAddedTab, onNavigateUp = { @@ -122,7 +115,8 @@ class UserListDetailActivity : AppCompatActivity(), UserListEditorDialog.OnSubmi } val intent = searchAndSelectUserNavigation.newIntent( SearchAndSelectUserNavigationArgs( - selectedUserIds = selected + selectedUserIds = selected, + accountId = mUserListDetailViewModel.getUserListId().accountId ) ) requestSelectUserResult.launch(intent) @@ -178,8 +172,8 @@ class UserListDetailActivity : AppCompatActivity(), UserListEditorDialog.OnSubmi private fun showEditUserListDialog() { val dialog = UserListEditorDialog.newInstance( - listId.userListId, - mUserListDetailViewModel.userList.value?.name ?: "" + mUserListDetailViewModel.getUserListId().userListId, + mUserListDetailViewModel.userList.value?.userList?.name ?: "" ) dialog.show(supportFragmentManager, "") } diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/compose/UserListDetailScreen.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/compose/UserListDetailScreen.kt index 5967c444b2..2b24cb5755 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/compose/UserListDetailScreen.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/compose/UserListDetailScreen.kt @@ -127,6 +127,7 @@ fun UserListDetailScreen( }, update = { frameLayout -> val fragment = pageableFragmentFactory.create( + listId.accountId, when(instanceType) { Account.InstanceType.MASTODON -> Pageable.Mastodon.ListTimeline(listId.userListId) else -> Pageable.UserListTimeline(listId = listId.userListId) diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt index 32989ac5f8..2af7bdc803 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt @@ -81,12 +81,11 @@ class ListListViewModel @Inject constructor( private val addTargetUserId = MutableStateFlow(null) val uiState = combine( - currentAccount, addTabToAccount, userListsFlow, userListsSyncState, addTargetUserId - ) { ac, addTabToAccount, userLists, syncState, addUser -> + ) { addTabToAccount, userLists, syncState, addUser -> UserListsUiState( userLists.map { userList -> UserListBindingModel( @@ -167,6 +166,10 @@ class ListListViewModel @Inject constructor( addTargetUserId.value = userId } + fun getAddTabToAccountId(): Long? { + return savedStateHandle[EXTRA_ADD_TAB_TO_ACCOUNT_ID] + } + private suspend fun syncUsers(accountId: Long) { val userIds = userListRepository.findByAccountId(accountId).map { it.userIds diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/UserListDetailViewModel.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/UserListDetailViewModel.kt index 184da3e797..6d3bbf22ad 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/UserListDetailViewModel.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/UserListDetailViewModel.kt @@ -1,80 +1,83 @@ package net.pantasystem.milktea.userlist.viewmodel +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope -import dagger.assisted.Assisted -import dagger.assisted.AssistedFactory -import dagger.assisted.AssistedInject +import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.runCancellableCatching -import net.pantasystem.milktea.model.account.AccountRepository -import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.list.UserList import net.pantasystem.milktea.model.list.UserListRepository import net.pantasystem.milktea.model.list.UserListTabToggleAddToTabUseCase import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserDataSource +import javax.inject.Inject -class UserListDetailViewModel @AssistedInject constructor( +@HiltViewModel +class UserListDetailViewModel @Inject constructor( private val userListRepository: UserListRepository, private val userDataSource: UserDataSource, accountStore: AccountStore, - private val accountRepository: AccountRepository, private val toggleAddToTabUseCase: UserListTabToggleAddToTabUseCase, loggerFactory: Logger.Factory, - @Assisted val listId: UserList.Id, + private val savedStateHandle: SavedStateHandle, ) : ViewModel() { - @AssistedFactory - interface ViewModelAssistedFactory { - fun create(listId: UserList.Id): UserListDetailViewModel - } companion object { - @Suppress("UNCHECKED_CAST") - fun provideFactory( - assistedFactory: ViewModelAssistedFactory, - listId: UserList.Id - ): ViewModelProvider.Factory = object : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return assistedFactory.create(listId) as T - } - } + const val EXTRA_LIST_ID = "jp.panta.misskeyandroidclient.EXTRA_LIST_ID" + const val EXTRA_ADD_TAB_TO_ACCOUNT_ID = "jp.panta.misskeyandroidclient.EXTRA_ADD_TAB_TO_ACCOUNT_ID" } + private val listIdFlow = savedStateHandle.getStateFlow( + EXTRA_LIST_ID, + null + ) + + @OptIn(ExperimentalCoroutinesApi::class) + val account = listIdFlow.flatMapLatest { listId -> + accountStore.state.map { state -> + listId?.let { + state.get(it.accountId) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) - val userList = userListRepository.observeOne(listId).filterNotNull().map { - it.userList + @OptIn(ExperimentalCoroutinesApi::class) + val userList = listIdFlow.filterNotNull().flatMapLatest { + userListRepository.observeOne(it) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) - val isAddedToTab = accountStore.observeAccounts.mapNotNull { - it.firstOrNull { ac -> - ac.accountId == listId.accountId - } - }.map { account -> - account.pages.firstOrNull { - (it.pageable() as? Pageable.UserListTimeline)?.listId == listId.userListId + @OptIn(ExperimentalCoroutinesApi::class) + private val addToTabAccount = savedStateHandle.getStateFlow( + EXTRA_ADD_TAB_TO_ACCOUNT_ID, + null + ).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(accountId) + } ?: state.currentAccount } - }.map { page -> - page != null + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + val isAddedToTab = combine(account, addToTabAccount,listIdFlow) { account, addTo, listId -> + (addTo?.pages ?: account?.pages)?.any { + it.pageParams.listId == listId?.userListId + } ?: false }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), false) @OptIn(ExperimentalCoroutinesApi::class) - val users = userListRepository.observeOne(listId).filterNotNull().flatMapLatest { - userDataSource.observeIn(listId.accountId, it.userList.userIds.map { userId -> userId.id }) + val users = userList.filterNotNull().flatMapLatest { + userDataSource.observeIn( + it.userList.id.accountId, + it.userList.userIds.map { userId -> userId.id }) }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), emptyList()) - @OptIn(FlowPreview::class) - val account = suspend { - accountRepository.get(listId.accountId).getOrNull() - }.asFlow().stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) private val logger = loggerFactory.create("UserListDetailViewModel") @@ -85,7 +88,9 @@ class UserListDetailViewModel @AssistedInject constructor( fun load() { viewModelScope.launch { runCancellableCatching { - userListRepository.syncOne(listId) + savedStateHandle.get(EXTRA_LIST_ID)?.let { + userListRepository.syncOne(it) + } }.onSuccess { logger.info("load list success") }.onFailure { @@ -99,6 +104,8 @@ class UserListDetailViewModel @AssistedInject constructor( fun updateName(name: String) { viewModelScope.launch { runCancellableCatching { + val listId = savedStateHandle.get(EXTRA_LIST_ID) + ?: throw IllegalStateException("listId is null") userListRepository.update(listId, name) userListRepository.syncOne(listId).getOrThrow() }.onSuccess { @@ -114,6 +121,8 @@ class UserListDetailViewModel @AssistedInject constructor( viewModelScope.launch { runCancellableCatching { + val listId = savedStateHandle.get(EXTRA_LIST_ID) + ?: throw IllegalStateException("listId is null") userListRepository.appendUser(listId, userId) userListRepository.syncOne(listId).getOrThrow() }.onSuccess { @@ -130,6 +139,8 @@ class UserListDetailViewModel @AssistedInject constructor( viewModelScope.launch { runCancellableCatching { + val listId = savedStateHandle.get(EXTRA_LIST_ID) + ?: throw IllegalStateException("listId is null") userListRepository.removeUser(listId, userId) userListRepository.syncOne(listId).getOrThrow() }.onFailure { t -> @@ -144,12 +155,18 @@ class UserListDetailViewModel @AssistedInject constructor( fun toggleAddToTab() { viewModelScope.launch { - toggleAddToTabUseCase(listId).onFailure { + val listId = savedStateHandle.get(EXTRA_LIST_ID) + ?: throw IllegalStateException("listId is null") + + toggleAddToTabUseCase(listId, savedStateHandle[EXTRA_ADD_TAB_TO_ACCOUNT_ID]).onFailure { logger.error("Page追加に失敗", it) } } } + fun getUserListId(): UserList.Id { + return requireNotNull(savedStateHandle[EXTRA_LIST_ID]) + } } \ No newline at end of file From 66e5af961c425d7c5472bd21a9b8ee84c8edfa84 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 22:20:16 +0900 Subject: [PATCH 279/432] fix --- .../misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt b/app/src/main/java/jp/panta/misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt index f6dd1cd1b6..8433667229 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt @@ -60,7 +60,7 @@ class PageableFragmentFactoryImpl @Inject constructor(): PageableFragmentFactory NotificationFragment() } is Pageable.Gallery -> { - return GalleryPostsFragment.newInstance(pageable, null) + return GalleryPostsFragment.newInstance(pageable, accountId) } else ->{ TimelineFragment.newInstance(pageable, accountId) From bc64146525c811f87db1af5dac0913f3f02f7361 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 22:31:02 +0900 Subject: [PATCH 280/432] fix --- .../setting/activities/PageSettingActivity.kt | 3 ++- .../viewmodel/page/PageSettingViewModel.kt | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt index 8518b37b51..e8966174e7 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt @@ -96,7 +96,8 @@ class PageSettingActivity : AppCompatActivity() { val intent = searchAndSelectUserNavigation.newIntent( SearchAndSelectUserNavigationArgs( - selectableMaximumSize = 1 + selectableMaximumSize = 1, + accountId = pt.relatedAccount.accountId, ) ) launchSearchAndSelectUserForAddGalleryTab.launch(intent) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt index c10416efdc..82330f5865 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageSettingViewModel.kt @@ -17,6 +17,7 @@ import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common_android.eventbus.EventBus import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.page.* import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserRepository @@ -29,6 +30,7 @@ class PageSettingViewModel @Inject constructor( private val pageTypeNameMap: PageTypeNameMap, private val userRepository: UserRepository, private val accountStore: AccountStore, + private val accountRepository: AccountRepository, private val pageCandidateGenerator: PageCandidateGenerator, ) : ViewModel(), SelectPageTypeToAdd, PageSettingAction { @@ -154,16 +156,22 @@ class PageSettingViewModel @Inject constructor( fun addUsersGalleryByIds(userIds: List) { viewModelScope.launch { runCancellableCatching { + val account = requireNotNull(account.value) userIds.map { async { userRepository.find(it) } }.awaitAll().map { user -> - val name = - if (settingStore.isUserNameDefault) user.shortDisplayName else user.displayName - account.value!!.newPage(Pageable.Gallery.User(userId = user.id.id), name = name) - }.forEach { - addPage(it, null) + val relatedAccount = accountRepository.get(user.id.accountId).getOrThrow() + val name = if (settingStore.isUserNameDefault) user.shortDisplayName else user.displayName + val title = if (relatedAccount.accountId == account.accountId) { + name + } else { + "$name(${relatedAccount.getAcct()})" + } + account.newPage(Pageable.Gallery.User(userId = user.id.id), name = title) to relatedAccount + }.forEach { (page, relatedAccount) -> + addPage(page, relatedAccount) } } } From 94cb05f4b16773d26667c59d076d9b83137178c2 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 29 May 2023 23:01:45 +0900 Subject: [PATCH 281/432] fix --- .../milktea/userlist/viewmodel/ListListViewModel.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt index 2af7bdc803..9dede983eb 100644 --- a/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt +++ b/modules/features/userlist/src/main/java/net/pantasystem/milktea/userlist/viewmodel/ListListViewModel.kt @@ -43,8 +43,8 @@ class ListListViewModel @Inject constructor( accountStore.state.map { state -> accountId?.let { state.get(it) - ?: state.currentAccount - } + + } ?: state.currentAccount } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) From e501a829edf7291828c20268c8f9e97fcbde1f04 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 30 May 2023 11:24:56 +0900 Subject: [PATCH 282/432] =?UTF-8?q?feat:=20Flow=E3=81=A7=E7=8A=B6=E6=85=8B?= =?UTF-8?q?=E3=82=92=E5=88=B6=E5=BE=A1=E3=81=99=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/antenna/AntennaListFragment.kt | 25 ++++-- .../antenna/viewmodel/AntennaListViewModel.kt | 89 ++++++++++++++----- 2 files changed, 81 insertions(+), 33 deletions(-) diff --git a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListFragment.kt b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListFragment.kt index 504bf6b40d..675290d81c 100644 --- a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListFragment.kt +++ b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListFragment.kt @@ -4,13 +4,19 @@ import android.os.Bundle import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager import com.wada811.databinding.dataBinding import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.FlowPreview +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach import net.pantasystem.milktea.antenna.databinding.FragmentAntennaListBinding import net.pantasystem.milktea.antenna.viewmodel.AntennaListViewModel +import net.pantasystem.milktea.common.ResultState +import net.pantasystem.milktea.common.StateContent @FlowPreview @ExperimentalCoroutinesApi @@ -30,18 +36,19 @@ class AntennaListFragment : Fragment(R.layout.fragment_antenna_list){ binding.antennaListView.adapter = adapter binding.antennaListView.layoutManager = layoutManager - antennaViewModel.antennas.observe(viewLifecycleOwner) { - adapter.submitList(it) - } + antennaViewModel.antennasState.onEach { + val antennas = when(val content = it.content) { + is StateContent.Exist -> content.rawContent + is StateContent.NotExist -> emptyList() + } + adapter.submitList(antennas) + + val isLoading = it is ResultState.Loading + binding.antennaListSwipeRefresh.isRefreshing = isLoading + }.flowWithLifecycle(viewLifecycleOwner.lifecycle).launchIn(viewLifecycleOwner.lifecycleScope) binding.antennaListSwipeRefresh.setOnRefreshListener { antennaViewModel.loadInit() } - - antennaViewModel.isLoading.observe(viewLifecycleOwner) { - binding.antennaListSwipeRefresh.isRefreshing = it - } - - } } \ No newline at end of file diff --git a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt index 70a080f049..3c4a936ba4 100644 --- a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt +++ b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt @@ -1,31 +1,77 @@ package net.pantasystem.milktea.antenna.viewmodel +import android.util.Log import androidx.lifecycle.* import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.plus import net.pantasystem.milktea.app_store.account.AccountStore -import net.pantasystem.milktea.common.runCancellableCatching +import net.pantasystem.milktea.common.ResultState +import net.pantasystem.milktea.common.StateContent +import net.pantasystem.milktea.common.asLoadingStateFlow import net.pantasystem.milktea.common_android.eventbus.EventBus -import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.account.page.PageableTemplate import net.pantasystem.milktea.model.antenna.Antenna import net.pantasystem.milktea.model.antenna.AntennaRepository +import java.util.* import javax.inject.Inject @HiltViewModel class AntennaListViewModel @Inject constructor( private val accountStore: AccountStore, - private val accountRepository: AccountRepository, private val antennaRepository: AntennaRepository, + private val savedStateHandle: SavedStateHandle, ) : ViewModel() { + companion object { + const val EXTRA_SPECIFIED_ACCOUNT_ID = "AntennaListViewModel.EXTRA_SPECIFIED_ACCOUNT_ID" + const val EXTRA_ADD_TAB_TO_ACCOUNT_ID = "AntennaListViewModel.EXTRA_ADD_TAB_TO_ACCOUNT_ID" + } + + @OptIn(ExperimentalCoroutinesApi::class) + private val currentAccount = savedStateHandle.getStateFlow( + EXTRA_SPECIFIED_ACCOUNT_ID, + null + ).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(it) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + + @OptIn(ExperimentalCoroutinesApi::class) + private val addTabToAccount = savedStateHandle.getStateFlow( + EXTRA_ADD_TAB_TO_ACCOUNT_ID, + null + ).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { accountId -> + state.get(accountId) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) - val antennas = MediatorLiveData>() + private val refreshAntennasEvents = MutableStateFlow(Date().time) + + @OptIn(ExperimentalCoroutinesApi::class) + val antennasState = refreshAntennasEvents.flatMapLatest { + currentAccount.filterNotNull().flatMapLatest { account -> + suspend { + Log.d("AntennaListViewModel", "antenna account state: ${account.accountId}") + antennaRepository.findByAccountId(account.accountId).getOrThrow() + }.asLoadingStateFlow() + } + }.stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + ResultState.Loading(StateContent.NotExist()) + ) val editAntennaEvent = EventBus() @@ -33,12 +79,6 @@ class AntennaListViewModel @Inject constructor( private val openAntennasTimelineEvent = EventBus() - val isLoading = MutableLiveData(false) - private var mIsLoading: Boolean = false - set(value) { - field = value - isLoading.postValue(value) - } private val mPagedAntennaIds = MutableLiveData>() val pagedAntennaIds: LiveData> = mPagedAntennaIds @@ -55,7 +95,6 @@ class AntennaListViewModel @Inject constructor( if (pageable is Pageable.Antenna) { it.accountId.let { accountId -> Antenna.Id(accountId, pageable.antennaId) - } } else { null @@ -70,16 +109,7 @@ class AntennaListViewModel @Inject constructor( private val deleteResultEvent = EventBus() fun loadInit() { - viewModelScope.launch { - runCancellableCatching { - val account = accountRepository.getCurrentAccount().getOrThrow() - antennaRepository.findByAccountId(account.accountId).getOrThrow() - }.onSuccess { - antennas.postValue(it) - } - mIsLoading = false - } - + refreshAntennasEvents.tryEmit(Date().time) } fun toggleTab(antenna: Antenna?) { @@ -126,4 +156,15 @@ class AntennaListViewModel @Inject constructor( } } } -} \ No newline at end of file +} + +data class AntennaListItem( + val antenna: Antenna, + val isAddedToTab: Boolean, +) + +data class AntennaListUiState( + val currentAccount: Account?, + val addTabToAccount: Account?, + val antennas: ResultState>, +) \ No newline at end of file From cf9d1e69556343f831f2bdeb5b6aea5bd9a42887 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 30 May 2023 11:32:41 +0900 Subject: [PATCH 283/432] =?UTF-8?q?UiState=E3=82=92=E4=BD=9C=E6=88=90?= =?UTF-8?q?=E3=81=97=E3=81=95=E3=82=89=E3=81=ABAntennaListItem=E3=82=92?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/antenna/AntennaListAdapter.kt | 14 +++++++------- .../milktea/antenna/AntennaListFragment.kt | 6 +++--- .../milktea/antenna/AntennaPagedStateHelper.kt | 8 ++++---- .../antenna/viewmodel/AntennaListViewModel.kt | 18 ++++++++++++++++++ .../src/main/res/layout/item_antenna.xml | 13 ++++++------- 5 files changed, 38 insertions(+), 21 deletions(-) diff --git a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListAdapter.kt b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListAdapter.kt index 7f6491636f..6159da7284 100644 --- a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListAdapter.kt +++ b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListAdapter.kt @@ -8,21 +8,21 @@ import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView import net.pantasystem.milktea.antenna.databinding.ItemAntennaBinding +import net.pantasystem.milktea.antenna.viewmodel.AntennaListItem import net.pantasystem.milktea.antenna.viewmodel.AntennaListViewModel -import net.pantasystem.milktea.model.antenna.Antenna class AntennaListAdapter( private val antennaListViewModel: AntennaListViewModel, val lifecycleOwner: LifecycleOwner -) : ListAdapter(ItemCallback()){ +) : ListAdapter(ItemCallback()){ - class ItemCallback : DiffUtil.ItemCallback(){ - override fun areContentsTheSame(oldItem: Antenna, newItem: Antenna): Boolean { - return oldItem.id == newItem.id + class ItemCallback : DiffUtil.ItemCallback(){ + override fun areContentsTheSame(oldItem: AntennaListItem, newItem: AntennaListItem): Boolean { + return oldItem == newItem } - override fun areItemsTheSame(oldItem: Antenna, newItem: Antenna): Boolean { - return oldItem == newItem + override fun areItemsTheSame(oldItem: AntennaListItem, newItem: AntennaListItem): Boolean { + return oldItem.antenna.id == newItem.antenna.id } } diff --git a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListFragment.kt b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListFragment.kt index 675290d81c..4b3f772a56 100644 --- a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListFragment.kt +++ b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListFragment.kt @@ -36,14 +36,14 @@ class AntennaListFragment : Fragment(R.layout.fragment_antenna_list){ binding.antennaListView.adapter = adapter binding.antennaListView.layoutManager = layoutManager - antennaViewModel.antennasState.onEach { - val antennas = when(val content = it.content) { + antennaViewModel.uiState.onEach { uiState -> + val antennas = when(val content = uiState.antennas.content) { is StateContent.Exist -> content.rawContent is StateContent.NotExist -> emptyList() } adapter.submitList(antennas) - val isLoading = it is ResultState.Loading + val isLoading = uiState.antennas is ResultState.Loading binding.antennaListSwipeRefresh.isRefreshing = isLoading }.flowWithLifecycle(viewLifecycleOwner.lifecycle).launchIn(viewLifecycleOwner.lifecycleScope) diff --git a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaPagedStateHelper.kt b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaPagedStateHelper.kt index 89c178e6c1..385501c147 100644 --- a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaPagedStateHelper.kt +++ b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaPagedStateHelper.kt @@ -2,14 +2,14 @@ package net.pantasystem.milktea.antenna import android.widget.ImageButton import androidx.databinding.BindingAdapter -import net.pantasystem.milktea.model.antenna.Antenna +import net.pantasystem.milktea.antenna.viewmodel.AntennaListItem object AntennaPagedStateHelper{ @JvmStatic - @BindingAdapter("targetAntenna", "pagedAntennaIds") - fun ImageButton.setPagedState(antenna: Antenna?, pagedAntennaIds: Set?){ - if(pagedAntennaIds?.contains(antenna?.id) == true){ + @BindingAdapter("targetAntenna") + fun ImageButton.setPagedState(antenna: AntennaListItem?){ + if(antenna?.isAddedToTab == true){ this.setImageResource(R.drawable.ic_remove_to_tab_24px) }else { this.setImageResource(R.drawable.ic_add_to_tab_24px) diff --git a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt index 3c4a936ba4..5d564aac88 100644 --- a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt +++ b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt @@ -73,6 +73,24 @@ class AntennaListViewModel @Inject constructor( ResultState.Loading(StateContent.NotExist()) ) + val uiState = combine(currentAccount, addTabToAccount, antennasState) { ca, ata, state -> + AntennaListUiState( + currentAccount = ca, + addTabToAccount = ata, + antennas = state.convert { antennas -> + antennas.map { antenna -> + AntennaListItem( + antenna = antenna, + isAddedToTab = (ata ?: ca)?.pages?.any { page -> + page.pageParams.antennaId == antenna.id.antennaId + && (page.attachedAccountId ?: page.accountId) == antenna.id.accountId + } ?: false + ) + } + } + ) + } + val editAntennaEvent = EventBus() val confirmDeletionAntennaEvent = EventBus() diff --git a/modules/features/antenna/src/main/res/layout/item_antenna.xml b/modules/features/antenna/src/main/res/layout/item_antenna.xml index 4431e17991..79d87de151 100644 --- a/modules/features/antenna/src/main/res/layout/item_antenna.xml +++ b/modules/features/antenna/src/main/res/layout/item_antenna.xml @@ -6,7 +6,7 @@ + type="net.pantasystem.milktea.antenna.viewmodel.AntennaListItem" /> @@ -28,9 +28,9 @@ android:layout_height="wrap_content" tools:text="かわいいどうぶつたち" android:textSize="20sp" - android:text="@{SafeUnbox.unbox(antenna.name)}" + android:text="@{SafeUnbox.unbox(antenna.antenna.name)}" android:layout_marginEnd="8dp" - android:onClick="@{ ()-> antennaListViewModel.openAntennasTimeline(antenna) }" + android:onClick="@{ ()-> antennaListViewModel.openAntennasTimeline(antenna.antenna) }" /> @@ -56,7 +55,7 @@ android:contentDescription="@string/add_to_tab" android:layout_below="@id/antennaNameInputLayout" android:layout_toStartOf="@id/editAntennaButton" - android:onClick="@{ ()-> antennaListViewModel.confirmDeletionAntenna(antenna) }" + android:onClick="@{ ()-> antennaListViewModel.confirmDeletionAntenna(antenna.antenna) }" app:tint="?attr/normalIconTint" /> From 09f7d7331fe07b79c8bb179fa682d38bb47f612a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 30 May 2023 11:45:29 +0900 Subject: [PATCH 284/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=83=96=E3=81=B8?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E3=81=99=E3=82=8B=E5=87=A6=E7=90=86=E3=82=92?= =?UTF-8?q?UseCase=E3=81=AB=E5=88=87=E3=82=8A=E5=87=BA=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../antenna/viewmodel/AntennaListViewModel.kt | 37 +++++++--------- .../antenna/AntennaToggleAddToTabUseCase.kt | 42 +++++++++++++++++++ 2 files changed, 56 insertions(+), 23 deletions(-) create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/antenna/AntennaToggleAddToTabUseCase.kt diff --git a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt index 5d564aac88..60a4080665 100644 --- a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt +++ b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/viewmodel/AntennaListViewModel.kt @@ -9,22 +9,22 @@ import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import kotlinx.coroutines.plus import net.pantasystem.milktea.app_store.account.AccountStore -import net.pantasystem.milktea.common.ResultState -import net.pantasystem.milktea.common.StateContent -import net.pantasystem.milktea.common.asLoadingStateFlow +import net.pantasystem.milktea.common.* import net.pantasystem.milktea.common_android.eventbus.EventBus import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.page.Pageable -import net.pantasystem.milktea.model.account.page.PageableTemplate import net.pantasystem.milktea.model.antenna.Antenna import net.pantasystem.milktea.model.antenna.AntennaRepository +import net.pantasystem.milktea.model.antenna.AntennaToggleAddToTabUseCase import java.util.* import javax.inject.Inject @HiltViewModel class AntennaListViewModel @Inject constructor( + loggerFactory: Logger.Factory, private val accountStore: AccountStore, private val antennaRepository: AntennaRepository, + private val antennaToggleAddToTabUseCase: AntennaToggleAddToTabUseCase, private val savedStateHandle: SavedStateHandle, ) : ViewModel() { @@ -33,6 +33,10 @@ class AntennaListViewModel @Inject constructor( const val EXTRA_ADD_TAB_TO_ACCOUNT_ID = "AntennaListViewModel.EXTRA_ADD_TAB_TO_ACCOUNT_ID" } + private val logger by lazy(LazyThreadSafetyMode.NONE) { + loggerFactory.create("AntennaListViewModel") + } + @OptIn(ExperimentalCoroutinesApi::class) private val currentAccount = savedStateHandle.getStateFlow( EXTRA_SPECIFIED_ACCOUNT_ID, @@ -99,7 +103,6 @@ class AntennaListViewModel @Inject constructor( private val mPagedAntennaIds = MutableLiveData>() - val pagedAntennaIds: LiveData> = mPagedAntennaIds init { @@ -130,25 +133,13 @@ class AntennaListViewModel @Inject constructor( refreshAntennasEvents.tryEmit(Date().time) } - fun toggleTab(antenna: Antenna?) { - antenna ?: return - val paged = accountStore.currentAccount?.pages?.firstOrNull { - - it.pageParams.antennaId == antenna.id.antennaId + fun toggleTab(antenna: Antenna?) = viewModelScope.launch { + antenna ?: return@launch + runCancellableCatching { + antennaToggleAddToTabUseCase(antenna, savedStateHandle[EXTRA_ADD_TAB_TO_ACCOUNT_ID]) + }.onFailure { + logger.error("Failed to toggle tab", it) } - viewModelScope.launch { - if (paged == null) { - accountStore.addPage( - PageableTemplate(accountStore.currentAccount!!) - .antenna( - antenna - ) - ) - } else { - accountStore.removePage(paged) - } - } - } fun confirmDeletionAntenna(antenna: Antenna?) { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/antenna/AntennaToggleAddToTabUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/antenna/AntennaToggleAddToTabUseCase.kt new file mode 100644 index 0000000000..b15e2f22d2 --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/antenna/AntennaToggleAddToTabUseCase.kt @@ -0,0 +1,42 @@ +package net.pantasystem.milktea.model.antenna + +import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.account.page.PageableTemplate +import javax.inject.Inject + +class AntennaToggleAddToTabUseCase @Inject constructor( + private val accountRepository: AccountRepository, + private val accountService: AccountService, +) { + suspend operator fun invoke(antenna: Antenna, addTabToAccountId: Long? = null) { + val relatedAccount = accountRepository.get(antenna.id.accountId).getOrThrow() + val current = addTabToAccountId?.let { + accountRepository.get(it).getOrThrow() + } ?: accountRepository.getCurrentAccount().getOrThrow() + val paged = current.pages.firstOrNull { + it.pageParams.antennaId == antenna.id.antennaId + && (it.attachedAccountId ?: it.accountId) == antenna.id.accountId + } + val isSameAccount = relatedAccount.accountId == current.accountId + val title = if (isSameAccount) { + antenna.name + } else { + "${antenna.name}(${relatedAccount.getAcct()})" + } + if (paged == null) { + val page = PageableTemplate(current) + .antenna( + antenna + ).copy( + attachedAccountId = if (isSameAccount) null else relatedAccount.accountId, + title = title, + weight = -1, + ) + accountService.add( + page + ) + } else { + accountService.remove(paged) + } + } +} \ No newline at end of file From 4f59e98d8c426260916a24054e2c0c9fa452ce58 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 30 May 2023 12:12:29 +0900 Subject: [PATCH 285/432] fix --- .../milktea/model/antenna/AntennaToggleAddToTabUseCase.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/antenna/AntennaToggleAddToTabUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/antenna/AntennaToggleAddToTabUseCase.kt index b15e2f22d2..7472797973 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/antenna/AntennaToggleAddToTabUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/antenna/AntennaToggleAddToTabUseCase.kt @@ -31,6 +31,7 @@ class AntennaToggleAddToTabUseCase @Inject constructor( attachedAccountId = if (isSameAccount) null else relatedAccount.accountId, title = title, weight = -1, + accountId = current.accountId, ) accountService.add( page From 2de9fa6686d5fb37c8024e9320d0bbbdf518bc40 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 30 May 2023 12:12:54 +0900 Subject: [PATCH 286/432] =?UTF-8?q?feat:=20=E4=BB=96=E3=81=AE=E3=82=A2?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE=E3=82=A2=E3=83=B3?= =?UTF-8?q?=E3=83=86=E3=83=8A=E3=81=A7=E3=82=82=E6=AD=A3=E3=81=97=E3=81=8F?= =?UTF-8?q?=E7=8F=BE=E5=9C=A8=E3=81=AE=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=81=AE=E3=82=BF=E3=83=96=E3=81=AB=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_navigation/AntennaNavigation.kt | 9 +++++++-- .../pantasystem/milktea/antenna/AntennaListActivity.kt | 8 ++++++-- .../milktea/setting/activities/PageSettingActivity.kt | 5 ++++- .../setting/viewmodel/page/PageCandidateGenerator.kt | 1 - 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/AntennaNavigation.kt b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/AntennaNavigation.kt index 97ce3c9aed..c1cee00a69 100644 --- a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/AntennaNavigation.kt +++ b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/AntennaNavigation.kt @@ -1,4 +1,9 @@ package net.pantasystem.milktea.common_navigation -interface AntennaNavigation : ActivityNavigation { -} \ No newline at end of file +interface AntennaNavigation : ActivityNavigation { +} + +data class AntennaNavigationArgs( + val specifiedAccountId: Long? = null, + val addTabToAccountId: Long? = null, +) \ No newline at end of file diff --git a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListActivity.kt b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListActivity.kt index 214c1ebd46..a66ee866d6 100644 --- a/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListActivity.kt +++ b/modules/features/antenna/src/main/java/net/pantasystem/milktea/antenna/AntennaListActivity.kt @@ -16,6 +16,7 @@ import net.pantasystem.milktea.antenna.databinding.ActivityAntennaListBinding import net.pantasystem.milktea.antenna.viewmodel.AntennaListViewModel import net.pantasystem.milktea.common.ui.ApplyTheme import net.pantasystem.milktea.common_navigation.AntennaNavigation +import net.pantasystem.milktea.common_navigation.AntennaNavigationArgs import net.pantasystem.milktea.model.antenna.Antenna import javax.inject.Inject @@ -85,7 +86,10 @@ class AntennaListActivity : AppCompatActivity() { class AntennaNavigationImpl @Inject constructor( val activity: Activity ): AntennaNavigation { - override fun newIntent(args: Unit): Intent { - return Intent(activity, AntennaListActivity::class.java) + override fun newIntent(args: AntennaNavigationArgs): Intent { + return Intent(activity, AntennaListActivity::class.java).apply { + putExtra(AntennaListViewModel.EXTRA_SPECIFIED_ACCOUNT_ID, args.specifiedAccountId) + putExtra(AntennaListViewModel.EXTRA_ADD_TAB_TO_ACCOUNT_ID, args.addTabToAccountId) + } } } \ No newline at end of file diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt index e8966174e7..e4e0e926bd 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt @@ -86,7 +86,10 @@ class PageSettingActivity : AppCompatActivity() { )) ) PageType.DETAIL -> startActivity(searchNavigation.newIntent(SearchNavType.SearchScreen())) - PageType.ANTENNA -> startActivity(antennaNavigation.newIntent(Unit)) + PageType.ANTENNA -> startActivity(antennaNavigation.newIntent(AntennaNavigationArgs( + specifiedAccountId = pt.relatedAccount.accountId, + addTabToAccountId = mPageSettingViewModel.account.value?.accountId + ))) PageType.CLIP_NOTES -> startActivity( clipListNavigation.newIntent( ClipListNavigationArgs(mode = ClipListNavigationArgs.Mode.AddToTab) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt index b93b24a650..5579a77070 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt @@ -24,7 +24,6 @@ class PageCandidateGenerator @Inject constructor( val isSameAccount = related.accountId == currentAccount?.accountId || currentAccount == null val restrictionTypes = setOf( - PageType.ANTENNA, PageType.NOTIFICATION, PageType.CLIP_NOTES, PageType.SEARCH, From 85bd11da4668abaa80651c559b11a2539080d911 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 30 May 2023 12:23:51 +0900 Subject: [PATCH 287/432] fix --- .../main/java/net/pantasystem/milktea/model/account/Account.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt index 3250d5a892..109a05a5c2 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/Account.kt @@ -57,7 +57,7 @@ data class Account( } fun getAcct(): String { - return "@$userName@${getHost()}}" + return "@$userName@${getHost()}" } } From 9d75e69c77e2e1796e79598ec96f3bb1e34346ed Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 30 May 2023 12:23:55 +0900 Subject: [PATCH 288/432] fix --- .../pantasystem/milktea/model/account/page/PageableTemplate.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt index ec473c7e3f..568ab8804e 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt @@ -57,7 +57,7 @@ class PageableTemplate(val account: Account?) { return Page(account?.accountId?: - 1, title, 0, Pageable.Antenna(antennaId)) } fun antenna(antenna: Antenna): Page { - return Page(antenna.id.accountId, antenna.name, 0, Pageable.Antenna(antenna.id.antennaId)) + return Page(account?.accountId ?: antenna.id.accountId, antenna.name, 0, Pageable.Antenna(antenna.id.antennaId)) } fun mastodonPublicTimeline(title: String): Page { From 6c2f814f4f28671e3df7823a4ce54d66ac8cb3e5 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 30 May 2023 12:50:30 +0900 Subject: [PATCH 289/432] =?UTF-8?q?feat:=20=E5=8D=98=E4=BD=93=E3=83=86?= =?UTF-8?q?=E3=82=B9=E3=83=88=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../model/account/AccountTest.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/app/src/test/java/jp/panta/misskeyandroidclient/model/account/AccountTest.kt b/app/src/test/java/jp/panta/misskeyandroidclient/model/account/AccountTest.kt index fae657a15f..63b5459202 100644 --- a/app/src/test/java/jp/panta/misskeyandroidclient/model/account/AccountTest.kt +++ b/app/src/test/java/jp/panta/misskeyandroidclient/model/account/AccountTest.kt @@ -282,4 +282,17 @@ class AccountTest { ) Assertions.assertEquals("mk.iaia.moe", account.getHost()) } + + @Test + fun getAcct() { + val account = Account( + remoteId = "", + instanceDomain = "https://calc.panta.systems", + userName = "Panta", + instanceType = Account.InstanceType.MISSKEY, + token = "" + ) + val actual = account.getAcct() + Assertions.assertEquals("@Panta@calc.panta.systems", actual) + } } \ No newline at end of file From 6335fa7463a545529e6dab482617c524b93db589 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 11:34:22 +0900 Subject: [PATCH 290/432] =?UTF-8?q?feat:=20=E3=82=A8=E3=83=A9=E3=83=BC?= =?UTF-8?q?=E3=83=8F=E3=83=B3=E3=83=89=E3=83=AA=E3=83=B3=E3=82=B0=E3=82=92?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/clip/ClipListViewModel.kt | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt b/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt index b9c9d9a170..555e6237d8 100644 --- a/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt +++ b/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore +import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.ResultState import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common.asLoadingStateFlow @@ -21,6 +22,7 @@ import javax.inject.Inject @HiltViewModel class ClipListViewModel @Inject constructor( + loggerFactory: Logger.Factory, private val clipRepository: ClipRepository, private val accountRepository: AccountRepository, private val accountStore: AccountStore, @@ -28,6 +30,10 @@ class ClipListViewModel @Inject constructor( private val savedStateHandle: SavedStateHandle ) : ViewModel() { + private val logger by lazy(LazyThreadSafetyMode.NONE) { + loggerFactory.create("ClipListViewModel") + } + private val accountId = savedStateHandle.getStateFlow( ClipListNavigationImpl.EXTRA_ACCOUNT_ID, -1L ).map { @@ -58,7 +64,9 @@ class ClipListViewModel @Inject constructor( it.accountId }.distinctUntilChanged().flatMapLatest { accountId -> suspend { - clipRepository.getMyClips(accountId).getOrThrow() + clipRepository.getMyClips(accountId).onFailure { + logger.error("getClips failed: $it", it) + }.getOrThrow() }.asLoadingStateFlow() }.stateIn( viewModelScope, From a96f45c3bb02d69f323c4abbb54a7c91906b7a81 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 11:35:58 +0900 Subject: [PATCH 291/432] refactor --- .../milktea/clip/ClipListViewModel.kt | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt b/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt index 555e6237d8..c420a490ac 100644 --- a/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt +++ b/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt @@ -14,7 +14,6 @@ import net.pantasystem.milktea.common.StateContent import net.pantasystem.milktea.common.asLoadingStateFlow import net.pantasystem.milktea.common_navigation.ClipListNavigationArgs import net.pantasystem.milktea.model.account.Account -import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.clip.Clip import net.pantasystem.milktea.model.clip.ClipRepository import net.pantasystem.milktea.model.clip.ToggleClipAddToTabUseCase @@ -24,7 +23,6 @@ import javax.inject.Inject class ClipListViewModel @Inject constructor( loggerFactory: Logger.Factory, private val clipRepository: ClipRepository, - private val accountRepository: AccountRepository, private val accountStore: AccountStore, private val toggleClipAddToTabUseCase: ToggleClipAddToTabUseCase, private val savedStateHandle: SavedStateHandle @@ -43,17 +41,11 @@ class ClipListViewModel @Inject constructor( }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) @OptIn(ExperimentalCoroutinesApi::class) - private val currentAccount = accountId.map { - if (it == null) { - accountRepository.getCurrentAccount().getOrNull() - } else { - accountRepository.get(it).getOrNull() - } - }.filterNotNull().flatMapLatest { ac -> - accountStore.observeAccounts.map { accounts -> - accounts.firstOrNull { - ac.accountId == it.accountId - } + private val currentAccount = accountId.flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(it) + } ?: state.currentAccount } }.catch { From 9737112182f5f57b27d16f6a718e4b9d3b9e10d8 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 11:37:43 +0900 Subject: [PATCH 292/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=83=96=E3=81=AE?= =?UTF-8?q?=E8=BF=BD=E5=8A=A0=E5=85=88=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=97=E3=81=A6=E8=B5=B7?= =?UTF-8?q?=E5=8B=95=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/common_navigation/ClipNavigation.kt | 3 ++- .../main/java/net/pantasystem/milktea/clip/ClipListActivity.kt | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/ClipNavigation.kt b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/ClipNavigation.kt index 4af8083901..efcbac65c1 100644 --- a/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/ClipNavigation.kt +++ b/modules/common_navigation/src/main/java/net/pantasystem/milktea/common_navigation/ClipNavigation.kt @@ -6,7 +6,8 @@ interface ClipListNavigation : ActivityNavigation data class ClipListNavigationArgs( val accountId: Long? = null, - val mode: Mode = Mode.View + val mode: Mode = Mode.View, + val addTabToAccountId: Long? = null, ) { enum class Mode { AddToTab, diff --git a/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListActivity.kt b/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListActivity.kt index 15bc2ea3b7..55b3c78350 100644 --- a/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListActivity.kt +++ b/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListActivity.kt @@ -73,6 +73,7 @@ class ClipListNavigationImpl @Inject constructor( companion object { const val EXTRA_ACCOUNT_ID = "ClipListActivity.EXTRA_ACCOUNT_ID" const val EXTRA_MODE = "ClipListActivity.EXTRA_MODE" + const val EXTRA_ADD_TAB_TO_ACCOUNT_ID = "ClipListActivity.EXTRA_ADD_TAB_TO_ACCOUNT_ID" } override fun newIntent(args: ClipListNavigationArgs): Intent { @@ -80,6 +81,7 @@ class ClipListNavigationImpl @Inject constructor( args.accountId?.let { putExtra(EXTRA_ACCOUNT_ID, it) } + putExtra(EXTRA_ADD_TAB_TO_ACCOUNT_ID, args.addTabToAccountId) putExtra(EXTRA_MODE, args.mode.name) } } From 55520d73021c0bfc1651b90a95254ab159e418c1 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 11:46:18 +0900 Subject: [PATCH 293/432] =?UTF-8?q?feat:=20=E4=BB=96=E3=82=A2=E3=82=AB?= =?UTF-8?q?=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AE=E3=82=AF=E3=83=AA=E3=83=83?= =?UTF-8?q?=E3=83=97=E3=82=92=E3=82=BF=E3=83=96=E3=81=AB=E8=BF=BD=E5=8A=A0?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/clip/ClipListViewModel.kt | 50 +++++++++++++++---- .../model/clip/ToggleClipAddToTabUseCase.kt | 23 +++++++-- 2 files changed, 58 insertions(+), 15 deletions(-) diff --git a/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt b/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt index c420a490ac..6713461531 100644 --- a/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt +++ b/modules/features/clip/src/main/java/net/pantasystem/milktea/clip/ClipListViewModel.kt @@ -25,7 +25,7 @@ class ClipListViewModel @Inject constructor( private val clipRepository: ClipRepository, private val accountStore: AccountStore, private val toggleClipAddToTabUseCase: ToggleClipAddToTabUseCase, - private val savedStateHandle: SavedStateHandle + private val savedStateHandle: SavedStateHandle, ) : ViewModel() { private val logger by lazy(LazyThreadSafetyMode.NONE) { @@ -40,6 +40,15 @@ class ClipListViewModel @Inject constructor( } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + private val addTabToAccountId = savedStateHandle.getStateFlow( + ClipListNavigationImpl.EXTRA_ADD_TAB_TO_ACCOUNT_ID, + -1 + ).map { + it.takeIf { + it > 0 + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + @OptIn(ExperimentalCoroutinesApi::class) private val currentAccount = accountId.flatMapLatest { accountId -> accountStore.state.map { state -> @@ -48,7 +57,16 @@ class ClipListViewModel @Inject constructor( } ?: state.currentAccount } }.catch { + logger.error("currentAccount failed: $it", it) + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + @OptIn(ExperimentalCoroutinesApi::class) + private val addTabToAccount = addTabToAccountId.flatMapLatest { + accountStore.state.map { state -> + it?.let { + state.get(it) + } ?: state.currentAccount + } }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) @OptIn(ExperimentalCoroutinesApi::class) @@ -66,11 +84,16 @@ class ClipListViewModel @Inject constructor( ResultState.Loading(StateContent.NotExist()) ) - private val clipItemStatuses = combine(clips, currentAccount) { clipsState, account -> + private val clipItemStatuses = combine( + clips, + addTabToAccount, + currentAccount + ) { clipsState, addTabToAccount, account -> clipsState.convert { clips -> clips.map { clip -> - val isAddedToTab = account?.pages?.any { + val isAddedToTab = (addTabToAccount ?: account)?.pages?.any { clip.id.clipId == it.pageParams.clipId + && (it.attachedAccountId ?: it.accountId) == account?.accountId } ClipItemState(clip, isAddedToTab ?: false) } @@ -81,20 +104,24 @@ class ClipListViewModel @Inject constructor( ResultState.Loading(StateContent.NotExist()) ) - val uiState = combine(currentAccount, clipItemStatuses) { ac, statuses -> + val uiState = combine( + currentAccount, + addTabToAccount, + clipItemStatuses + ) { ac, addTabToAccount, statuses -> ClipListUiState( - ac, - statuses + ac, addTabToAccount, statuses ) }.stateIn( - viewModelScope, - SharingStarted.WhileSubscribed(5_000), - ClipListUiState() + viewModelScope, SharingStarted.WhileSubscribed(5_000), ClipListUiState() ) fun onToggleAddToTabButtonClicked(clipItemState: ClipItemState) { viewModelScope.launch { - toggleClipAddToTabUseCase(clipItemState.clip) + toggleClipAddToTabUseCase( + clipItemState.clip, + savedStateHandle[ClipListNavigationImpl.EXTRA_ADD_TAB_TO_ACCOUNT_ID] + ) } } @@ -102,7 +129,7 @@ class ClipListViewModel @Inject constructor( val mode = savedStateHandle.get(ClipListNavigationImpl.EXTRA_MODE)?.let { ClipListNavigationArgs.Mode.valueOf(it) } ?: ClipListNavigationArgs.Mode.View - when(mode) { + when (mode) { ClipListNavigationArgs.Mode.AddToTab -> { onToggleAddToTabButtonClicked(clipItemState) } @@ -118,5 +145,6 @@ data class ClipItemState( data class ClipListUiState( val account: Account? = null, + val addToTabAccount: Account? = null, val clipStatusesState: ResultState> = ResultState.Loading(StateContent.NotExist()), ) \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt index 2bfc200739..740af806a9 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt @@ -15,20 +15,35 @@ class ToggleClipAddToTabUseCase @Inject constructor( private val accountRepository: AccountRepository, ) : UseCase { - suspend operator fun invoke(clip: Clip): Result = runCancellableCatching { - val account = accountRepository.get(clip.id.accountId).getOrThrow() + suspend operator fun invoke(clip: Clip, addTabToAccountId: Long?): Result = runCancellableCatching { + val account = addTabToAccountId?.let { + accountRepository.get(it).getOrThrow() + } ?: accountRepository.get(clip.id.accountId).getOrThrow() val existsPage = account.pages.firstOrNull { it.pageParams.clipId == clip.id.clipId + && (it.attachedAccountId ?: it.accountId) == clip.id.accountId + } + val isSameAccount = account.accountId == clip.id.accountId + val relatedAccount = if (isSameAccount) { + account + } else { + accountRepository.get(clip.id.accountId).getOrThrow() + } + val title = if (isSameAccount) { + clip.name + } else { + "${clip.name}(${relatedAccount.getAcct()})" } if (existsPage == null) { accountService.add( Page( - title = clip.name, + title = title, weight = -1, accountId = clip.id.accountId, pageable = Pageable.ClipNotes( clipId = clip.id.clipId, - ) + ), + attachedAccountId = if (isSameAccount) null else relatedAccount.accountId, ) ) } else { From bb29c698d11e8df413cb348099d9d8809ed518c3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 11:48:49 +0900 Subject: [PATCH 294/432] =?UTF-8?q?feat:=20=E8=BF=BD=E5=8A=A0=E5=85=88?= =?UTF-8?q?=E3=81=AE=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3=E3=83=88=E3=81=A8?= =?UTF-8?q?=E3=82=BF=E3=83=96=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=81=9F=E3=82=81=E3=81=AE=E3=82=A2=E3=82=AB=E3=82=A6=E3=83=B3?= =?UTF-8?q?=E3=83=88=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=97=E3=81=A6=E8=B5=B7?= =?UTF-8?q?=E5=8B=95=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/setting/activities/PageSettingActivity.kt | 6 +++++- .../setting/viewmodel/page/PageCandidateGenerator.kt | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt index e4e0e926bd..cbc43e99fe 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt @@ -92,7 +92,11 @@ class PageSettingActivity : AppCompatActivity() { ))) PageType.CLIP_NOTES -> startActivity( clipListNavigation.newIntent( - ClipListNavigationArgs(mode = ClipListNavigationArgs.Mode.AddToTab) + ClipListNavigationArgs( + mode = ClipListNavigationArgs.Mode.AddToTab, + accountId = pt.relatedAccount.accountId, + addTabToAccountId = mPageSettingViewModel.account.value?.accountId + ) ) ) PageType.USERS_GALLERY_POSTS -> { diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt index 5579a77070..f204cbcd29 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt @@ -25,7 +25,6 @@ class PageCandidateGenerator @Inject constructor( val isSameAccount = related.accountId == currentAccount?.accountId || currentAccount == null val restrictionTypes = setOf( PageType.NOTIFICATION, - PageType.CLIP_NOTES, PageType.SEARCH, PageType.SEARCH, PageType.SEARCH_HASH, From 13e452ba4b4df3c0f42977131e667ffd802bb76e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 11:50:13 +0900 Subject: [PATCH 295/432] fix --- .../pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt index 740af806a9..20923f8719 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/clip/ToggleClipAddToTabUseCase.kt @@ -39,7 +39,7 @@ class ToggleClipAddToTabUseCase @Inject constructor( Page( title = title, weight = -1, - accountId = clip.id.accountId, + accountId = addTabToAccountId ?: clip.id.accountId, pageable = Pageable.ClipNotes( clipId = clip.id.clipId, ), From 048e08d77bb71498a1d8694fa3ab33cd9ea7cd2d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 12:18:37 +0900 Subject: [PATCH 296/432] =?UTF-8?q?feat:=20=E3=82=A2=E3=82=AB=E3=82=A6?= =?UTF-8?q?=E3=83=B3=E3=83=88=E3=81=AEId=E3=82=92=E6=8C=87=E5=AE=9A?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../viewmodel/NotificationViewModel.kt | 36 +++++++++++++++---- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewModel.kt b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewModel.kt index 076869866d..86a0bf2785 100644 --- a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewModel.kt +++ b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewModel.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.notification.viewmodel +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope @@ -14,6 +15,7 @@ import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.common.* import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.common_android_ui.APIErrorStringConverter +import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.UnauthorizedException import net.pantasystem.milktea.model.account.page.Pageable @@ -36,16 +38,21 @@ class NotificationViewModel @Inject constructor( planeNoteViewDataCacheFactory: PlaneNoteViewDataCache.Factory, loggerFactory: Logger.Factory, accountStore: AccountStore, - notificationPagingStoreFactory: NotificationPagingStore.Factory + notificationPagingStoreFactory: NotificationPagingStore.Factory, + private val savedStateHandle: SavedStateHandle, ) : ViewModel() { + companion object { + const val EXTRA_SPECIFIED_ACCOUNT_ID = "NotificationViewModel.EXTRA_SPECIFIED_ACCOUNT_ID" + } + private val planeNoteViewDataCache: PlaneNoteViewDataCache = planeNoteViewDataCacheFactory.create({ - accountRepository.getCurrentAccount().getOrThrow() + getCurrentAccount() }, viewModelScope) private val notificationPagingStore = notificationPagingStoreFactory.create { - accountRepository.getCurrentAccount().getOrThrow() + getCurrentAccount() } private val notificationPageableState = notificationPagingStore.notifications.map { state -> @@ -87,13 +94,21 @@ class NotificationViewModel @Inject constructor( private val logger = loggerFactory.create("NotificationViewModel") + private val currentAccount = savedStateHandle.getStateFlow(EXTRA_SPECIFIED_ACCOUNT_ID, null).flatMapLatest { accountId -> + accountStore.state.map { state -> + accountId?.let { + state.get(it) + } ?: state.currentAccount + } + }.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), null) + init { - accountStore.observeCurrentAccount.filterNotNull().flowOn(Dispatchers.IO) + currentAccount.filterNotNull().flowOn(Dispatchers.IO) .onEach { loadInit() }.launchIn(viewModelScope) - accountStore.observeCurrentAccount.filterNotNull().flatMapLatest { ac -> + currentAccount.filterNotNull().flatMapLatest { ac -> notificationStreaming.connect { ac }.map { @@ -192,7 +207,9 @@ class NotificationViewModel @Inject constructor( fun onMarkAsReadAllNotifications() { viewModelScope.launch { - accountRepository.getCurrentAccount().mapCancellableCatching { + runCancellableCatching { + getCurrentAccount() + }.mapCancellableCatching { notificationRepository.markAsRead(it.accountId) }.onSuccess { loadInit() @@ -203,6 +220,13 @@ class NotificationViewModel @Inject constructor( } } + private suspend fun getCurrentAccount(): Account { + return savedStateHandle.get(EXTRA_SPECIFIED_ACCOUNT_ID)?.let { accountId -> + return accountRepository.get(accountId).getOrThrow() + } ?: accountRepository.getCurrentAccount().getOrThrow() + } + + } sealed interface NotificationListItem { From 6e0a75781262273ab0c9d5828d96d5b9cb4bfeec Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 12:18:55 +0900 Subject: [PATCH 297/432] =?UTF-8?q?feat:=20=E4=BB=96=E3=82=A2=E3=82=AB?= =?UTF-8?q?=E3=82=A6=E3=83=B3=E3=83=88=E3=81=AEId=E3=82=92=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=81=97=E3=81=A6=E9=80=9A=E7=9F=A5=E3=82=92=E3=82=BF?= =?UTF-8?q?=E3=83=96=E3=81=AB=E8=BF=BD=E5=8A=A0=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/PageableFragmentFactoryImpl.kt | 6 +++--- .../milktea/notification/NotificationFragment.kt | 12 ++++++++++++ .../setting/viewmodel/page/PageCandidateGenerator.kt | 2 -- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt b/app/src/main/java/jp/panta/misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt index 8433667229..6997edb4e1 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/ui/PageableFragmentFactoryImpl.kt @@ -19,10 +19,10 @@ class PageableFragmentFactoryImpl @Inject constructor(): PageableFragmentFactory NoteDetailFragment.newInstance(page) } is Pageable.Notification ->{ - NotificationFragment() + NotificationFragment.newInstance(page.attachedAccountId ?: page.accountId) } is Pageable.Gallery -> { - return GalleryPostsFragment.newInstance(pageable, page.accountId) + return GalleryPostsFragment.newInstance(pageable, page.attachedAccountId ?: page.accountId) } else ->{ TimelineFragment.newInstance(page) @@ -57,7 +57,7 @@ class PageableFragmentFactoryImpl @Inject constructor(): PageableFragmentFactory NoteDetailFragment.newInstance(pageable.noteId, accountId) } is Pageable.Notification ->{ - NotificationFragment() + NotificationFragment.newInstance(accountId) } is Pageable.Gallery -> { return GalleryPostsFragment.newInstance(pageable, accountId) diff --git a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt index 237f9e12e5..dfcb69e6a5 100644 --- a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt +++ b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/NotificationFragment.kt @@ -46,6 +46,18 @@ import javax.inject.Inject @AndroidEntryPoint class NotificationFragment : Fragment(R.layout.fragment_notification) { + companion object { + fun newInstance(specifiedAccountId: Long? = null): NotificationFragment { + return NotificationFragment().apply { + arguments = Bundle().apply { + specifiedAccountId?.also { + putLong(NotificationViewModel.EXTRA_SPECIFIED_ACCOUNT_ID, it) + } + } + } + } + } + lateinit var mLinearLayoutManager: LinearLayoutManager private val mViewModel: NotificationViewModel by viewModels() diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt index f204cbcd29..db98f260fa 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt @@ -24,8 +24,6 @@ class PageCandidateGenerator @Inject constructor( val isSameAccount = related.accountId == currentAccount?.accountId || currentAccount == null val restrictionTypes = setOf( - PageType.NOTIFICATION, - PageType.SEARCH, PageType.SEARCH, PageType.SEARCH_HASH, PageType.USER, From 88c2753e27c0b838006c844cfaa2a5f42ef24ac9 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 31 May 2023 12:20:02 +0900 Subject: [PATCH 298/432] =?UTF-8?q?feat:=20=E3=82=AE=E3=83=A3=E3=83=A9?= =?UTF-8?q?=E3=83=AA=E3=83=BC=E3=81=A7=E3=82=82=E8=BF=BD=E5=8A=A0=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../setting/viewmodel/page/PageCandidateGenerator.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt index db98f260fa..b86110898d 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/page/PageCandidateGenerator.kt @@ -28,12 +28,6 @@ class PageCandidateGenerator @Inject constructor( PageType.SEARCH_HASH, PageType.USER, PageType.DETAIL, - PageType.GALLERY_FEATURED, - PageType.GALLERY_POPULAR, - PageType.GALLERY_POSTS, - PageType.MY_GALLERY_POSTS, - PageType.I_LIKED_GALLERY_POSTS, - PageType.USERS_GALLERY_POSTS, ) return when (related.instanceType) { Account.InstanceType.MISSKEY -> { From d88ca9daf39d473f8680432585cb11db22ec119c Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 1 Jun 2023 21:08:42 +0900 Subject: [PATCH 299/432] =?UTF-8?q?feat:=20=E3=82=B9=E3=83=AF=E3=82=A4?= =?UTF-8?q?=E3=83=97=E3=81=A7MediaActivity=E3=82=92=E4=BF=AE=E4=BA=86?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/media/MediaActivity.kt | 3 + .../milktea/media/PhotoViewViewPager.kt | 103 +++++++++++++++++- 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt index 596647b56b..e6f7026fef 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt @@ -117,6 +117,9 @@ class MediaActivity : AppCompatActivity() { val pagerAdapter = MediaPagerAdapter(list) mBinding.mediaViewPager.adapter = pagerAdapter + mBinding.mediaViewPager.setOnFinishEventListener { + finish() + } mBinding.mediaViewPager.currentItem = fileCurrentIndex } diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt index 6ea6fc37a9..0286be18ff 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt @@ -2,8 +2,11 @@ package net.pantasystem.milktea.media import android.content.Context import android.util.AttributeSet +import android.view.GestureDetector import android.view.MotionEvent import androidx.viewpager.widget.ViewPager +import com.github.chrisbanes.photoview.PhotoView +import kotlin.math.abs /** @@ -11,15 +14,113 @@ import androidx.viewpager.widget.ViewPager */ class PhotoViewViewPager : ViewPager { + fun interface OnFinishEventListener { + fun onFinish() + } + + companion object { + + private const val SWIPE_THRESHOLD = 100 + + private const val SWIPE_VELOCITY_THRESHOLD = 100 + } + constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet): super(context, attrs) + private var onFinishEventListener: OnFinishEventListener? = null + + fun setOnFinishEventListener(listener: OnFinishEventListener?){ + onFinishEventListener = listener + } + + override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { return try{ - super.onInterceptTouchEvent(ev) + if (ev?.action == MotionEvent.ACTION_DOWN) { + startY = ev.y + } + if (ev != null) { + return gestureDetector.onTouchEvent(ev) || super.onInterceptTouchEvent(ev) + } + return super.onInterceptTouchEvent(null) }catch(e: IllegalArgumentException){ false } } + + + private var startY = 0f + + var lastY = 0f + var totalTranslationY = 0f + + + private val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { + + override fun onScroll( + e1: MotionEvent, + e2: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + totalTranslationY += e2.rawY - lastY + viewToMove()?.translationY = totalTranslationY + viewToMove()?.rotation = totalTranslationY / 10 + lastY = e2.rawY + return super.onScroll(e1, e2, distanceX, distanceY) + } + + override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { + var result = false + try { + val diffY = e2.y.minus(e1.y) + if (abs(diffY) > SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { + if (diffY <= 0) { + onSwipeTop() + result = true + } + } + } catch (exception: Exception) { + exception.printStackTrace() + } + if (!result) { + viewToMove()?.animate()?.translationY(0f)?.setDuration(200)?.start() + totalTranslationY = 0f + } + return result + } + + override fun onDown(e: MotionEvent): Boolean { + lastY = e.rawY + return super.onDown(e) + } + + + }) + + + private fun onSwipeTop() { + val currentItemView = try { + findViewById(R.id.imageView) + } catch (e: Exception) { + null + } + if (currentItemView == null || currentItemView.scale == 1.0F) { + // Only close if not zoomed in + onFinishEventListener?.onFinish() +// (context as? Activity)?.finish() + } + } + + private fun viewToMove(): PhotoView? { + return try { + findViewById(R.id.imageView) + } catch (e: Exception) { + null + } + } + + } \ No newline at end of file From 2dde3c4465ffa1fe3c7ede2a25d49ff03398a296 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 1 Jun 2023 21:22:34 +0900 Subject: [PATCH 300/432] =?UTF-8?q?feat:=20video=E3=82=82=E3=82=B9?= =?UTF-8?q?=E3=83=AF=E3=82=A4=E3=83=97=E3=81=A7=E7=B5=82=E4=BA=86=E3=81=A7?= =?UTF-8?q?=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/media/PhotoViewViewPager.kt | 57 +++++++++++++------ 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt index 0286be18ff..3d1164461e 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt @@ -4,6 +4,7 @@ import android.content.Context import android.util.AttributeSet import android.view.GestureDetector import android.view.MotionEvent +import android.view.View import androidx.viewpager.widget.ViewPager import com.github.chrisbanes.photoview.PhotoView import kotlin.math.abs @@ -20,9 +21,9 @@ class PhotoViewViewPager : ViewPager { companion object { - private const val SWIPE_THRESHOLD = 100 + private const val SWIPE_THRESHOLD = 150 - private const val SWIPE_VELOCITY_THRESHOLD = 100 + private const val SWIPE_VELOCITY_THRESHOLD = 150 } constructor(context: Context) : super(context) @@ -41,6 +42,12 @@ class PhotoViewViewPager : ViewPager { if (ev?.action == MotionEvent.ACTION_DOWN) { startY = ev.y } + when(ev?.action) { + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + getPhotoView()?.animate()?.translationY(0f)?.rotation(0f)?.setDuration(200)?.start() + totalTranslationY = 0f + } + } if (ev != null) { return gestureDetector.onTouchEvent(ev) || super.onInterceptTouchEvent(ev) } @@ -65,10 +72,14 @@ class PhotoViewViewPager : ViewPager { distanceX: Float, distanceY: Float ): Boolean { - totalTranslationY += e2.rawY - lastY - viewToMove()?.translationY = totalTranslationY - viewToMove()?.rotation = totalTranslationY / 10 - lastY = e2.rawY + + if (isNotScaled()) { + totalTranslationY += e2.rawY - lastY + viewToMove()?.translationY = totalTranslationY + viewToMove()?.rotation = totalTranslationY / 10 + lastY = e2.rawY + } + return super.onScroll(e1, e2, distanceX, distanceY) } @@ -85,10 +96,8 @@ class PhotoViewViewPager : ViewPager { } catch (exception: Exception) { exception.printStackTrace() } - if (!result) { - viewToMove()?.animate()?.translationY(0f)?.setDuration(200)?.start() - totalTranslationY = 0f - } + viewToMove()?.animate()?.translationY(0f)?.rotation(0f)?.setDuration(200)?.start() + totalTranslationY = 0f return result } @@ -102,25 +111,37 @@ class PhotoViewViewPager : ViewPager { private fun onSwipeTop() { - val currentItemView = try { - findViewById(R.id.imageView) - } catch (e: Exception) { - null - } - if (currentItemView == null || currentItemView.scale == 1.0F) { + + if (isNotScaled()) { // Only close if not zoomed in onFinishEventListener?.onFinish() // (context as? Activity)?.finish() } } - private fun viewToMove(): PhotoView? { + private fun getPhotoView(): PhotoView? { return try { - findViewById(R.id.imageView) + findViewById(R.id.imageView) } catch (e: Exception) { null } } + private fun getPlayerView(): View? { + return try { + findViewById(R.id.player_view) + } catch (e: Exception) { + null + } + } + + private fun viewToMove(): View? { + return getPhotoView() ?: getPlayerView() + } + + private fun isNotScaled(): Boolean { + return getPhotoView() == null || getPhotoView()?.scale == 1.0F + } + } \ No newline at end of file From 9ef7ff39401bdf268d219942c91d4db29c0c2e98 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 1 Jun 2023 22:59:54 +0900 Subject: [PATCH 301/432] =?UTF-8?q?feat:=20title=E3=82=92=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=97=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=80=81elevation=E3=82=920=E3=81=AB=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/java/net/pantasystem/milktea/media/MediaActivity.kt | 1 + modules/features/media/src/main/res/layout/activity_media.xml | 1 + 2 files changed, 2 insertions(+) diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt index e6f7026fef..27de6e0b1d 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt @@ -88,6 +88,7 @@ class MediaActivity : AppCompatActivity() { mBinding = DataBindingUtil.setContentView(this, R.layout.activity_media) setSupportActionBar(mBinding.mediaToolbar) supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.title = "" val file = intent.getSerializableExtra(MediaNavigationKeys.EXTRA_FILE) as File? diff --git a/modules/features/media/src/main/res/layout/activity_media.xml b/modules/features/media/src/main/res/layout/activity_media.xml index 54e5396221..70109ac8a0 100644 --- a/modules/features/media/src/main/res/layout/activity_media.xml +++ b/modules/features/media/src/main/res/layout/activity_media.xml @@ -26,6 +26,7 @@ android:theme="?attr/actionBarTheme" android:layout_gravity="top" android:backgroundTint="#00000000" + android:elevation="0dp" /> From 4bfc4238950ccd5655fb8fd3e88582d22efd2d23 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 2 Jun 2023 03:08:58 +0900 Subject: [PATCH 302/432] =?UTF-8?q?feat:=20x2,=20x3,=20x4=E3=82=92?= =?UTF-8?q?=E8=A1=A8=E7=A4=BA=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86?= =?UTF-8?q?=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_android/mfm/ElementType.kt | 5 +++- .../milktea/common_android/mfm/MFMContract.kt | 6 ++++ .../milktea/common_android/mfm/MFMParser.kt | 30 ++++++++++++++++++- .../milktea/common_android_ui/MFMDecorator.kt | 9 ++++++ 4 files changed, 48 insertions(+), 2 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/ElementType.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/ElementType.kt index 52544ab375..9fcc49e964 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/ElementType.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/ElementType.kt @@ -21,7 +21,10 @@ enum class ElementType(val elementClass: ElementClass) { TEXT(ElementClass.TEXT), EMOJI(ElementClass.EMOJI), MENTION(ElementClass.LINK), - HASH_TAG(ElementClass.LINK) + HASH_TAG(ElementClass.LINK), + FnX2(ElementClass.STANDARD), + FnX3(ElementClass.STANDARD), + FnX4(ElementClass.STANDARD), } enum class ElementClass(val weight: Int){ diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/MFMContract.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/MFMContract.kt index 1db7708722..ba496645f8 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/MFMContract.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/MFMContract.kt @@ -16,4 +16,10 @@ object MFMContract { "motion" to TagType.MOTION, "jump" to TagType.JUMP*/ ) + + val fnTypeTagNameMap = mapOf( + "x2" to ElementType.FnX2, + "x3" to ElementType.FnX3, + "x4" to ElementType.FnX4 + ) } \ No newline at end of file diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/MFMParser.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/MFMParser.kt index bd560b7609..ff8db37585 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/MFMParser.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/mfm/MFMParser.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.common_android.mfm +import android.util.Log import jp.panta.misskeyandroidclient.mfm.* import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common_android.emoji.V13EmojiUrlResolver @@ -28,6 +29,8 @@ object MFMParser { private val idPattern = Pattern.compile("""^([a-zA-Z0-9]+)$""") + private val fnPattern = Pattern.compile("""\A\$\[([a-z\d]+) (.+?)]""", Pattern.DOTALL) + fun parse( text: String?, @@ -116,13 +119,14 @@ object MFMParser { '>' to listOf(::parseQuote), //引用 '*' to listOf(::parseTypeStar), // 横伸縮対称揺れ, 太字 '【' to listOf(::parseTitle),//タイトル + '$' to listOf(::parseFn), '[' to listOf(::parseSearch, ::parseLink, ::parseTitle), '?' to listOf(::parseLink), 'S' to listOf(::parseSearch), ':' to listOf(::parseEmoji), '@' to listOf(::parseMention), '#' to listOf(::parseHashTag), - 'h' to listOf(::parseUrl) + 'h' to listOf(::parseUrl), ) @@ -251,6 +255,30 @@ object MFMParser { } } + private fun parseFn(): Node? { + Log.d("parseFn", "parseFn: ${sourceText.substring(position, parent.insideEnd)}") + // $[x2 任意のテキスト]みたいのをParseする + val matcher = fnPattern.matcher(sourceText.substring(position, parent.insideEnd)) + + if (!matcher.find()) { + return null + } + val tagName = matcher.nullableGroup(1).also { + Log.d("parseFn", "parseFn: $it, ${matcher.nullableGroup(0)}") + } ?: return null + val tag = MFMContract.fnTypeTagNameMap[tagName] ?: return null + if (parent.elementType.elementClass.weight < tag.elementClass.weight) { + return null + } + return Node( + start = position, + end = position + matcher.end(), + insideStart = position + tagName.length + 3, + insideEnd = position + matcher.end(2), + elementType = tag + ) + } + private fun parseStrike(): Node? { val pattern = Pattern.compile("""\A~~(.+?)~~""", Pattern.DOTALL) val matcher = pattern.matcher(sourceText.substring(position, parent.insideEnd)) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt index 714c63dd74..cc2d375513 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt @@ -264,6 +264,15 @@ object MFMDecorator { ElementType.SMALL -> { setSpan(RelativeSizeSpan(0.6F)) } + ElementType.FnX2 -> { + setSpan(RelativeSizeSpan(2.0F)) + } + ElementType.FnX3 -> { + setSpan(RelativeSizeSpan(3.0F)) + } + ElementType.FnX4 -> { + setSpan(RelativeSizeSpan(4.0F)) + } ElementType.ROOT -> { } From 8fcd75fc7bc97ef7a6cf90d9c775251ac6536e96 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 20:22:18 +0000 Subject: [PATCH 303/432] build(deps): bump github.com/gin-gonic/gin in /server/api Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.8.1 to 1.9.1. - [Release notes](https://github.com/gin-gonic/gin/releases) - [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md) - [Commits](https://github.com/gin-gonic/gin/compare/v1.8.1...v1.9.1) --- updated-dependencies: - dependency-name: github.com/gin-gonic/gin dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- server/api/go.mod | 39 +++++++++-------- server/api/go.sum | 109 ++++++++++++++++++++++++---------------------- 2 files changed, 80 insertions(+), 68 deletions(-) diff --git a/server/api/go.mod b/server/api/go.mod index b81fe4ace8..0aaceafdbb 100644 --- a/server/api/go.mod +++ b/server/api/go.mod @@ -4,21 +4,24 @@ go 1.18 require ( github.com/aead/ecdh v0.2.0 - github.com/gin-gonic/gin v1.8.1 + github.com/gin-gonic/gin v1.9.1 github.com/google/uuid v1.3.0 - github.com/stretchr/testify v1.8.0 - golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 + github.com/stretchr/testify v1.8.3 + golang.org/x/crypto v0.9.0 gorm.io/driver/postgres v1.4.4 gorm.io/gorm v1.24.0 ) require ( + github.com/bytedance/sonic v1.9.1 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/go-playground/locales v0.14.0 // indirect - github.com/go-playground/universal-translator v0.18.0 // indirect - github.com/go-playground/validator/v10 v10.10.0 // indirect - github.com/goccy/go-json v0.9.7 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.13.0 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -30,18 +33,20 @@ require ( github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/leodido/go-urn v1.2.1 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect + github.com/leodido/go-urn v1.2.4 // indirect github.com/lib/pq v1.10.7 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.1 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/ugorji/go/codec v1.2.7 // indirect - golang.org/x/net v0.0.0-20221004154528-8021a29435af // indirect - golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 // indirect - golang.org/x/text v0.3.8 // indirect - google.golang.org/protobuf v1.28.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + golang.org/x/arch v0.3.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/server/api/go.sum b/server/api/go.sum index a6e71cd8e3..cead5fdb58 100644 --- a/server/api/go.sum +++ b/server/api/go.sum @@ -3,32 +3,38 @@ github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030I github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ= github.com/aead/ecdh v0.2.0/go.mod h1:a9HHtXuSo8J1Js1MwLQx2mBhkXMT6YwUmVVEY4tTB8U= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= -github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= -github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= -github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= -github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= -github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= -github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -93,19 +99,19 @@ github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= -github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -117,23 +123,20 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= -github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= @@ -147,18 +150,22 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= -github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= @@ -171,6 +178,9 @@ go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9E go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= +golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= @@ -182,8 +192,8 @@ golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 h1:x8vtB3zMecnlqZIwJNUUpwYKYSqCz5jXbiyv0ZJJZeI= -golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= @@ -193,8 +203,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20221004154528-8021a29435af h1:wv66FM3rLZGPdxpYL+ApnDe2HzHcTFta3z5nsc13wI4= -golang.org/x/net v0.0.0-20221004154528-8021a29435af/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -209,10 +219,10 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875 h1:AzgQNqF+FKwyQ5LbVrVqOcuuFB67N47F9+htZYH0wFM= -golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -221,8 +231,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= @@ -239,19 +249,15 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.4.4 h1:zt1fxJ+C+ajparn0SteEnkoPg0BQ6wOWXEQ99bteAmw= @@ -260,3 +266,4 @@ gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk= gorm.io/gorm v1.24.0 h1:j/CoiSm6xpRpmzbFJsQHYj+I8bGYWLXVHeYEyyKlF74= gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From a29fa507c056b693d680adfcbf861839c9b8dc7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 1 Jun 2023 20:33:30 +0000 Subject: [PATCH 304/432] build(deps): bump github.com/gin-gonic/gin in /push-to-fcm Bumps [github.com/gin-gonic/gin](https://github.com/gin-gonic/gin) from 1.9.0 to 1.9.1. - [Release notes](https://github.com/gin-gonic/gin/releases) - [Changelog](https://github.com/gin-gonic/gin/blob/master/CHANGELOG.md) - [Commits](https://github.com/gin-gonic/gin/compare/v1.9.0...v1.9.1) --- updated-dependencies: - dependency-name: github.com/gin-gonic/gin dependency-type: indirect ... Signed-off-by: dependabot[bot] --- push-to-fcm/go.mod | 36 +++++++++++++++++-------------- push-to-fcm/go.sum | 54 ++++++++++++++++++++++++++-------------------- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/push-to-fcm/go.mod b/push-to-fcm/go.mod index 2873f60bda..1404209b83 100644 --- a/push-to-fcm/go.mod +++ b/push-to-fcm/go.mod @@ -3,41 +3,45 @@ module systems.panta.milktea/push-to-fcm go 1.20 require ( - github.com/aead/ecdh v0.2.0 // indirect - github.com/bytedance/sonic v1.8.6 // indirect + github.com/aead/ecdh v0.2.0 + github.com/gin-gonic/gin v1.9.1 + github.com/google/uuid v1.3.0 + github.com/joho/godotenv v1.5.1 + github.com/stretchr/testify v1.8.3 + gorm.io/driver/postgres v1.5.0 + gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 +) + +require ( + github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect - github.com/gin-gonic/gin v1.9.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.12.0 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/google/uuid v1.3.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/pgx/v5 v5.3.1 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect - github.com/joho/godotenv v1.5.1 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect - github.com/leodido/go-urn v1.2.2 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/pelletier/go-toml/v2 v2.0.7 // indirect + github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.8.2 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.11 // indirect golang.org/x/arch v0.3.0 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/net v0.8.0 // indirect - golang.org/x/sys v0.6.0 // indirect - golang.org/x/text v0.8.0 // indirect + golang.org/x/crypto v0.9.0 // indirect + golang.org/x/net v0.10.0 // indirect + golang.org/x/sys v0.8.0 // indirect + golang.org/x/text v0.9.0 // indirect google.golang.org/protobuf v1.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gorm.io/driver/postgres v1.5.0 // indirect - gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 // indirect ) diff --git a/push-to-fcm/go.sum b/push-to-fcm/go.sum index 270f925153..72c54c2f14 100644 --- a/push-to-fcm/go.sum +++ b/push-to-fcm/go.sum @@ -1,8 +1,8 @@ github.com/aead/ecdh v0.2.0 h1:pYop54xVaq/CEREFEcukHRZfTdjiWvYIsZDXXrBapQQ= github.com/aead/ecdh v0.2.0/go.mod h1:a9HHtXuSo8J1Js1MwLQx2mBhkXMT6YwUmVVEY4tTB8U= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= -github.com/bytedance/sonic v1.8.6 h1:aUgO9S8gvdN6SyW2EhIpAw5E4ChworywIEndZCkCVXk= -github.com/bytedance/sonic v1.8.6/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= @@ -10,19 +10,23 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-gonic/gin v1.9.0 h1:OjyFBKICoexlu99ctXNR2gg+c5pKrKMuyjgARg9qeY8= -github.com/gin-gonic/gin v1.9.0/go.mod h1:W1Me9+hsUSyj3CePGrd1/QrKJMSJ1Tu/0hFEH89961k= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.12.0 h1:E4gtWgxWxp8YSxExrQFv5BpCahla0PVF2oTTEYaWQGI= -github.com/go-playground/validator/v10 v10.12.0/go.mod h1:hCAPuzYvKdP33pxWa+2+6AIKXEKqjIUyqsNCtbsSJrA= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= @@ -48,25 +52,27 @@ github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZX github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.2 h1:7z68G0FCGvDk646jz1AelTYNYWrTNm0bEcFAo147wt4= -github.com/leodido/go-urn v1.2.2/go.mod h1:kUaIbLZWttglzwNuG0pgsh5vuV6u2YcGBYz1hIPjtOQ= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/pelletier/go-toml/v2 v2.0.7 h1:muncTPStnKRos5dpVKULv2FVd4bMOhNePj9CjgDb8Us= -github.com/pelletier/go-toml/v2 v2.0.7/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= +github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rwtodd/Go.Sed v0.0.0-20210816025313-55464686f9ef/go.mod h1:8AEUvGVi2uQ5b24BIhcr0GCcpd/RNAFWaN2CJFrWIIQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -75,8 +81,9 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY= +github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= @@ -89,15 +96,15 @@ golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnf golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= +golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -108,8 +115,9 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -117,18 +125,20 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -136,8 +146,6 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/postgres v1.5.0 h1:u2FXTy14l45qc3UeCJ7QaAXZmZfDDv0YrthvmRq1l0U= gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A= -gorm.io/gorm v1.24.6 h1:wy98aq9oFEetsc4CAbKD2SoBCdMzsbSIvSUUFJuHi5s= -gorm.io/gorm v1.24.6/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11 h1:9qNbmu21nNThCNnF5i2R3kw2aL27U8ZwbzccNjOmW0g= gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= From 68bf3b6ac9fb4ada8a5ba99a8c0fd52506a299fd Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 2 Jun 2023 14:55:13 +0900 Subject: [PATCH 305/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E3=81=AE?= =?UTF-8?q?=E9=A0=85=E7=9B=AE=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/settings/Config.kt | 8 +++++++- .../milktea/data/infrastructure/settings/ConfigKtTest.kt | 4 ++++ .../milktea/data/infrastructure/settings/KeysKtTest.kt | 8 ++++++-- .../java/net/pantasystem/milktea/model/setting/Config.kt | 2 ++ .../java/net/pantasystem/milktea/model/setting/Keys.kt | 4 ++++ 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt index fd1c5872d6..6463cbb8c8 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt @@ -126,7 +126,10 @@ fun Config.Companion.from(map: Map): Config { )?.value ?: DefaultConfig.config.noteHeaderFontSize, noteContentFontSize = map.getValue( Keys.NoteContentFontSize - )?.value ?: DefaultConfig.config.noteContentFontSize + )?.value ?: DefaultConfig.config.noteContentFontSize, + isDisplayTimestampsAsAbsoluteDates = map.getValue( + Keys.IsDisplayTimestampsAsAbsoluteDates + )?.value ?: DefaultConfig.config.isDisplayTimestampsAsAbsoluteDates, ) } @@ -229,6 +232,9 @@ fun Config.pref(key: Keys): PrefType { Keys.NoteHeaderFontSize -> { PrefType.FloatPref(noteHeaderFontSize) } + Keys.IsDisplayTimestampsAsAbsoluteDates -> { + PrefType.BoolPref(isDisplayTimestampsAsAbsoluteDates) + } } } diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt index 93ff06ecf9..cf8b637119 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt @@ -147,6 +147,10 @@ class ConfigKtTest { config.noteHeaderFontSize, (u as PrefType.FloatPref).value ) + Keys.IsDisplayTimestampsAsAbsoluteDates -> Assertions.assertEquals( + config.isDisplayTimestampsAsAbsoluteDates, + (u as PrefType.BoolPref).value + ) } } } diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt index 5b4d52d74e..3feee59d63 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt @@ -108,6 +108,10 @@ class KeysKtTest { "NoteHeaderFontSize", key.str() ) + Keys.IsDisplayTimestampsAsAbsoluteDates -> Assertions.assertEquals( + "IsDisplayTimestampsAsAbsoluteDates", + key.str() + ) } } } @@ -115,8 +119,8 @@ class KeysKtTest { @Test fun checkAllKeysCount() { - Assertions.assertEquals(29, Keys.allKeys.size) - Assertions.assertEquals(29, Keys.allKeys.map { it.str() }.toSet().size) + Assertions.assertEquals(30, Keys.allKeys.size) + Assertions.assertEquals(30, Keys.allKeys.map { it.str() }.toSet().size) } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt index 83b10e5fc8..1ea943e1ff 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt @@ -81,6 +81,7 @@ data class Config( val isHideMediaWhenMobileNetwork: Boolean, val noteHeaderFontSize: Float, val noteContentFontSize: Float, + val isDisplayTimestampsAsAbsoluteDates: Boolean, ) { companion object @@ -138,6 +139,7 @@ object DefaultConfig { isHideMediaWhenMobileNetwork = false, noteContentFontSize = 15f, noteHeaderFontSize = 15f, + isDisplayTimestampsAsAbsoluteDates = false, ) fun getRememberVisibilityConfig(accountId: Long): RememberVisibility.Remember { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt index fc25b4fbbd..7576726a1b 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt @@ -31,6 +31,7 @@ val Keys.Companion.allKeys by lazy { Keys.IsHideMediaWhenMobileNetwork, Keys.NoteHeaderFontSize, Keys.NoteContentFontSize, + Keys.IsDisplayTimestampsAsAbsoluteDates, ) } @@ -88,6 +89,8 @@ sealed interface Keys { object NoteContentFontSize: Keys + object IsDisplayTimestampsAsAbsoluteDates: Keys + companion object } @@ -122,5 +125,6 @@ fun Keys.str(): String { is Keys.IsHideMediaWhenMobileNetwork -> "IsHideMediaWhenMobileNetwork" is Keys.NoteContentFontSize -> "NoteContentFontSize" is Keys.NoteHeaderFontSize -> "NoteHeaderFontSize" + is Keys.IsDisplayTimestampsAsAbsoluteDates -> "IsDisplayTimestampsAsAbsoluteDates" } } From 8161d44d88f4c81ffa8056ec1cc9e1e35744249a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 2 Jun 2023 15:08:00 +0900 Subject: [PATCH 306/432] =?UTF-8?q?feat:=20=E6=97=A5=E4=BB=98=E3=81=AE?= =?UTF-8?q?=E7=B5=B6=E5=AF=BE=E8=A1=A8=E7=A4=BA=E3=82=92=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/text/DateFormatHelper.kt | 20 +++++++++++++------ .../layout/item_note_editor_reply_to_note.xml | 1 + .../src/main/res/layout/item_simple_note.xml | 1 + .../activities/SettingAppearanceActivity.kt | 9 +++++++++ 4 files changed, 25 insertions(+), 6 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt index 9d9f8b5dcc..14563df08c 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt @@ -45,9 +45,9 @@ object DateFormatHelper { ).getString(context) } - @BindingAdapter("elapsedTime", "visibility") + @BindingAdapter("elapsedTime", "visibility", "isDisplayTimestampsAsAbsoluteDates") @JvmStatic - fun TextView.setElapsedTimeAndVisibility(elapsedTime: Instant?, visibility: Visibility?) { + fun TextView.setElapsedTimeAndVisibility(elapsedTime: Instant?, visibility: Visibility?, isDisplayTimestampsAsAbsoluteDates: Boolean?) { val visibilityIcon = when(visibility ?: Visibility.Public(false)) { is Visibility.Followers -> R.drawable.ic_lock_black_24dp is Visibility.Home -> R.drawable.ic_home_black_24dp @@ -57,11 +57,19 @@ object DateFormatHelper { Visibility.Mutual -> R.drawable.ic_sync_alt_24px Visibility.Personal -> R.drawable.ic_person_black_24dp } - val text = GetElapsedTimeStringSource( - SimpleElapsedTime( - elapsedTime ?: Clock.System.now() + val text = if (isDisplayTimestampsAsAbsoluteDates == true) { + SimpleDateFormat.getDateTimeInstance().format( + elapsedTime?.let { + Date(it.toEpochMilliseconds()) + } ?: Date() ) - ).getString(context) + } else { + GetElapsedTimeStringSource( + SimpleElapsedTime( + elapsedTime ?: Clock.System.now() + ) + ).getString(context) + } this.text = if (visibilityIcon == null) { text diff --git a/modules/features/note/src/main/res/layout/item_note_editor_reply_to_note.xml b/modules/features/note/src/main/res/layout/item_note_editor_reply_to_note.xml index aecc9e7d6f..4fa559b402 100644 --- a/modules/features/note/src/main/res/layout/item_note_editor_reply_to_note.xml +++ b/modules/features/note/src/main/res/layout/item_note_editor_reply_to_note.xml @@ -92,6 +92,7 @@ android:ellipsize="end" elapsedTime="@{note.toShowNote.note.createdAt}" visibility="@{note.toShowNote.note.visibility}" + isDisplayTimestampsAsAbsoluteDates="@{note.config.displayTimestampsAsAbsoluteDates}" android:layout_alignParentEnd="true" android:gravity="end" tools:text="16分前" diff --git a/modules/features/note/src/main/res/layout/item_simple_note.xml b/modules/features/note/src/main/res/layout/item_simple_note.xml index 8fbcff46e0..98636b2d00 100644 --- a/modules/features/note/src/main/res/layout/item_simple_note.xml +++ b/modules/features/note/src/main/res/layout/item_simple_note.xml @@ -95,6 +95,7 @@ android:ellipsize="end" elapsedTime="@{note.toShowNote.note.createdAt}" visibility="@{note.toShowNote.note.visibility}" + isDisplayTimestampsAsAbsoluteDates="@{note.config.displayTimestampsAsAbsoluteDates}" android:layout_alignParentEnd="true" android:gravity="end" diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index 5ebbfda8e8..aed9ea4ef1 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -207,6 +207,15 @@ class SettingAppearanceActivity : AppCompatActivity() { ) { Text(stringResource(id = R.string.settings_visible_instance_domain_in_toolbar)) } + + SettingSwitchTile( + checked = currentConfigState.isDisplayTimestampsAsAbsoluteDates, + onChanged = { + currentConfigState = currentConfigState.copy(isDisplayTimestampsAsAbsoluteDates = it) + } + ) { + Text("Display timestamps as absolute dates") + } } SettingSection( title = stringResource(id = R.string.background_image), From f47345630aedc74a6b9e023fc1f1ef5fbcf044a9 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 2 Jun 2023 15:17:47 +0900 Subject: [PATCH 307/432] =?UTF-8?q?feat:=20=E9=80=9A=E7=9F=A5=E3=81=AE?= =?UTF-8?q?=E6=97=A5=E4=BB=98=E3=81=AE=E7=B5=B6=E5=AF=BE=E8=A1=A8=E7=A4=BA?= =?UTF-8?q?=E3=82=92=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/text/DateFormatHelper.kt | 20 +++++++++++++------ .../viewmodel/NotificationViewData.kt | 11 +++++++++- .../viewmodel/NotificationViewModel.kt | 4 ++++ .../src/main/res/layout/item_notification.xml | 1 + .../user/reaction/UserReactionBindingModel.kt | 2 ++ .../user/reaction/UserReactionsViewModel.kt | 8 +++++++- .../main/res/layout/item_user_reaction.xml | 1 + 7 files changed, 39 insertions(+), 8 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt index 14563df08c..5b16bc9fc2 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt @@ -34,15 +34,23 @@ object DateFormatHelper { - @BindingAdapter("elapsedTime") + @BindingAdapter("elapsedTime", "isDisplayTimestampsAsAbsoluteDates") @JvmStatic - fun TextView.setElapsedTime(elapsedTime: Instant?) { + fun TextView.setElapsedTime(elapsedTime: Instant?, isDisplayTimestampsAsAbsoluteDates: Boolean?) { - this.text = GetElapsedTimeStringSource( - SimpleElapsedTime( - elapsedTime ?: Clock.System.now() + this.text = if (isDisplayTimestampsAsAbsoluteDates == true) { + SimpleDateFormat.getDateTimeInstance().format( + elapsedTime?.let { + Date(it.toEpochMilliseconds()) + } ?: Date() ) - ).getString(context) + } else { + GetElapsedTimeStringSource( + SimpleElapsedTime( + elapsedTime ?: Clock.System.now() + ) + ).getString(context) + } } @BindingAdapter("elapsedTime", "visibility", "isDisplayTimestampsAsAbsoluteDates") diff --git a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewData.kt b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewData.kt index 1cd2bbe00f..b24a34c47b 100644 --- a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewData.kt +++ b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewData.kt @@ -1,14 +1,21 @@ package net.pantasystem.milktea.notification.viewmodel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn import net.pantasystem.milktea.common_android.resource.StringSource import net.pantasystem.milktea.model.notification.* +import net.pantasystem.milktea.model.setting.DefaultConfig +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData import net.pantasystem.milktea.notification.R class NotificationViewData( val notification: NotificationRelation, - val noteViewData: PlaneNoteViewData? + val noteViewData: PlaneNoteViewData?, + configRepository: LocalConfigRepository, + coroutineScope: CoroutineScope, ) { enum class Type(val default: String) { FOLLOW("follow"), @@ -63,6 +70,8 @@ class NotificationViewData( StringSource(R.string.follow_requested_by, name ?: "") } + val config = configRepository.observe().stateIn(coroutineScope, SharingStarted.WhileSubscribed(5_000), DefaultConfig.config) + override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false diff --git a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewModel.kt b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewModel.kt index 86a0bf2785..84693dc359 100644 --- a/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewModel.kt +++ b/modules/features/notification/src/main/java/net/pantasystem/milktea/notification/viewmodel/NotificationViewModel.kt @@ -22,6 +22,7 @@ import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.filter.WordFilterService import net.pantasystem.milktea.model.group.GroupRepository import net.pantasystem.milktea.model.notification.* +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.model.user.FollowRequestRepository import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewDataCache import javax.inject.Inject @@ -35,6 +36,7 @@ class NotificationViewModel @Inject constructor( private val followRequestRepository: FollowRequestRepository, private val notificationRepository: NotificationRepository, private val noteWordFilterService: WordFilterService, + private val configRepository: LocalConfigRepository, planeNoteViewDataCacheFactory: PlaneNoteViewDataCache.Factory, loggerFactory: Logger.Factory, accountStore: AccountStore, @@ -66,6 +68,8 @@ class NotificationViewModel @Inject constructor( NotificationViewData( n, noteViewData, + configRepository, + viewModelScope, ) } } diff --git a/modules/features/notification/src/main/res/layout/item_notification.xml b/modules/features/notification/src/main/res/layout/item_notification.xml index 2c9095382e..f74fad4607 100644 --- a/modules/features/notification/src/main/res/layout/item_notification.xml +++ b/modules/features/notification/src/main/res/layout/item_notification.xml @@ -105,6 +105,7 @@ tools:text="16min" android:singleLine="true" elapsedTime="@{notification.notification.notification.createdAt}" + isDisplayTimestampsAsAbsoluteDates="@{notification.config.displayTimestampsAsAbsoluteDates}" android:layout_gravity="center"/> , + val config: StateFlow, ) { val emojis get() = note.toShowNote.note.emojis diff --git a/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsViewModel.kt b/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsViewModel.kt index d0e55fc1d0..16098b6675 100644 --- a/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsViewModel.kt +++ b/modules/features/user/src/main/java/net/pantasystem/milktea/user/reaction/UserReactionsViewModel.kt @@ -13,6 +13,8 @@ import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common.PageableState import net.pantasystem.milktea.model.account.AccountRepository import net.pantasystem.milktea.model.account.CurrentAccountWatcher +import net.pantasystem.milktea.model.setting.DefaultConfig +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserDataSource import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewDataCache @@ -26,6 +28,7 @@ class UserReactionsViewModel @Inject constructor( loggerFactory: Logger.Factory, planeNoteViewDataCacheFactory: PlaneNoteViewDataCache.Factory, private val savedStateHandle: SavedStateHandle, + configRepository: LocalConfigRepository, ) : ViewModel() { // private val _userId = MutableStateFlow(null) companion object { @@ -48,6 +51,8 @@ class UserReactionsViewModel @Inject constructor( private val cache = planeNoteViewDataCacheFactory.create(currentAccountWatcher::getAccount, viewModelScope) + private val config = configRepository.observe().stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), DefaultConfig.config) + private val store = storeFactory.create(userId) val state = store.state.map { pageableState -> @@ -57,7 +62,8 @@ class UserReactionsViewModel @Inject constructor( reaction = it.reaction, user = userDataSource.observe(it.user.id) .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), it.user), - note = cache.get(it.note) + note = cache.get(it.note), + config = config, ) } } diff --git a/modules/features/user/src/main/res/layout/item_user_reaction.xml b/modules/features/user/src/main/res/layout/item_user_reaction.xml index 53b8eee90e..54594876ba 100644 --- a/modules/features/user/src/main/res/layout/item_user_reaction.xml +++ b/modules/features/user/src/main/res/layout/item_user_reaction.xml @@ -66,6 +66,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" elapsedTime="@{bindingModel.reaction.createdAt}" + isDisplayTimestampsAsAbsoluteDates="@{bindingModel.config.displayTimestampsAsAbsoluteDates}" android:textColor="?attr/colorPrimary" app:emojiCompatEnabled="false" /> From fcf87c0b0b6b7764819d2f355a332c14454e682d Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 2 Jun 2023 15:25:12 +0900 Subject: [PATCH 308/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index f390f2652b..dcf45de382 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -598,6 +598,7 @@ 下書き投稿を選択 スクロール位置を保持する 投稿のみ + タイムスタンプを絶対表示にする diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 225be03cd1..b6d055cd39 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -592,6 +592,7 @@ Select draft post Remember scroll position Post only + Display timestamps as absolute dates \ No newline at end of file diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index fad04a851d..9396735f78 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -597,4 +597,5 @@ Select draft post Remember scroll position Post only + Display timestamps as absolute dates From 17334e8646a3387e62549526a4fee8139296b42e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 2 Jun 2023 15:28:28 +0900 Subject: [PATCH 309/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/setting/activities/SettingAppearanceActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index aed9ea4ef1..5a862bc376 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -214,7 +214,7 @@ class SettingAppearanceActivity : AppCompatActivity() { currentConfigState = currentConfigState.copy(isDisplayTimestampsAsAbsoluteDates = it) } ) { - Text("Display timestamps as absolute dates") + Text(stringResource(id = R.string.settings_display_timestamps_as_absolute_dates)) } } SettingSection( From c64b3f7707917cf8fc4c18cb8bc7055e7796dacb Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 2 Jun 2023 15:36:05 +0900 Subject: [PATCH 310/432] =?UTF-8?q?feat:=20=E7=B5=B6=E5=AF=BE=E6=97=A5?= =?UTF-8?q?=E6=99=82=E8=A1=A8=E7=A4=BA=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/renote/RenotesViewModel.kt | 9 +++++++++ .../milktea/note/renote/item_renote_user.kt | 17 +++++++++++++++-- .../milktea/note/renote/renote_users.kt | 4 ++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt index 07755ea9a9..fc767354c9 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenotesViewModel.kt @@ -15,6 +15,8 @@ import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.model.notes.* import net.pantasystem.milktea.model.notes.repost.RenoteType import net.pantasystem.milktea.model.notes.repost.RenotesPagingService +import net.pantasystem.milktea.model.setting.DefaultConfig +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.model.user.User import net.pantasystem.milktea.model.user.UserRepository @@ -24,6 +26,7 @@ class RenotesViewModel @AssistedInject constructor( private val noteRepository: NoteRepository, private val noteCaptureAPIAdapter: NoteCaptureAPIAdapter, private val userRepository: UserRepository, + configRepository: LocalConfigRepository, accountStore: AccountStore, loggerFactory: Logger.Factory, @Assisted val noteId: Note.Id, @@ -43,6 +46,12 @@ class RenotesViewModel @AssistedInject constructor( private val logger = loggerFactory.create("RenotesVM") + val config = configRepository.observe().stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + DefaultConfig.config, + ) + val renotes = renotesPagingService.state.map { state -> state.suspendConvert { renotes -> renotes.mapNotNull {renote -> diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/item_renote_user.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/item_renote_user.kt index bcf6c35792..3ff7bd7f90 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/item_renote_user.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/item_renote_user.kt @@ -13,6 +13,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.runtime.Composable import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -24,6 +25,8 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import net.pantasystem.milktea.common_compose.CustomEmojiText import net.pantasystem.milktea.common_compose.getSimpleElapsedTime import net.pantasystem.milktea.model.user.User +import java.text.SimpleDateFormat +import java.util.* @ExperimentalCoroutinesApi @Composable @@ -32,12 +35,22 @@ fun ItemRenoteUser( note: RenoteItemType, myId: User.Id?, accountHost: String?, + isDisplayTimestampsAsAbsoluteDates: Boolean, onAction: (ItemRenoteAction) -> Unit, isUserNameDefault: Boolean = false ) { - val createdAt = (note as? RenoteItemType.Renote)?.let { - getSimpleElapsedTime(time = it.note.note.createdAt) + val createdAt = (note as? RenoteItemType.Renote)?.let { renote -> + if (isDisplayTimestampsAsAbsoluteDates) { + remember(renote.note.note.createdAt) { + SimpleDateFormat.getDateTimeInstance().format(renote.note.note.createdAt.let { + Date(it.toEpochMilliseconds()) + }) + } + } else { + getSimpleElapsedTime(time = renote.note.note.createdAt) + } + } Card( diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/renote_users.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/renote_users.kt index e58d1c15f7..6323c07ad9 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/renote_users.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/renote_users.kt @@ -39,6 +39,7 @@ fun RenoteUsersScreen( val myId by renotesViewModel.myId.collectAsState() val account by renotesViewModel.account.collectAsState() + val config by renotesViewModel.config.collectAsState() val renotes: PageableState> by renotesViewModel.renotes.asLiveData() .observeAsState( @@ -72,6 +73,7 @@ fun RenoteUsersScreen( onScrollState = onScrollState, myId = myId, accountHost = account?.getHost(), + isDisplayTimestampsAsAbsoluteDates = config.isDisplayTimestampsAsAbsoluteDates, ) } else { Column( @@ -103,6 +105,7 @@ fun RenoteUserList( notes: List, myId: User.Id?, accountHost: String?, + isDisplayTimestampsAsAbsoluteDates: Boolean?, onAction: (ItemRenoteAction) -> Unit, onBottomReached: () -> Unit, onScrollState: (Boolean) -> Unit, @@ -141,6 +144,7 @@ fun RenoteUserList( onAction = onAction, myId = myId, accountHost = accountHost, + isDisplayTimestampsAsAbsoluteDates = isDisplayTimestampsAsAbsoluteDates ?: false, ) } } From 2609ff05e227b7e83f1fc082cbc914313a100c26 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Fri, 2 Jun 2023 21:45:33 +0900 Subject: [PATCH 311/432] =?UTF-8?q?feat:=20ViewPager=E3=81=AE=E7=B6=99?= =?UTF-8?q?=E6=89=BF=E3=82=AF=E3=83=A9=E3=82=B9=E3=81=A7=E3=81=AF=E3=81=AA?= =?UTF-8?q?=E3=81=8F=E3=82=B9=E3=83=AF=E3=82=A4=E3=83=97=E3=82=92=E6=A4=9C?= =?UTF-8?q?=E7=9F=A5=E3=81=97=E3=82=A4=E3=83=99=E3=83=B3=E3=83=88=E3=82=92?= =?UTF-8?q?=E9=80=81=E4=BF=A1=E3=81=99=E3=82=8B=E3=82=AB=E3=82=B9=E3=82=BF?= =?UTF-8?q?=E3=83=A0View=E3=82=92=E4=BD=9C=E6=88=90=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/media/ImageFragment.kt | 4 + .../milktea/media/MediaActivity.kt | 3 - .../milktea/media/PhotoViewViewPager.kt | 119 +------------- .../milktea/media/PlayerFragment.kt | 4 + .../milktea/media/SwipeFinishLayout.kt | 151 ++++++++++++++++++ .../src/main/res/layout/fragment_image.xml | 8 +- .../src/main/res/layout/fragment_player.xml | 7 +- 7 files changed, 174 insertions(+), 122 deletions(-) create mode 100644 modules/features/media/src/main/java/net/pantasystem/milktea/media/SwipeFinishLayout.kt diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/ImageFragment.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/ImageFragment.kt index b7ce73af3b..8327ee7fab 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/ImageFragment.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/ImageFragment.kt @@ -56,6 +56,10 @@ class ImageFragment : Fragment(R.layout.fragment_image){ return } + binding.swipeFinishLayout.setOnFinishEventListener { + requireActivity().finish() + } + Glide.with(view.context).let { if (uri == null) { it.load(url) diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt index 27de6e0b1d..bf6cb6a1ad 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/MediaActivity.kt @@ -118,9 +118,6 @@ class MediaActivity : AppCompatActivity() { val pagerAdapter = MediaPagerAdapter(list) mBinding.mediaViewPager.adapter = pagerAdapter - mBinding.mediaViewPager.setOnFinishEventListener { - finish() - } mBinding.mediaViewPager.currentItem = fileCurrentIndex } diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt index 3d1164461e..5f6c588a9b 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/PhotoViewViewPager.kt @@ -2,12 +2,8 @@ package net.pantasystem.milktea.media import android.content.Context import android.util.AttributeSet -import android.view.GestureDetector import android.view.MotionEvent -import android.view.View import androidx.viewpager.widget.ViewPager -import com.github.chrisbanes.photoview.PhotoView -import kotlin.math.abs /** @@ -15,133 +11,22 @@ import kotlin.math.abs */ class PhotoViewViewPager : ViewPager { - fun interface OnFinishEventListener { - fun onFinish() - } - - companion object { - - private const val SWIPE_THRESHOLD = 150 - - private const val SWIPE_VELOCITY_THRESHOLD = 150 - } constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet): super(context, attrs) - private var onFinishEventListener: OnFinishEventListener? = null - fun setOnFinishEventListener(listener: OnFinishEventListener?){ - onFinishEventListener = listener - } override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { return try{ - if (ev?.action == MotionEvent.ACTION_DOWN) { - startY = ev.y - } - when(ev?.action) { - MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { - getPhotoView()?.animate()?.translationY(0f)?.rotation(0f)?.setDuration(200)?.start() - totalTranslationY = 0f - } - } - if (ev != null) { - return gestureDetector.onTouchEvent(ev) || super.onInterceptTouchEvent(ev) - } - return super.onInterceptTouchEvent(null) + + return super.onInterceptTouchEvent(ev) }catch(e: IllegalArgumentException){ false } } - private var startY = 0f - - var lastY = 0f - var totalTranslationY = 0f - - - private val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { - - override fun onScroll( - e1: MotionEvent, - e2: MotionEvent, - distanceX: Float, - distanceY: Float - ): Boolean { - - if (isNotScaled()) { - totalTranslationY += e2.rawY - lastY - viewToMove()?.translationY = totalTranslationY - viewToMove()?.rotation = totalTranslationY / 10 - lastY = e2.rawY - } - - return super.onScroll(e1, e2, distanceX, distanceY) - } - - override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { - var result = false - try { - val diffY = e2.y.minus(e1.y) - if (abs(diffY) > SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { - if (diffY <= 0) { - onSwipeTop() - result = true - } - } - } catch (exception: Exception) { - exception.printStackTrace() - } - viewToMove()?.animate()?.translationY(0f)?.rotation(0f)?.setDuration(200)?.start() - totalTranslationY = 0f - return result - } - - override fun onDown(e: MotionEvent): Boolean { - lastY = e.rawY - return super.onDown(e) - } - - - }) - - - private fun onSwipeTop() { - - if (isNotScaled()) { - // Only close if not zoomed in - onFinishEventListener?.onFinish() -// (context as? Activity)?.finish() - } - } - - private fun getPhotoView(): PhotoView? { - return try { - findViewById(R.id.imageView) - } catch (e: Exception) { - null - } - } - - private fun getPlayerView(): View? { - return try { - findViewById(R.id.player_view) - } catch (e: Exception) { - null - } - } - - private fun viewToMove(): View? { - return getPhotoView() ?: getPlayerView() - } - - private fun isNotScaled(): Boolean { - return getPhotoView() == null || getPhotoView()?.scale == 1.0F - } - - } \ No newline at end of file diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/PlayerFragment.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/PlayerFragment.kt index 35a5f67303..f7da2596a9 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/PlayerFragment.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/PlayerFragment.kt @@ -73,6 +73,10 @@ class PlayerFragment : Fragment(R.layout.fragment_player){ simpleExoPlayer.prepare() simpleExoPlayer.play() + view.findViewById(R.id.swipeFinishLayout).setOnFinishEventListener { + requireActivity().finish() + } + mExoPlayer = simpleExoPlayer } diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/SwipeFinishLayout.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/SwipeFinishLayout.kt new file mode 100644 index 0000000000..0012b19b66 --- /dev/null +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/SwipeFinishLayout.kt @@ -0,0 +1,151 @@ +package net.pantasystem.milktea.media + +import android.content.Context +import android.util.AttributeSet +import android.view.GestureDetector +import android.view.MotionEvent +import android.view.View +import android.widget.FrameLayout +import com.github.chrisbanes.photoview.PhotoView +import kotlin.math.abs + +class SwipeFinishLayout : FrameLayout { + + companion object { + + private const val SWIPE_THRESHOLD = 150 + + private const val SWIPE_VELOCITY_THRESHOLD = 150 + } + + + + constructor(context: Context) : super(context) + + constructor(context: Context, attrs: AttributeSet): super(context, attrs) + + fun interface OnFinishEventListener { + fun onFinish() + } + + + + private var onFinishEventListener: OnFinishEventListener? = null + + fun setOnFinishEventListener(listener: OnFinishEventListener?){ + onFinishEventListener = listener + } + + + override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean { + return try{ + if (ev?.action == MotionEvent.ACTION_DOWN) { + startY = ev.y + } + when(ev?.action) { + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> { + getPhotoView()?.animate()?.translationY(0f)?.rotation(0f)?.setDuration(200)?.start() + totalTranslationY = 0f + } + } + if (ev != null) { + return gestureDetector.onTouchEvent(ev) || super.onInterceptTouchEvent(ev) + } + return super.onInterceptTouchEvent(null) + }catch(e: IllegalArgumentException){ + false + } + } + + + private var startY = 0f + + var lastY = 0f + var totalTranslationY = 0f + + + private val gestureDetector = GestureDetector(context, object : GestureDetector.SimpleOnGestureListener() { + + override fun onScroll( + e1: MotionEvent, + e2: MotionEvent, + distanceX: Float, + distanceY: Float + ): Boolean { + + // 縦スクロールの場合 + if (abs(e2.y - startY) > abs(e2.x - e1.x)) { + if (isNotScaled()) { + totalTranslationY += e2.rawY - lastY + viewToMove()?.translationY = totalTranslationY + viewToMove()?.rotation = totalTranslationY / 10 + lastY = e2.rawY + } + + } + + return super.onScroll(e1, e2, distanceX, distanceY) + } + + override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { + var result = false + try { + val diffY = e2.y.minus(e1.y) + if (abs(diffY) > SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { + if (diffY <= 0) { + onSwipeTop() + result = true + } + } + } catch (exception: Exception) { + exception.printStackTrace() + } + viewToMove()?.animate()?.translationY(0f)?.rotation(0f)?.setDuration(200)?.start() + totalTranslationY = 0f + return result + } + + override fun onDown(e: MotionEvent): Boolean { + lastY = e.rawY + return super.onDown(e) + } + + + }) + + + private fun onSwipeTop() { + + if (isNotScaled()) { + // Only close if not zoomed in + onFinishEventListener?.onFinish() +// (context as? Activity)?.finish() + } + } + + private fun getPhotoView(): PhotoView? { + return try { + findViewById(R.id.imageView) + } catch (e: Exception) { + null + } + } + + private fun getPlayerView(): View? { + return try { + findViewById(R.id.player_view) + } catch (e: Exception) { + null + } + } + + private fun viewToMove(): View? { + return getPhotoView() ?: getPlayerView() + } + + private fun isNotScaled(): Boolean { + return getPhotoView() == null || getPhotoView()?.scale == 1.0F + } + + +} \ No newline at end of file diff --git a/modules/features/media/src/main/res/layout/fragment_image.xml b/modules/features/media/src/main/res/layout/fragment_image.xml index 3c70390582..6e8a3980bd 100644 --- a/modules/features/media/src/main/res/layout/fragment_image.xml +++ b/modules/features/media/src/main/res/layout/fragment_image.xml @@ -1,8 +1,14 @@ - + + + diff --git a/modules/features/media/src/main/res/layout/fragment_player.xml b/modules/features/media/src/main/res/layout/fragment_player.xml index 4c12e8568d..951d30e288 100644 --- a/modules/features/media/src/main/res/layout/fragment_player.xml +++ b/modules/features/media/src/main/res/layout/fragment_player.xml @@ -2,8 +2,13 @@ - + + \ No newline at end of file From 51f775babd4e2a3c5b1bac923d5b9609f852d27e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 5 Jun 2023 14:02:25 +0900 Subject: [PATCH 312/432] =?UTF-8?q?feat:=20=E7=B5=B6=E5=AF=BE=E6=97=A5?= =?UTF-8?q?=E6=99=82=E8=A1=A8=E8=A8=98=E3=81=AE=E6=99=82=E3=81=AF=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=82=B9=E3=82=BF=E3=83=B3=E3=83=97=E3=82=92?= =?UTF-8?q?=E4=B8=8B=E9=83=A8=E3=81=AB=E8=A1=A8=E7=A4=BA=E3=81=99=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/layout/item_simple_note.xml | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/modules/features/note/src/main/res/layout/item_simple_note.xml b/modules/features/note/src/main/res/layout/item_simple_note.xml index 98636b2d00..6e6885aacb 100644 --- a/modules/features/note/src/main/res/layout/item_simple_note.xml +++ b/modules/features/note/src/main/res/layout/item_simple_note.xml @@ -106,6 +106,7 @@ android:textColor="?attr/colorPrimary" app:emojiCompatEnabled="false" android:onClick="@{() -> noteCardActionListener.onNoteCardClicked(note.note.note)}" + android:visibility="@{note.config.displayTimestampsAsAbsoluteDates ? View.GONE : View.VISIBLE}" /> + + Date: Mon, 5 Jun 2023 14:10:47 +0900 Subject: [PATCH 313/432] =?UTF-8?q?feat:=20visibility=E3=82=A2=E3=82=A4?= =?UTF-8?q?=E3=82=B3=E3=83=B3=E3=81=AE=E8=A1=A8=E7=A4=BA=E4=BD=8D=E7=BD=AE?= =?UTF-8?q?=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/text/DateFormatHelper.kt | 33 +++++++++++++++++++ .../src/main/res/layout/item_simple_note.xml | 2 ++ 2 files changed, 35 insertions(+) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt index 5b16bc9fc2..029ba181eb 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DateFormatHelper.kt @@ -103,4 +103,37 @@ object DateFormatHelper { val javaDate = Date(date.toEpochMilliseconds()) this.text = SimpleDateFormat.getDateTimeInstance().format(javaDate) } + + @BindingAdapter("createdAt", "visibility") + @JvmStatic + fun TextView.setCreatedAtWithVisibility(createdAt: Instant?, visibility: Visibility?) { + val date = createdAt ?: Clock.System.now() + val javaDate = Date(date.toEpochMilliseconds()) + val visibilityIcon = when(visibility ?: Visibility.Public(false)) { + is Visibility.Followers -> R.drawable.ic_lock_black_24dp + is Visibility.Home -> R.drawable.ic_home_black_24dp + is Visibility.Public -> null + is Visibility.Specified -> R.drawable.ic_email_black_24dp + is Visibility.Limited -> R.drawable.ic_groups + Visibility.Mutual -> R.drawable.ic_sync_alt_24px + Visibility.Personal -> R.drawable.ic_person_black_24dp + } + val text = SimpleDateFormat.getDateTimeInstance().format(javaDate) + + this.text = if (visibilityIcon == null) { + text + } else { + val target = "visibility $text" + SpannableStringBuilder(target).apply { + val drawable = ContextCompat.getDrawable(context, visibilityIcon) + drawable?.setTint(currentTextColor) + val span = DrawableEmojiSpan(EmojiAdapter(this@setCreatedAtWithVisibility), visibilityIcon) + setSpan(span, 0, "visibility".length,0) + GlideApp.with(this@setCreatedAtWithVisibility) + .load(drawable) + .override(min(textSize.toInt(), 640)) + .into(span.target) + } + } + } } \ No newline at end of file diff --git a/modules/features/note/src/main/res/layout/item_simple_note.xml b/modules/features/note/src/main/res/layout/item_simple_note.xml index 6e6885aacb..4544f5fd90 100644 --- a/modules/features/note/src/main/res/layout/item_simple_note.xml +++ b/modules/features/note/src/main/res/layout/item_simple_note.xml @@ -429,6 +429,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" createdAt="@{note.toShowNote.note.createdAt}" + visibility="@{note.toShowNote.note.visibility}" app:layout_constraintTop_toBottomOf="@id/expandAllReactionCounts" app:layout_constraintStart_toEndOf="@id/avatarIcon" tools:text="10秒前" @@ -438,6 +439,7 @@ android:visibility="@{note.config.displayTimestampsAsAbsoluteDates ? View.VISIBLE : View.GONE}" android:textColor="?attr/colorPrimary" android:onClick="@{() -> noteCardActionListener.onNoteCardClicked(note.note.note)}" + android:layout_marginTop="2dp" /> From a6423d11d9ec22c9f316e3f8158b904e8db7fb40 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 5 Jun 2023 15:16:45 +0900 Subject: [PATCH 314/432] =?UTF-8?q?feat:=20mastodon=E3=81=A3=E3=81=BD?= =?UTF-8?q?=E3=81=84=E3=83=86=E3=83=BC=E3=83=9E=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../jp/panta/misskeyandroidclient/ThemeUtil.kt | 1 + .../src/main/res/values-ja/strings.xml | 2 ++ .../src/main/res/values-zh/strings.xml | 2 ++ .../src/main/res/values/strings.xml | 1 + .../src/main/res/values/themes.xml | 16 ++++++++++++++++ .../data/infrastructure/settings/Theme.kt | 2 ++ .../activities/SettingAppearanceActivity.kt | 4 ++++ .../pantasystem/milktea/model/setting/Theme.kt | 4 +++- 8 files changed, 31 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/ThemeUtil.kt b/app/src/main/java/jp/panta/misskeyandroidclient/ThemeUtil.kt index 7e13d02438..441f1dd06c 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/ThemeUtil.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/ThemeUtil.kt @@ -29,6 +29,7 @@ fun Activity.setTheme() { Theme.Black -> setTheme(R.style.AppThemeBlack) Theme.Bread -> setTheme(R.style.AppThemeBread) Theme.White -> setTheme(R.style.AppTheme) + Theme.ElephantDark -> setTheme(R.style.AppThemeMastodonDark) } } diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index dcf45de382..e0099a4d07 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -90,6 +90,8 @@ ブラック ダーク パンケーキ + ダーク(🐘) + MediaActivity Dummy Button diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index b6d055cd39..3c9942c02f 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -90,6 +90,8 @@ 黑色 暗色 薄饼 + Elephant Dark + 媒体活动 虚拟按钮 diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 9396735f78..6dcd92f50e 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -91,6 +91,7 @@ AMOLED Dark Pancake + Elephant Dark MediaActivity Dummy Button diff --git a/modules/common_resource/src/main/res/values/themes.xml b/modules/common_resource/src/main/res/values/themes.xml index 6ca77467ee..16f58b93fc 100644 --- a/modules/common_resource/src/main/res/values/themes.xml +++ b/modules/common_resource/src/main/res/values/themes.xml @@ -102,4 +102,20 @@ + + \ No newline at end of file diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Theme.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Theme.kt index 1b15bba1fb..5578e921b2 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Theme.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Theme.kt @@ -9,6 +9,7 @@ fun Theme.toInt(): Int { is Theme.Black -> 1 is Theme.Dark -> 2 is Theme.Bread -> 3 + Theme.ElephantDark -> 4 } } @@ -18,6 +19,7 @@ fun Theme.Companion.from(n: Int): Theme { 1 -> Theme.Black 2 -> Theme.Dark 3 -> Theme.Bread + 4 -> Theme.ElephantDark else -> DefaultConfig.config.theme } } diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index 5a862bc376..dda61cdd90 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -91,6 +91,10 @@ class SettingAppearanceActivity : AppCompatActivity() { ThemeUiState( Theme.Bread, R.string.theme_bread, + ), + ThemeUiState( + Theme.ElephantDark, + R.string.theme_mastodon_dark, ) ) } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Theme.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Theme.kt index 3ec10b4e89..7c1a96760c 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Theme.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Theme.kt @@ -5,10 +5,12 @@ sealed interface Theme { object Black : Theme object Dark : Theme object Bread : Theme + + object ElephantDark : Theme companion object } fun Theme.isNightTheme(): Boolean { - return this is Theme.Black || this is Theme.Dark + return this is Theme.Black || this is Theme.Dark || this is Theme.ElephantDark } \ No newline at end of file From 3ef4db1d0d5ebc6d48df2ff5fd7725c7100cc3f3 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Mon, 5 Jun 2023 15:50:16 +0900 Subject: [PATCH 315/432] fix --- modules/common_resource/src/main/res/values/themes.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/common_resource/src/main/res/values/themes.xml b/modules/common_resource/src/main/res/values/themes.xml index 16f58b93fc..cd27387f42 100644 --- a/modules/common_resource/src/main/res/values/themes.xml +++ b/modules/common_resource/src/main/res/values/themes.xml @@ -113,6 +113,7 @@ @style/BottomNavigationView4Dark #FF8C8DFE #FF424859 + #FF191B22 From a58749dae7bac12cf7d91f812846290ed22a3a88 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 13 Jun 2023 12:03:45 +0900 Subject: [PATCH 316/432] =?UTF-8?q?fix:=20=E7=94=BB=E9=9D=A2=E3=81=8C?= =?UTF-8?q?=E8=A6=8B=E5=88=87=E3=82=8C=E3=82=8B=E5=95=8F=E9=A1=8C=E3=82=92?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/AndroidManifest.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d7f2597e66..16405e8b1b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -128,7 +128,10 @@ - + From acb0d459c82d28882ae4271c48b3555b7379ef25 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 13 Jun 2023 14:28:49 +0900 Subject: [PATCH 317/432] =?UTF-8?q?feat:=20=E7=94=BB=E5=83=8F=E3=81=AE?= =?UTF-8?q?=E8=AA=AD=E3=81=BF=E8=BE=BC=E3=81=BF=E3=82=B5=E3=82=A4=E3=82=BA?= =?UTF-8?q?=E3=82=92=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_android_ui/MFMDecorator.kt | 52 ++++--------------- 1 file changed, 10 insertions(+), 42 deletions(-) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt index cc2d375513..1753c6247f 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt @@ -25,7 +25,6 @@ import net.pantasystem.milktea.common_navigation.UserDetailNavigationArgs import net.pantasystem.milktea.model.emoji.Emoji import java.lang.ref.WeakReference import kotlin.math.max -import kotlin.math.min object MFMDecorator { @@ -36,7 +35,6 @@ object MFMDecorator { textView: TextView, lazyDecorateResult: LazyDecorateResult?, skipEmojis: SkipEmojiHolder = SkipEmojiHolder(), - retryCounter: Int = 0, ): Spanned? { lazyDecorateResult ?: return null val emojiAdapter = EmojiAdapter(textView) @@ -47,7 +45,6 @@ object MFMDecorator { lazyDecorateResult, skipEmojis, emojiAdapter, - retryCounter, ).decorate() } @@ -288,11 +285,10 @@ object MFMDecorator { } class LazyEmojiDecorator( - val textView: WeakReference, - val lazyDecorateResult: LazyDecorateResult, - val skipEmojis: SkipEmojiHolder, - val emojiAdapter: EmojiAdapter, - val retryCounter: Int, + private val textView: WeakReference, + private val lazyDecorateResult: LazyDecorateResult, + private val skipEmojis: SkipEmojiHolder, + private val emojiAdapter: EmojiAdapter, ) { private val spannableString = SpannableString(lazyDecorateResult.spanned) @@ -315,42 +311,14 @@ object MFMDecorator { textView.get()?.let { textView -> val emojiSpan = DrawableEmojiSpan(emojiAdapter, emojiElement.emoji.url, emojiElement.emoji.aspectRatio) spannableString.setSpan(emojiSpan, skippedEmoji.start, skippedEmoji.end, 0) + val height = max(textView.textSize * 0.75f, 10f) + val width = when(val aspectRatio = emojiElement.emoji.aspectRatio) { + null -> height + else -> height * aspectRatio + } GlideApp.with(textView) .load(emojiElement.emoji.url) - .override(min(max(textView.textSize.toInt(), 10), 128)) -// .addListener(object : RequestListener { -// override fun onLoadFailed( -// e: GlideException?, -// model: Any?, -// target: Target?, -// isFirstResource: Boolean -// ): Boolean { -// val t = this@LazyEmojiDecorator.textView.get() -// if (t != null && !skipEmojis.contains(emojiElement.emoji) && t.getTag(R.id.TEXT_VIEW_MFM_TAG_ID) == lazyDecorateResult.sourceText) { -// if (retryCounter < 10) { -// -// t.text = decorate( -// t, -// lazyDecorateResult = lazyDecorateResult, -// skipEmojis = skipEmojis.add(emojiElement.emoji), -// retryCounter + 1 -// ) -// } -// } -// -// return false -// } -// -// override fun onResourceReady( -// resource: Drawable?, -// model: Any?, -// target: Target?, -// dataSource: DataSource?, -// isFirstResource: Boolean -// ): Boolean { -// return false -// } -// }) + .override(width.toInt(), height.toInt()) .into(emojiSpan.target) } } From 5556ccb55b4ea06280aad5f8e599041bd911dd4e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 13 Jun 2023 14:42:55 +0900 Subject: [PATCH 318/432] =?UTF-8?q?refactor:=20=E3=81=82=E3=81=BE=E3=82=8A?= =?UTF-8?q?=E3=81=AB=E3=82=82=E3=82=8F=E3=81=8B=E3=82=8A=E3=81=A5=E3=82=89?= =?UTF-8?q?=E3=81=84=E3=81=AE=E3=81=A7=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/common_android/ui/text/EmojiSpan.kt | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt index 58703e9a6c..14bb666e06 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt @@ -7,9 +7,16 @@ import android.text.TextPaint import android.text.style.ReplacementSpan import kotlin.math.min +/** + * @param key 画像の種別を識別するためのキー値で、画像のURLなどが入る + * @param aspectRatio 画像の比率が入る + */ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : ReplacementSpan(){ companion object { + /** + * 変数keyに対応するDrawableの画像サイズをここに保持している。 + */ private val drawableSizeCache = mutableMapOf() } @@ -43,16 +50,20 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : val size = key?.let { drawableSizeCache[key] } ?: drawable?.let { + // NOTE: drawableSizeCacheに画像のサイズが登録されていない場合は、drawableからサイズを取得する EmojiSizeCache( intrinsicHeight = it.intrinsicHeight, intrinsicWidth = it.intrinsicWidth ) } ?: aspectRatio?.let { + // NOTE: drawableが読み込まれていない状態の時は、文字の高さと画像の比率から横幅のサイズを取得する EmojiSizeCache( intrinsicHeight = textHeight.toInt(), intrinsicWidth = (textHeight * aspectRatio).toInt() ) } + + // NOTE: keyが存在しかつdrawableが存在する場合は、drawableSizeCacheを更新する key?.run { drawableSizeCache[key] ?: drawable?.let { EmojiSizeCache( @@ -69,6 +80,7 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : fm.bottom = metrics.bottom } + // NOTE: 画像のサイズが不明かつ初めてサイズを取得しようとした時は暫定的なサイズを返す if (size == null || beforeTextSize != 0) { beforeTextSize = (paint.textSize * 1.2).toInt() return beforeTextSize @@ -77,6 +89,7 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : drawableSizeCache[key] = size } + // NOTE: 暫定的なサイズではない場合はbeforeTextSizeを0にする必要性がある beforeTextSize = 0 val imageWidth = size.intrinsicWidth From 405d45524af73cc70875e36e0e3d1cf89bdd3ba6 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Tue, 13 Jun 2023 14:52:57 +0900 Subject: [PATCH 319/432] =?UTF-8?q?refactor:=20=E3=81=82=E3=81=BE=E3=82=8A?= =?UTF-8?q?=E3=81=AB=E3=82=82=E3=82=8F=E3=81=8B=E3=82=8A=E3=81=A5=E3=82=89?= =?UTF-8?q?=E3=81=84=E3=81=AE=E3=81=A7=E3=82=B3=E3=83=A1=E3=83=B3=E3=83=88?= =?UTF-8?q?=E3=82=92=E8=BF=BD=E5=8A=A0=E3=80=82=E3=81=BE=E3=81=9F=E7=84=A1?= =?UTF-8?q?=E9=A7=84=E3=81=AA=E5=87=A6=E7=90=86=E3=82=92=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_android/ui/text/EmojiSpan.kt | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt index 14bb666e06..6cd2306030 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt @@ -22,12 +22,11 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : var imageDrawable: Drawable? = null + /** - * imageDrawableにDrawableが代入されている時にupdateImageDrawableSizeが呼び出されるとここに絵文字のサイズが代入される。 - * 画像は縦横比が異なることがあるので、それぞれの高さが代入される。 + * 文字サイズなどのスケールに応じて画像サイズをDrawableに反映したorしてないの状態 + * 反映済みの場合はtrueが入り、そうでない場合はfalseが入る */ - private var textHeight: Int = 0 - private var textWidth: Int = 0 private var isSizeComputed = false /** @@ -132,9 +131,16 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : } + /** + * サイズが大きな画像をGPUのメモリに展開してしまうと、 + * GPUに負荷がかかりフレーム落ちの原因につながる可能性があるので、 + * Drawableのサイズを必要なサイズにリサイズを行う処理 + */ private fun updateImageDrawableSize(paint: Paint) { val emojiHeight = min((paint.textSize).toInt(), 128) val drawable = imageDrawable + + // drawableSizeCacheあるいはdrawableあるいはaspectRatioと文字サイズから画像のwidth, heightを得る処理 val size = key?.let { drawableSizeCache[key] } ?: drawable?.let { @@ -154,7 +160,10 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : val imageWidth = size.intrinsicWidth val imageHeight = size.intrinsicHeight + // 計算された画像サイズが適切なものかチェックする val unknownEmojiSize = imageWidth <= 0 || imageHeight <= 0 + + // 画像サイズが暫定的なサイズかつ、暫定的なサイズと画像のサイズが一致しない場合は処理を終了する if (beforeTextSize != 0 && beforeTextSize != emojiHeight || unknownEmojiSize) { if (!isSizeComputed) { beforeTextSize = emojiHeight @@ -169,8 +178,6 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : val scaledImageWidth = (emojiHeight * ratio).toInt() if (!isSizeComputed) { - textHeight = emojiHeight - textWidth = scaledImageWidth isSizeComputed = imageDrawable != null imageDrawable?.setBounds(0, 0, scaledImageWidth, emojiHeight) } From ee36bb2a761d230d60f5e46d3a5c908a89afc9ae Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 14 Jun 2023 00:24:20 +0900 Subject: [PATCH 320/432] =?UTF-8?q?feat:=203=E5=80=A4=E3=82=92=E8=A1=A8?= =?UTF-8?q?=E7=8F=BE=E3=81=99=E3=82=8B=E3=81=9F=E3=82=81=E3=81=AE=E6=A7=8B?= =?UTF-8?q?=E9=80=A0=E4=BD=93=E3=82=92=E4=BD=9C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/model/drive/UpdateFileProperty.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/drive/UpdateFileProperty.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/drive/UpdateFileProperty.kt index a805e1c986..15de6dc714 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/drive/UpdateFileProperty.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/drive/UpdateFileProperty.kt @@ -6,4 +6,11 @@ data class UpdateFileProperty( val name: String, val isSensitive: Boolean, val comment: String?, -) \ No newline at end of file +) + +sealed interface ValueType { + class Empty : ValueType + + data class Some(val value: T) : ValueType + +} From d636a751f77b0a11b89c18292fcb464917d91557 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 14 Jun 2023 00:46:15 +0900 Subject: [PATCH 321/432] =?UTF-8?q?feat:=203=E5=80=A4(null,=20some,=20unde?= =?UTF-8?q?fined)=E3=82=92=E8=A1=A8=E7=8F=BE=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=BE=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/model/drive/UpdateFileProperty.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/drive/UpdateFileProperty.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/drive/UpdateFileProperty.kt index 15de6dc714..ebb54ae474 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/drive/UpdateFileProperty.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/drive/UpdateFileProperty.kt @@ -2,10 +2,10 @@ package net.pantasystem.milktea.model.drive data class UpdateFileProperty( val fileId: FileProperty.Id, - val folderId: String?, - val name: String, - val isSensitive: Boolean, - val comment: String?, + val folderId: ValueType? = null, + val name: ValueType? = null, + val isSensitive: ValueType? = null, + val comment: ValueType? = null, ) sealed interface ValueType { From 66eb2efd282306b7942b8a4bc67f26cf4c81d75f Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 14 Jun 2023 00:47:01 +0900 Subject: [PATCH 322/432] =?UTF-8?q?feat:=20Kotlin=20serialization=E3=81=AE?= =?UTF-8?q?class=E3=81=AE=E3=81=BE=E3=81=BE=E3=81=A7=E3=81=AF3=E5=80=A4?= =?UTF-8?q?=E3=82=92=E8=A1=A8=E7=8F=BE=E3=81=99=E3=82=8B=E3=81=AE=E3=81=AF?= =?UTF-8?q?=E5=9B=B0=E9=9B=A3=E3=81=A0=E3=81=A3=E3=81=9F=E3=81=9F=E3=82=81?= =?UTF-8?q?=E3=80=81=E7=9B=B4=E6=8E=A5JsonObject=E3=82=92=E6=89=B1?= =?UTF-8?q?=E3=81=86=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F=EF=BC=88?= =?UTF-8?q?=E5=9C=B0=E7=8D=84=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/api/misskey/MisskeyAPI.kt | 3 +- .../api/misskey/drive/UpdateFileDTO.kt | 104 ++++++++++++------ 2 files changed, 72 insertions(+), 35 deletions(-) diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt index 3abd6dc14a..7767ebc0f0 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/MisskeyAPI.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.api.misskey +import kotlinx.serialization.json.JsonObject import net.pantasystem.milktea.api.misskey.ap.ApResolveRequest import net.pantasystem.milktea.api.misskey.ap.ApResolveResult import net.pantasystem.milktea.api.misskey.app.CreateApp @@ -198,7 +199,7 @@ interface MisskeyAPI { suspend fun getFiles(@Body fileRequest: RequestFile): Response> @POST("api/drive/files/update") - suspend fun updateFile(@Body updateFileRequest: UpdateFileDTO): Response + suspend fun updateFile(@Body updateFileRequest: JsonObject): Response @POST("api/drive/files/delete") suspend fun deleteFile(@Body req: DeleteFileDTO): Response diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/drive/UpdateFileDTO.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/drive/UpdateFileDTO.kt index 8ca341d85e..9d89676c79 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/drive/UpdateFileDTO.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/drive/UpdateFileDTO.kt @@ -1,39 +1,75 @@ package net.pantasystem.milktea.api.misskey.drive -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.buildJsonObject import net.pantasystem.milktea.model.drive.UpdateFileProperty +import net.pantasystem.milktea.model.drive.ValueType -@Serializable -data class UpdateFileDTO( - @SerialName("i") - val i: String, - - @SerialName("fileId") - val fileId: String, - - @SerialName("folderId") - val folderId: String?, - - @SerialName("name") - val name: String, - - @SerialName("comment") - val comment: String?, - - @SerialName("isSensitive") - val isSensitive: Boolean, -) { - companion object -} - -fun UpdateFileDTO.Companion.from(token: String, model: UpdateFileProperty): UpdateFileDTO { - return UpdateFileDTO( - i = token, - comment = model.comment, - fileId = model.fileId.fileId, - folderId = model.folderId, - isSensitive = model.isSensitive, - name = model.name - ) +//@Serializable +//data class UpdateFileDTO( +// @SerialName("i") +// val i: String, +// +// @SerialName("fileId") +// val fileId: String, +// +// @SerialName("folderId") +// val folderId: String?, +// +// @SerialName("name") +// val name: String, +// +// @SerialName("comment") +// val comment: String?, +// +// @SerialName("isSensitive") +// val isSensitive: Boolean, +//) { +// companion object +//} + +//fun UpdateFileDTO.Companion.from(token: String, model: UpdateFileProperty): UpdateFileDTO { +// return UpdateFileDTO( +// i = token, +// comment = model.comment, +// fileId = model.fileId.fileId, +// folderId = model.folderId, +// isSensitive = model.isSensitive, +// name = model.name +// ) +//} + +@OptIn(ExperimentalSerializationApi::class) +fun UpdateFileProperty.toJsonObject(token: String): JsonObject { + return buildJsonObject { + put("i", JsonPrimitive(token)) + put("fileId", JsonPrimitive(fileId.fileId)) + + when(val v = comment) { + is ValueType.Empty -> put("comment", JsonPrimitive(null)) + is ValueType.Some -> put("comment", JsonPrimitive(v.value)) + null -> Unit + } + + when(val v = folderId) { + is ValueType.Empty -> put("folderId", JsonPrimitive(null)) + is ValueType.Some -> put("folderId", JsonPrimitive(v.value)) + null -> Unit + } + + when(val v = name) { + is ValueType.Empty -> put("name", JsonPrimitive(null)) + is ValueType.Some -> put("name", JsonPrimitive(v.value)) + null -> Unit + } + + when(val v = isSensitive) { + is ValueType.Empty -> put("isSensitive", JsonPrimitive(null)) + is ValueType.Some -> put("isSensitive", JsonPrimitive(v.value)) + null -> Unit + } + + } } \ No newline at end of file From b625b9ec06667770337a9b5f76dc5d2ee175be8a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Wed, 14 Jun 2023 00:49:06 +0900 Subject: [PATCH 323/432] =?UTF-8?q?feat:=20=E5=A4=89=E6=9B=B4=E3=81=AB?= =?UTF-8?q?=E5=90=88=E3=82=8F=E3=81=9B=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../drive/DriveFileRepositoryImpl.kt | 23 +++----- .../editor/viewmodel/NoteEditorViewModel.kt | 15 +---- .../milktea/model/drive/FileProperty.kt | 55 +++++++++++++++---- 3 files changed, 53 insertions(+), 40 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/drive/DriveFileRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/drive/DriveFileRepositoryImpl.kt index 7ab435fa25..ca9391af04 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/drive/DriveFileRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/drive/DriveFileRepositoryImpl.kt @@ -4,18 +4,14 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext import net.pantasystem.milktea.api.misskey.drive.DeleteFileDTO import net.pantasystem.milktea.api.misskey.drive.ShowFile -import net.pantasystem.milktea.api.misskey.drive.UpdateFileDTO -import net.pantasystem.milktea.api.misskey.drive.from +import net.pantasystem.milktea.api.misskey.drive.toJsonObject import net.pantasystem.milktea.common.runCancellableCatching import net.pantasystem.milktea.common.throwIfHasError import net.pantasystem.milktea.common_android.hilt.IODispatcher import net.pantasystem.milktea.data.api.misskey.MisskeyAPIProvider import net.pantasystem.milktea.data.converters.FilePropertyDTOEntityConverter import net.pantasystem.milktea.model.account.GetAccount -import net.pantasystem.milktea.model.drive.DriveFileRepository -import net.pantasystem.milktea.model.drive.FileProperty -import net.pantasystem.milktea.model.drive.FilePropertyDataSource -import net.pantasystem.milktea.model.drive.UpdateFileProperty +import net.pantasystem.milktea.model.drive.* import net.pantasystem.milktea.model.file.AppFile import javax.inject.Inject @@ -50,14 +46,10 @@ class DriveFileRepositoryImpl @Inject constructor( val api = misskeyAPIProvider.get(account.normalizedInstanceUri) val fileProperty = find(id) val result = api.updateFile( - UpdateFileDTO( - account.token, - fileId = id.fileId, - isSensitive = !fileProperty.isSensitive, - name = fileProperty.name, - folderId = fileProperty.folderId, - comment = fileProperty.comment - ) + UpdateFileProperty( + fileProperty.id, + isSensitive = ValueType.Some(!fileProperty.isSensitive) + ).toJsonObject(account.token) ).throwIfHasError() driveFileDataSource.add( @@ -100,9 +92,8 @@ class DriveFileRepositoryImpl @Inject constructor( val res = misskeyAPIProvider.get(getAccount.get(updateFileProperty.fileId.accountId)) .updateFile( - UpdateFileDTO.from( + updateFileProperty.toJsonObject( getAccount.get(updateFileProperty.fileId.accountId).token, - updateFileProperty, ) ).throwIfHasError() .body()!! diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt index 082b609524..957031bc48 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/editor/viewmodel/NoteEditorViewModel.kt @@ -22,10 +22,7 @@ import net.pantasystem.milktea.model.ap.ApResolver import net.pantasystem.milktea.model.ap.ApResolverRepository import net.pantasystem.milktea.model.channel.Channel import net.pantasystem.milktea.model.channel.ChannelRepository -import net.pantasystem.milktea.model.drive.DriveFileRepository -import net.pantasystem.milktea.model.drive.FileProperty -import net.pantasystem.milktea.model.drive.FilePropertyDataSource -import net.pantasystem.milktea.model.drive.UpdateFileProperty +import net.pantasystem.milktea.model.drive.* import net.pantasystem.milktea.model.emoji.Emoji import net.pantasystem.milktea.model.file.AppFile import net.pantasystem.milktea.model.file.FilePreviewSource @@ -513,10 +510,7 @@ class NoteEditorViewModel @Inject constructor( driveFileRepository.update( UpdateFileProperty( fileId = file.id, - comment = file.comment, - folderId = file.folderId, - isSensitive = file.isSensitive, - name = name + name = ValueType.Some(name) ) ).getOrThrow() }.onFailure { @@ -540,10 +534,7 @@ class NoteEditorViewModel @Inject constructor( driveFileRepository.update( UpdateFileProperty( fileId = file.id, - comment = comment, - folderId = file.folderId, - isSensitive = file.isSensitive, - name = file.name + comment = ValueType.Some(comment), ) ).getOrThrow() }.onFailure { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/drive/FileProperty.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/drive/FileProperty.kt index 8c84bbb63e..59052d97fa 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/drive/FileProperty.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/drive/FileProperty.kt @@ -5,7 +5,7 @@ import net.pantasystem.milktea.model.user.User import java.io.Serializable as JSerializable -data class FileProperty ( +data class FileProperty( val id: Id, val name: String, val createdAt: Instant?, @@ -20,28 +20,59 @@ data class FileProperty ( val blurhash: String? = null, val url: String, val thumbnailUrl: String? = null, -) : JSerializable{ +) : JSerializable { data class Id( val accountId: Long, - val fileId: String + val fileId: String, ) : JSerializable + data class Properties( val width: Float?, - val height: Float? + val height: Float?, ) : JSerializable fun update( name: String = requireNotNull(this.name), - comment: String? = this.comment, - isSensitive: Boolean = this.isSensitive, - folderId: String? = this.folderId, + comment: String? = null, + isSensitive: Boolean? = null, + folderId: String? = null, ): UpdateFileProperty { return UpdateFileProperty( - name = name, - comment = comment, - fileId = this.id, - isSensitive = isSensitive, - folderId = folderId, + fileId = id, + name = if (name == this.name) null else ValueType.Some(name), + comment = when (comment) { + this.comment -> { + null + } + null -> { + ValueType.Empty() + } + else -> { + ValueType.Some(comment) + } + }, + isSensitive = when (isSensitive) { + this.isSensitive -> { + null + } + null -> { + null + } + else -> { + ValueType.Some(isSensitive) + } + }, + folderId = when (folderId) { + this.folderId -> { + null + } + null -> { + ValueType.Empty() + } + else -> { + ValueType.Some(folderId) + } + }, ) } } \ No newline at end of file From a9b4fa27301c18becc17562b8042faca0395f2a8 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 12:19:30 +0900 Subject: [PATCH 324/432] =?UTF-8?q?fix:=20=E3=81=AA=E3=81=9C=E3=81=8B?= =?UTF-8?q?=E3=82=BF=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=AE?= =?UTF-8?q?=E7=A8=AE=E5=88=A5=E3=81=AB=E3=83=9E=E3=82=B9=E3=83=88=E3=83=89?= =?UTF-8?q?=E3=83=B3=E3=81=AE=E3=82=BF=E3=82=B0=E3=82=BF=E3=82=A4=E3=83=A0?= =?UTF-8?q?=E3=83=A9=E3=82=A4=E3=83=B3=E3=81=8C=E9=87=8D=E8=A4=87=E3=81=97?= =?UTF-8?q?=E3=81=A6=E5=AE=9F=E8=A3=85=E3=81=95=E3=82=8C=E3=81=A6=E3=81=84?= =?UTF-8?q?=E3=81=9F=E5=95=8F=E9=A1=8C=E3=82=92=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../account/page/PageTypeHelper.kt | 1 - .../notes/MastodonTimelineStorePagingStoreImpl.kt | 13 +------------ .../milktea/search/SearchResultViewPagerAdapter.kt | 2 +- .../setting/activities/PageSettingActivity.kt | 2 +- .../milktea/model/account/page/PageParams.kt | 8 +------- .../milktea/model/account/page/PageType.kt | 1 - .../milktea/model/account/page/Pageable.kt | 13 +------------ .../milktea/model/account/page/PageableTemplate.kt | 2 +- 8 files changed, 6 insertions(+), 36 deletions(-) diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt index 46fc2ff48f..6e17c7e309 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/account/page/PageTypeHelper.kt @@ -36,7 +36,6 @@ object PageTypeHelper{ MASTODON_LOCAL_TIMELINE -> context.getString(R.string.local_timeline) MASTODON_PUBLIC_TIMELINE -> context.getString(R.string.global_timeline) MASTODON_HOME_TIMELINE -> context.getString(R.string.home_timeline) - MASTODON_HASHTAG_TIMELINE -> context.getString(R.string.tag) MASTODON_LIST_TIMELINE -> context.getString(R.string.list) MASTODON_USER_TIMELINE -> context.getString(R.string.user) CALCKEY_RECOMMENDED_TIMELINE -> context.getString(R.string.calckey_recomended_timeline) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt index 4fd8ec9643..7e00396954 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/notes/MastodonTimelineStorePagingStoreImpl.kt @@ -97,12 +97,7 @@ internal class MastodonTimelineStorePagingStoreImpl( is Pageable.Mastodon.TrendTimeline -> { return@runCancellableCatching emptyList() } - is Pageable.Mastodon.TagTimeline -> { - api.getHashtagTimeline( - tag = pageableTimeline.tag, - minId = minId, - ).throwIfHasError().body()!! - } + }.let { list -> if (isShouldUseLinkHeader()) { filterNotExistsStatuses(list) @@ -212,12 +207,6 @@ internal class MastodonTimelineStorePagingStoreImpl( offset = (getState().content as? StateContent.Exist)?.rawContent?.size ?: 0 ).getBodyOrFail() } - is Pageable.Mastodon.TagTimeline -> { - api.getHashtagTimeline( - tag = pageableTimeline.tag, - maxId = maxId, - ).getBodyOrFail() - } }!!.let { list -> if (isShouldUseLinkHeader()) { filterNotExistsStatuses(list) diff --git a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt index 2df2a04a76..d6f02fd591 100644 --- a/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt +++ b/modules/features/search/src/main/java/net/pantasystem/milktea/search/SearchResultViewPagerAdapter.kt @@ -38,7 +38,7 @@ class SearchResultViewPagerAdapter( ) ) SearchResultTabItem.Type.SearchMastodonPostsByTag -> pageableFragmentFactory.create( - Pageable.Mastodon.TagTimeline(item.query) + Pageable.Mastodon.HashTagTimeline(item.query) ) SearchResultTabItem.Type.SearchMastodonUsers -> SearchUserFragment.newInstance(item.query) } diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt index cbc43e99fe..dd964e09b2 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/PageSettingActivity.kt @@ -67,7 +67,7 @@ class PageSettingActivity : AppCompatActivity() { // mPageSettingViewModel.pageAddedEvent.observe(this) { pt -> when (pt.type) { - PageType.SEARCH, PageType.SEARCH_HASH, PageType.MASTODON_HASHTAG_TIMELINE -> startActivity( + PageType.SEARCH, PageType.SEARCH_HASH, PageType.MASTODON_TAG_TIMELINE -> startActivity( searchNavigation.newIntent(SearchNavType.SearchScreen()) ) PageType.USER -> { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt index 58cfbd44bd..fed69037a0 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageParams.kt @@ -162,12 +162,6 @@ data class PageParams( MASTODON_HOME_TIMELINE -> { Pageable.Mastodon.HomeTimeline } - MASTODON_HASHTAG_TIMELINE -> { - Pageable.Mastodon.HashTagTimeline( - requireNotNull(tag), - isOnlyMedia = withFiles, - ) - } MASTODON_LIST_TIMELINE -> { Pageable.Mastodon.ListTimeline( listId = requireNotNull(listId), @@ -199,7 +193,7 @@ data class PageParams( ) } MASTODON_TAG_TIMELINE -> { - Pageable.Mastodon.TagTimeline( + Pageable.Mastodon.HashTagTimeline( requireNotNull(tag) ) } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt index a589b98963..13f0eb2b10 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageType.kt @@ -26,7 +26,6 @@ enum class PageType(val defaultName: String, val label: String){ MASTODON_LOCAL_TIMELINE("MastodonLocalTimeline", "MASTODON_LOCAL_TIMELINE"), MASTODON_PUBLIC_TIMELINE("MastodonPublicTimeline", "MASTODON_PUBLIC_TIMELINE"), MASTODON_HOME_TIMELINE("MastodonHomeTimeline", "MASTODON_HOME_TIMELINE"), - MASTODON_HASHTAG_TIMELINE("MastodonHashtagTimeline", "MASTODON_HASHTAG_TIMELINE"), MASTODON_LIST_TIMELINE("MastodonListTimeline", "MASTODON_LIST_TIMELINE"), MASTODON_USER_TIMELINE("MastodonUserTimeline", "MASTODON_USER_TIMELINE"), MASTODON_BOOKMARK_TIMELINE("MastodonBookmarkTimeline", "MASTODON_BOOKMARK_TIMELINE"), diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt index 7316c06bf6..5958e48963 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/Pageable.kt @@ -413,7 +413,7 @@ sealed class Pageable : Serializable { Mastodon(), CanOnlyMedia, SincePaginate, UntilPaginate { override fun toParams(): PageParams { return PageParams( - type = PageType.MASTODON_HASHTAG_TIMELINE, + type = PageType.MASTODON_TAG_TIMELINE, tag = hashtag, withFiles = isOnlyMedia ) @@ -493,17 +493,6 @@ sealed class Pageable : Serializable { } } - data class TagTimeline( - val tag: String, - ) : Mastodon(), SincePaginate, UntilPaginate { - override fun toParams(): PageParams { - return PageParams( - type = PageType.MASTODON_TAG_TIMELINE, - tag = tag, - ) - } - } - object TrendTimeline : Mastodon() { override fun toParams(): PageParams { return PageParams( diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt index 568ab8804e..7be7cbb97c 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/account/page/PageableTemplate.kt @@ -31,7 +31,7 @@ class PageableTemplate(val account: Account?) { } fun tag(tag: String): Page { return when(account?.instanceType) { - Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> Page(account.accountId, tag, 0, Pageable.Mastodon.TagTimeline(tag.replace("#", ""))) + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> Page(account.accountId, tag, 0, Pageable.Mastodon.HashTagTimeline(tag.replace("#", ""))) else -> Page(account?.accountId?: - 1, tag, 0, Pageable.SearchByTag(tag.replace("#", ""))) } } From 91f3524ace0a2dee43a03b3c5dbf14e72ebefe5e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 12:20:02 +0900 Subject: [PATCH 325/432] =?UTF-8?q?feat:=20=E6=9C=AC=E6=96=87=E3=82=92?= =?UTF-8?q?=E4=BB=96Activity=E3=81=8B=E3=82=89=E5=8F=97=E3=81=91=E4=BB=98?= =?UTF-8?q?=E3=81=91=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/note/NoteEditorActivity.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/NoteEditorActivity.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/NoteEditorActivity.kt index 019cef1285..dc537f1811 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/NoteEditorActivity.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/NoteEditorActivity.kt @@ -16,6 +16,7 @@ import net.pantasystem.milktea.model.file.toAppFile import net.pantasystem.milktea.model.notes.Note import net.pantasystem.milktea.note.databinding.ActivityNoteEditorBinding import net.pantasystem.milktea.note.editor.NoteEditorFragment +import net.pantasystem.milktea.note.editor.viewmodel.NoteEditorSavedStateKey import net.pantasystem.milktea.note.editor.viewmodel.NoteEditorViewModel import javax.inject.Inject @@ -43,6 +44,7 @@ class NoteEditorActivity : AppCompatActivity() { mentions: List? = null, channelId: Channel.Id? = null, accountId: Long? = null, + text: String? = null, ): Intent { return Intent(context, NoteEditorActivity::class.java).apply { replyTo?.let { @@ -73,6 +75,9 @@ class NoteEditorActivity : AppCompatActivity() { putExtra(EXTRA_SPECIFIED_ACCOUNT_ID, it) } + text?.let { + putExtra(NoteEditorSavedStateKey.Text.name, it) + } } } } From db4e3ab8b65da6334d335a948fcba1285082b895 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 12:20:22 +0900 Subject: [PATCH 326/432] =?UTF-8?q?feat:=20=E3=82=BF=E3=82=B0=E3=82=BF?= =?UTF-8?q?=E3=82=A4=E3=83=A0=E3=83=A9=E3=82=A4=E3=83=B3=E3=82=92=E8=A1=A8?= =?UTF-8?q?=E7=A4=BA=E3=81=97=E3=81=A6=E3=81=84=E3=82=8B=E6=99=82=E3=81=AB?= =?UTF-8?q?fab=E3=81=8C=E3=82=AF=E3=83=AA=E3=83=83=E3=82=AF=E3=81=95?= =?UTF-8?q?=E3=82=8C=E3=81=9F=E6=99=82=E3=81=AF=E3=82=BF=E3=82=B0=E3=82=92?= =?UTF-8?q?=E6=9C=AC=E6=96=87=E3=81=AB=E5=90=AB=E3=82=81=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/main/FabClickHandler.kt | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt b/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt index 38ee5f1d1d..271792bfd5 100644 --- a/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt +++ b/app/src/main/java/jp/panta/misskeyandroidclient/ui/main/FabClickHandler.kt @@ -9,6 +9,7 @@ import net.pantasystem.milktea.common_viewmodel.CurrentPageableTimelineViewModel import net.pantasystem.milktea.common_viewmodel.SuitableType import net.pantasystem.milktea.common_viewmodel.suitableType import net.pantasystem.milktea.gallery.GalleryPostsActivity +import net.pantasystem.milktea.model.account.page.Pageable import net.pantasystem.milktea.model.channel.Channel import net.pantasystem.milktea.note.NoteEditorActivity @@ -20,14 +21,28 @@ internal class FabClickHandler( fun onClicked() { activity.apply { - when(val type = currentPageableTimelineViewModel.currentType.value) { + when (val type = currentPageableTimelineViewModel.currentType.value) { CurrentPageType.Account -> { - AccountSwitchingDialog().show(activity.supportFragmentManager, "AccountSwitchingDialog") + AccountSwitchingDialog().show( + activity.supportFragmentManager, + "AccountSwitchingDialog" + ) } is CurrentPageType.Page -> { when (val suitableType = type.pageable.suitableType()) { is SuitableType.Other -> { - startActivity(NoteEditorActivity.newBundle(this, accountId = type.accountId)) + val text = when (val pageable = type.pageable) { + is Pageable.SearchByTag -> "#${pageable.tag}" + is Pageable.Mastodon.HashTagTimeline -> "#${pageable.hashtag}" + else -> "" + } + startActivity( + NoteEditorActivity.newBundle( + this, + accountId = type.accountId, + text = text + ) + ) } is SuitableType.Gallery -> { val intent = Intent(this, GalleryPostsActivity::class.java) From 348dece6b99bae45058de216cb35b83d762a1d71 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:12:02 +0900 Subject: [PATCH 327/432] =?UTF-8?q?feat:=20px=E3=81=A7=E3=82=B5=E3=82=A4?= =?UTF-8?q?=E3=82=BA=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../reaction/CustomEmojiImageViewSizeHelper.kt | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt index 8da39cf158..1a3ca9b0e6 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt @@ -19,11 +19,23 @@ object CustomEmojiImageViewSizeHelper { fun Context.calculateImageWidthAndHeightSize(baseHeightDp: Int, aspectRatio: Float?): Pair { val metrics = resources.displayMetrics val heightPx = baseHeightDp * metrics.density + return calculateImageWidthAndHeightSize(heightPx, aspectRatio) + } + + fun ImageView.applySizeByAspectRatio(baseHeightPx: Float, aspectRatio: Float?) { + val (imageViewWidthPx, imageViewHeightPx) = calculateImageWidthAndHeightSize(baseHeightPx, aspectRatio) + val params = layoutParams as T + params.height = imageViewHeightPx.toInt() + params.width = imageViewWidthPx.toInt() + layoutParams = params + } + + fun calculateImageWidthAndHeightSize(baseHeightPx: Float, aspectRatio: Float?): Pair { val imageViewWidthPx = if (aspectRatio == null) { - heightPx + baseHeightPx } else { - (heightPx * aspectRatio) + (baseHeightPx * aspectRatio) } - return imageViewWidthPx to heightPx + return imageViewWidthPx to baseHeightPx } } \ No newline at end of file From 133366f6e325c8a12cf78dafa933103b14e29d09 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:20:13 +0900 Subject: [PATCH 328/432] =?UTF-8?q?feat:=20TextView=E3=81=AEfontSize?= =?UTF-8?q?=E3=81=AB=E9=96=A2=E3=81=99=E3=82=8Butil=E3=82=92=E4=BD=9C?= =?UTF-8?q?=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_android/ui/FontSizeHelper.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/FontSizeHelper.kt diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/FontSizeHelper.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/FontSizeHelper.kt new file mode 100644 index 0000000000..dbb07fe8a3 --- /dev/null +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/FontSizeHelper.kt @@ -0,0 +1,18 @@ +package net.pantasystem.milktea.common_android.ui + +import android.util.TypedValue +import android.widget.TextView + +object FontSizeHelper { + fun TextView.setMemoFontPxSize(fontSize: Float) { + if (this.textSize == fontSize) { + return + } + this.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize) + } + + fun TextView.setMemoFontSpSize(fontSize: Float) { + val baseHeightPx = context.resources.displayMetrics.scaledDensity * fontSize + setMemoFontPxSize(baseHeightPx) + } +} \ No newline at end of file From 5af8a95f7cac62698621f9eea9826d5ce54734f8 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:20:26 +0900 Subject: [PATCH 329/432] =?UTF-8?q?feat:=20=E3=82=BB=E3=83=B3=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E6=8F=83=E3=81=88=E3=82=92=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/features/note/src/main/res/layout/item_reaction.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/note/src/main/res/layout/item_reaction.xml b/modules/features/note/src/main/res/layout/item_reaction.xml index 6a51445682..c9a57fc6ca 100644 --- a/modules/features/note/src/main/res/layout/item_reaction.xml +++ b/modules/features/note/src/main/res/layout/item_reaction.xml @@ -12,7 +12,7 @@ android:layout_marginBottom="2dp" android:layout_marginTop="2dp" tools:background="@drawable/shape_normal_reaction_backgruond" - + android:gravity="center_vertical" > From 7d7c07e34d29af81aff37ade001e295c3d80ec5b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:20:41 +0900 Subject: [PATCH 330/432] =?UTF-8?q?feat:=20=E5=AE=9F=E9=A8=93=E7=9A=84?= =?UTF-8?q?=E3=81=ABsp=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D?= =?UTF-8?q?=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/reaction/NoteReactionViewHelper.kt | 17 ++++++++++------- .../note/reaction/ReactionCountAdapter.kt | 5 ++++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 346d8ebf36..1713c5384e 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -5,9 +5,9 @@ import android.view.View import android.widget.ImageView import android.widget.LinearLayout import android.widget.TextView -import androidx.databinding.BindingAdapter import dagger.hilt.android.EntryPointAccessors import net.pantasystem.milktea.common.glide.GlideApp +import net.pantasystem.milktea.common_android.ui.FontSizeHelper.setMemoFontPxSize import net.pantasystem.milktea.common_android.ui.VisibilityHelper.setMemoVisibility import net.pantasystem.milktea.common_android_ui.BindingProvider import net.pantasystem.milktea.model.notes.reaction.LegacyReaction @@ -19,37 +19,40 @@ import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData object NoteReactionViewHelper { - const val REACTION_IMAGE_WIDTH_SIZE_DP = 20 +// const val REACTION_IMAGE_WIDTH_SIZE_DP = 20 - @JvmStatic - @BindingAdapter("reactionTextTypeView", "reactionImageTypeView", "reaction") fun LinearLayout.bindReactionCount( reactionTextTypeView: TextView, reactionImageTypeView: ImageView, reaction: ReactionViewData, + reactionBaseSizeSp: Float, ) { val textReaction = reaction.reaction val emoji = reaction.emoji + val baseHeightPx = context.resources.displayMetrics.scaledDensity * reactionBaseSizeSp + if (emoji == null) { reactionImageTypeView.setMemoVisibility(View.GONE) reactionTextTypeView.setMemoVisibility(View.VISIBLE) reactionTextTypeView.text = textReaction + reactionTextTypeView.setMemoFontPxSize(baseHeightPx) } else { reactionImageTypeView.setMemoVisibility(View.VISIBLE) reactionTextTypeView.setMemoVisibility(View.GONE) + val imageAspectRatio = ImageAspectRatioCache.get(emoji.url ?: emoji.uri) ?: emoji.aspectRatio - val (imageViewWidthPx, imageViewHeightPx) = reactionImageTypeView.context.calculateImageWidthAndHeightSize( - REACTION_IMAGE_WIDTH_SIZE_DP, + val (imageViewWidthPx, imageViewHeightPx) = calculateImageWidthAndHeightSize( + baseHeightPx, imageAspectRatio ) reactionImageTypeView.applySizeByAspectRatio( - REACTION_IMAGE_WIDTH_SIZE_DP, + baseHeightPx, imageAspectRatio ) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt index ed3a880234..b3a4be5ac3 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt @@ -6,6 +6,7 @@ import android.view.ViewGroup import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.RecyclerView +import net.pantasystem.milktea.common_android.ui.FontSizeHelper.setMemoFontSpSize import net.pantasystem.milktea.note.databinding.ItemReactionBinding import net.pantasystem.milktea.note.reaction.NoteReactionViewHelper.bindReactionCount import net.pantasystem.milktea.note.reaction.ReactionHelper.applyBackgroundColor @@ -65,10 +66,12 @@ class ReactionHolder(val binding: ItemReactionBinding) : RecyclerView.ViewHolder binding.reactionLayout.bindReactionCount( binding.reactionText, binding.reactionImage, - viewData + viewData, + 15f, ) binding.reactionCounter.text = viewData.reactionCount.count.toString() + binding.reactionCounter.setMemoFontSpSize(15f) binding.root.setOnLongClickListener { val id = note?.toShowNote?.note?.id if (id != null) { From 3701738cb63cec24ee8dff55d0bfe24d16f89428 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:25:45 +0900 Subject: [PATCH 331/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=AB=E3=82=A6=E3=83=B3=E3=82=BF?= =?UTF-8?q?=E3=83=BC=E3=81=AE=E3=83=95=E3=82=A9=E3=83=B3=E3=83=88=E3=82=B5?= =?UTF-8?q?=E3=82=A4=E3=82=BA=E3=81=AE=E8=A8=AD=E5=AE=9A=E3=82=92=E4=BF=9D?= =?UTF-8?q?=E6=8C=81=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/settings/Config.kt | 6 ++++++ .../milktea/data/infrastructure/settings/ConfigKtTest.kt | 4 ++++ .../milktea/data/infrastructure/settings/KeysKtTest.kt | 8 ++++++-- .../java/net/pantasystem/milktea/model/setting/Config.kt | 3 +++ .../java/net/pantasystem/milktea/model/setting/Keys.kt | 4 ++++ 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt index 6463cbb8c8..df015f1964 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt @@ -130,6 +130,9 @@ fun Config.Companion.from(map: Map): Config { isDisplayTimestampsAsAbsoluteDates = map.getValue( Keys.IsDisplayTimestampsAsAbsoluteDates )?.value ?: DefaultConfig.config.isDisplayTimestampsAsAbsoluteDates, + noteReactionCounterFontSize = map.getValue( + Keys.NoteReactionCounterFontSize + )?.value ?: DefaultConfig.config.noteReactionCounterFontSize, ) } @@ -235,6 +238,9 @@ fun Config.pref(key: Keys): PrefType { Keys.IsDisplayTimestampsAsAbsoluteDates -> { PrefType.BoolPref(isDisplayTimestampsAsAbsoluteDates) } + Keys.NoteReactionCounterFontSize -> { + PrefType.FloatPref(noteReactionCounterFontSize) + } } } diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt index cf8b637119..db07060cbd 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt @@ -151,6 +151,10 @@ class ConfigKtTest { config.isDisplayTimestampsAsAbsoluteDates, (u as PrefType.BoolPref).value ) + Keys.NoteReactionCounterFontSize -> Assertions.assertEquals( + config.noteReactionCounterFontSize, + (u as PrefType.FloatPref).value + ) } } } diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt index 3feee59d63..6eaa1475f3 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt @@ -112,6 +112,10 @@ class KeysKtTest { "IsDisplayTimestampsAsAbsoluteDates", key.str() ) + Keys.NoteReactionCounterFontSize -> Assertions.assertEquals( + "NoteReactionCounterFontSize", + key.str() + ) } } } @@ -119,8 +123,8 @@ class KeysKtTest { @Test fun checkAllKeysCount() { - Assertions.assertEquals(30, Keys.allKeys.size) - Assertions.assertEquals(30, Keys.allKeys.map { it.str() }.toSet().size) + Assertions.assertEquals(31, Keys.allKeys.size) + Assertions.assertEquals(31, Keys.allKeys.map { it.str() }.toSet().size) } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt index 1ea943e1ff..dd3451e6b7 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt @@ -52,6 +52,7 @@ data class IsAnalyticsCollectionEnabled( * @param isHideMediaWhenMobileNetwork モバイルネットワークの時はメディアを表示しない * @param noteHeaderFontSize ノートのヘッダー部分のテキストサイズ * @param noteContentFontSize ノートのコンテンツ部分のテキストサイズ + * @param noteReactionCounterFontSize ノートのリアクションカウンターのカスタム絵文字、絵文字と件数表示のフォントサイズ */ data class Config( val isSimpleEditorEnabled: Boolean, @@ -82,6 +83,7 @@ data class Config( val noteHeaderFontSize: Float, val noteContentFontSize: Float, val isDisplayTimestampsAsAbsoluteDates: Boolean, + val noteReactionCounterFontSize: Float, ) { companion object @@ -140,6 +142,7 @@ object DefaultConfig { noteContentFontSize = 15f, noteHeaderFontSize = 15f, isDisplayTimestampsAsAbsoluteDates = false, + noteReactionCounterFontSize = 15f, ) fun getRememberVisibilityConfig(accountId: Long): RememberVisibility.Remember { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt index 7576726a1b..6c4612175d 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt @@ -32,6 +32,7 @@ val Keys.Companion.allKeys by lazy { Keys.NoteHeaderFontSize, Keys.NoteContentFontSize, Keys.IsDisplayTimestampsAsAbsoluteDates, + Keys.NoteReactionCounterFontSize, ) } @@ -91,6 +92,8 @@ sealed interface Keys { object IsDisplayTimestampsAsAbsoluteDates: Keys + object NoteReactionCounterFontSize : Keys + companion object } @@ -126,5 +129,6 @@ fun Keys.str(): String { is Keys.NoteContentFontSize -> "NoteContentFontSize" is Keys.NoteHeaderFontSize -> "NoteHeaderFontSize" is Keys.IsDisplayTimestampsAsAbsoluteDates -> "IsDisplayTimestampsAsAbsoluteDates" + is Keys.NoteReactionCounterFontSize -> "NoteReactionCounterFontSize" } } From 2328757d865eaf5fe54f005af2c7456f4f80b121 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:30:23 +0900 Subject: [PATCH 332/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E9=A0=85?= =?UTF-8?q?=E7=9B=AE=E3=81=AE=E6=96=87=E5=AD=97=E5=88=97=E3=83=AA=E3=82=BD?= =?UTF-8?q?=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 1 + modules/common_resource/src/main/res/values/strings.xml | 1 + 3 files changed, 3 insertions(+) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index e0099a4d07..c69fa9cecd 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -594,6 +594,7 @@ フォローされています ノートコンテンツ文字サイズ(%fsp) ノートヘッダー文字サイズ(%fsp) + ノートリアクション件数表示の絵文字および文字サイズ(%fsp) おすすめユーザ %d人が投稿 引用として添付しますか? diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 3c9942c02f..ca7796e041 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -588,6 +588,7 @@ 正在关注你 Note content font size(%fsp) Note header font size(%fsp) + Note reaction counter font size(%fsp) Suggestions %d people posting Attach as a quote post? diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 6dcd92f50e..95291ba0e7 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -570,6 +570,7 @@ Source code(GitHub) Note content font size(%fsp) Note header font size(%fsp) + Note reaction counter font size(%fsp) From ccf971c99a8edc3a6ca47d25f8d21a0c299f835b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:33:50 +0900 Subject: [PATCH 333/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E3=81=8B?= =?UTF-8?q?=E3=82=89=E3=83=AA=E3=82=A2=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3?= =?UTF-8?q?=E3=82=AB=E3=82=A6=E3=83=B3=E3=82=BF=E3=83=BC=E3=81=AE=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=82=B5=E3=82=A4=E3=82=BA=E3=82=92=E5=A4=89=E6=9B=B4?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activities/SettingAppearanceActivity.kt | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index dda61cdd90..4eb9b65eeb 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -206,7 +206,8 @@ class SettingAppearanceActivity : AppCompatActivity() { SettingSwitchTile( checked = currentConfigState.isVisibleInstanceUrlInToolbar, onChanged = { - currentConfigState = currentConfigState.copy(isVisibleInstanceUrlInToolbar = it) + currentConfigState = + currentConfigState.copy(isVisibleInstanceUrlInToolbar = it) } ) { Text(stringResource(id = R.string.settings_visible_instance_domain_in_toolbar)) @@ -215,7 +216,8 @@ class SettingAppearanceActivity : AppCompatActivity() { SettingSwitchTile( checked = currentConfigState.isDisplayTimestampsAsAbsoluteDates, onChanged = { - currentConfigState = currentConfigState.copy(isDisplayTimestampsAsAbsoluteDates = it) + currentConfigState = + currentConfigState.copy(isDisplayTimestampsAsAbsoluteDates = it) } ) { Text(stringResource(id = R.string.settings_display_timestamps_as_absolute_dates)) @@ -338,6 +340,29 @@ class SettingAppearanceActivity : AppCompatActivity() { modifier = Modifier.padding(horizontal = 16.dp) ) + Text( + stringResource( + id = R.string.settings_note_reaction_counter_font_size, + currentConfigState.noteReactionCounterFontSize + ), + modifier = Modifier.padding(horizontal = 16.dp) + ) + Text( + stringResource(id = R.string.settings_app_restart_required), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colors.error, + fontSize = 14.sp + ) + Slider( + value = currentConfigState.noteReactionCounterFontSize, + valueRange = 10f..24f, + onValueChange = { + currentConfigState = + currentConfigState.copy(noteReactionCounterFontSize = it) + }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + SettingSwitchTile( checked = currentConfigState.isEnableNoteDivider, From 31dcd40709c526d0f44948e24a64eee342b68a22 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:34:15 +0900 Subject: [PATCH 334/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E3=81=AE?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E3=82=92=E5=85=83=E3=81=AB=E3=83=AA=E3=82=A2?= =?UTF-8?q?=E3=82=AF=E3=82=B7=E3=83=A7=E3=83=B3=E3=82=AB=E3=82=A6=E3=83=B3?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=81=AE=E7=B5=B5=E6=96=87=E5=AD=97=E3=83=BB?= =?UTF-8?q?=E4=BB=B6=E6=95=B0=E8=A1=A8=E7=A4=BA=E3=81=AE=E3=82=B5=E3=82=A4?= =?UTF-8?q?=E3=82=BA=E3=82=92=E5=A4=89=E6=9B=B4=E3=81=99=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/reaction/ReactionCountAdapter.kt | 48 +++++++++++-------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt index b3a4be5ac3..8824761659 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt @@ -13,29 +13,30 @@ import net.pantasystem.milktea.note.reaction.ReactionHelper.applyBackgroundColor import net.pantasystem.milktea.note.viewmodel.PlaneNoteViewData class ReactionCountAdapter( - val reactionCountActionListener: (ReactionCountAction) -> Unit + val reactionCountActionListener: (ReactionCountAction) -> Unit, ) : ListAdapter( reactionDiffUtilItemCallback ) { companion object { - private val reactionDiffUtilItemCallback = object : DiffUtil.ItemCallback() { - override fun areContentsTheSame( - oldItem: ReactionViewData, - newItem: ReactionViewData - ): Boolean { - return oldItem == newItem - } + private val reactionDiffUtilItemCallback = + object : DiffUtil.ItemCallback() { + override fun areContentsTheSame( + oldItem: ReactionViewData, + newItem: ReactionViewData, + ): Boolean { + return oldItem == newItem + } - override fun areItemsTheSame( - oldItem: ReactionViewData, - newItem: ReactionViewData - ): Boolean { - return oldItem.noteId == newItem.noteId - && oldItem.reactionCount.reaction == newItem.reactionCount.reaction + override fun areItemsTheSame( + oldItem: ReactionViewData, + newItem: ReactionViewData, + ): Boolean { + return oldItem.noteId == newItem.noteId + && oldItem.reactionCount.reaction == newItem.reactionCount.reaction + } } - } } var note: PlaneNoteViewData? = null @@ -57,21 +58,30 @@ class ReactionCountAdapter( } class ReactionHolder(val binding: ItemReactionBinding) : RecyclerView.ViewHolder(binding.root) { - fun onBind(viewData: ReactionViewData, note: PlaneNoteViewData?, reactionCountActionListener: (ReactionCountAction) -> Unit) { + fun onBind( + viewData: ReactionViewData, + note: PlaneNoteViewData?, + reactionCountActionListener: (ReactionCountAction) -> Unit, + ) { if (note == null) { Log.w("ReactionCountAdapter", "noteがNullです。正常に処理が行われない可能性があります。") } - binding.reactionLayout.applyBackgroundColor(viewData, note?.toShowNote?.note?.isMisskey ?: false) + binding.reactionLayout.applyBackgroundColor( + viewData, + note?.toShowNote?.note?.isMisskey ?: false + ) binding.reactionLayout.bindReactionCount( binding.reactionText, binding.reactionImage, viewData, - 15f, + note?.config?.value?.noteReactionCounterFontSize ?: 15f ) binding.reactionCounter.text = viewData.reactionCount.count.toString() - binding.reactionCounter.setMemoFontSpSize(15f) + binding.reactionCounter.setMemoFontSpSize( + note?.config?.value?.noteReactionCounterFontSize ?: 15f + ) binding.root.setOnLongClickListener { val id = note?.toShowNote?.note?.id if (id != null) { From e8add2adb554cf3abbd33077066a4843dca75fff Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 13:41:50 +0900 Subject: [PATCH 335/432] =?UTF-8?q?feat:=20=E3=83=87=E3=83=95=E3=82=A9?= =?UTF-8?q?=E3=83=AB=E3=83=88=E5=80=A4=E3=82=9216=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/net/pantasystem/milktea/model/setting/Config.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt index dd3451e6b7..1ddfbeae03 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt @@ -142,7 +142,7 @@ object DefaultConfig { noteContentFontSize = 15f, noteHeaderFontSize = 15f, isDisplayTimestampsAsAbsoluteDates = false, - noteReactionCounterFontSize = 15f, + noteReactionCounterFontSize = 16f, ) fun getRememberVisibilityConfig(accountId: Long): RememberVisibility.Remember { From c8d927d30cf8e954908618671a42ace7e9baad19 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 14:09:05 +0900 Subject: [PATCH 336/432] =?UTF-8?q?feat:=20=E3=82=B5=E3=82=A4=E3=82=BA?= =?UTF-8?q?=E3=82=92=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/note/reaction/ReactionCountAdapter.kt | 2 +- .../main/java/net/pantasystem/milktea/model/setting/Config.kt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt index 8824761659..1a87827ea2 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/ReactionCountAdapter.kt @@ -75,7 +75,7 @@ class ReactionHolder(val binding: ItemReactionBinding) : RecyclerView.ViewHolder binding.reactionText, binding.reactionImage, viewData, - note?.config?.value?.noteReactionCounterFontSize ?: 15f + (note?.config?.value?.noteReactionCounterFontSize ?: 15f) * 1.2f ) binding.reactionCounter.text = viewData.reactionCount.count.toString() diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt index 1ddfbeae03..dd3451e6b7 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt @@ -142,7 +142,7 @@ object DefaultConfig { noteContentFontSize = 15f, noteHeaderFontSize = 15f, isDisplayTimestampsAsAbsoluteDates = false, - noteReactionCounterFontSize = 16f, + noteReactionCounterFontSize = 15f, ) fun getRememberVisibilityConfig(accountId: Long): RememberVisibility.Remember { From 1ee48b3898318cf995234edb6ff3c9551dd5b2bb Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 14:12:15 +0900 Subject: [PATCH 337/432] =?UTF-8?q?feat:=20=E3=82=AB=E3=82=A6=E3=83=B3?= =?UTF-8?q?=E3=82=BF=E3=83=BC=E3=81=AE=E3=82=B5=E3=82=A4=E3=82=BA=E3=81=A8?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE=E3=82=B5=E3=82=A4=E3=82=BA?= =?UTF-8?q?=E3=81=AE=E6=AF=94=E7=8E=87=E3=81=8C=E7=95=B0=E3=81=AA=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=AA=E3=81=A3=E3=81=9F=E3=81=AE?= =?UTF-8?q?=E3=81=A7=E3=81=9D=E3=82=8C=E3=82=92=E5=8F=8D=E6=98=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 2 +- modules/common_resource/src/main/res/values-zh/strings.xml | 2 +- modules/common_resource/src/main/res/values/strings.xml | 2 +- .../milktea/setting/activities/SettingAppearanceActivity.kt | 1 + 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index c69fa9cecd..8b41d93955 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -594,7 +594,7 @@ フォローされています ノートコンテンツ文字サイズ(%fsp) ノートヘッダー文字サイズ(%fsp) - ノートリアクション件数表示の絵文字および文字サイズ(%fsp) + ノートリアクション件数表示の絵文字および文字サイズ(%fsp, %fps) おすすめユーザ %d人が投稿 引用として添付しますか? diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index ca7796e041..8600e9fb8d 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -588,7 +588,7 @@ 正在关注你 Note content font size(%fsp) Note header font size(%fsp) - Note reaction counter font size(%fsp) + Note reaction counter font size(%fsp, %fps) Suggestions %d people posting Attach as a quote post? diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 95291ba0e7..dd3c402af9 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -570,7 +570,7 @@ Source code(GitHub) Note content font size(%fsp) Note header font size(%fsp) - Note reaction counter font size(%fsp) + Note reaction counter font size(%fsp, %fsp) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index 4eb9b65eeb..2d29ccceb7 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -343,6 +343,7 @@ class SettingAppearanceActivity : AppCompatActivity() { Text( stringResource( id = R.string.settings_note_reaction_counter_font_size, + currentConfigState.noteReactionCounterFontSize * 1.2f, currentConfigState.noteReactionCounterFontSize ), modifier = Modifier.padding(horizontal = 16.dp) From e06c5dd94e804ae61de73c0cbb68d77ad720db94 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 14:25:00 +0900 Subject: [PATCH 338/432] fix --- .../reaction/CustomEmojiImageViewSizeHelper.kt | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt index 1a3ca9b0e6..b3afd5a4ae 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/CustomEmojiImageViewSizeHelper.kt @@ -2,31 +2,23 @@ package net.pantasystem.milktea.note.reaction -import android.content.Context import android.view.ViewGroup import android.widget.ImageView +import kotlin.math.max object CustomEmojiImageViewSizeHelper { fun ImageView.applySizeByAspectRatio(baseHeightDp: Int, aspectRatio: Float?) { - val (imageViewWidthPx, imageViewHeightPx) = context.calculateImageWidthAndHeightSize(baseHeightDp, aspectRatio) - val params = layoutParams as T - params.height = imageViewHeightPx.toInt() - params.width = imageViewWidthPx.toInt() - layoutParams = params - } - - fun Context.calculateImageWidthAndHeightSize(baseHeightDp: Int, aspectRatio: Float?): Pair { val metrics = resources.displayMetrics val heightPx = baseHeightDp * metrics.density - return calculateImageWidthAndHeightSize(heightPx, aspectRatio) + applySizeByAspectRatio(heightPx, aspectRatio) } fun ImageView.applySizeByAspectRatio(baseHeightPx: Float, aspectRatio: Float?) { val (imageViewWidthPx, imageViewHeightPx) = calculateImageWidthAndHeightSize(baseHeightPx, aspectRatio) val params = layoutParams as T params.height = imageViewHeightPx.toInt() - params.width = imageViewWidthPx.toInt() + params.width = max(imageViewWidthPx, imageViewHeightPx).toInt() layoutParams = params } From 85d891d9eee91b88a876090c7bab9ae26230dfd9 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Thu, 15 Jun 2023 18:40:01 +0900 Subject: [PATCH 339/432] =?UTF-8?q?feat:=20=E3=83=AC=E3=82=A4=E3=82=A2?= =?UTF-8?q?=E3=82=A6=E3=83=88=E3=82=92=E8=AA=BF=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/features/note/src/main/res/layout/item_reaction.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/features/note/src/main/res/layout/item_reaction.xml b/modules/features/note/src/main/res/layout/item_reaction.xml index c9a57fc6ca..8e49db4140 100644 --- a/modules/features/note/src/main/res/layout/item_reaction.xml +++ b/modules/features/note/src/main/res/layout/item_reaction.xml @@ -6,7 +6,9 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" - android:padding="5dp" + android:paddingHorizontal="6dp" + android:paddingVertical="4dp" + android:layout_marginStart="4dp" android:layout_marginBottom="2dp" From 308d9e4cbe14ffc3a62adc1cf7279d31a8219226 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 00:39:54 +0900 Subject: [PATCH 340/432] =?UTF-8?q?refactor:=20=E9=87=8D=E8=A4=87=E3=81=97?= =?UTF-8?q?=E3=81=A6=E3=81=84=E3=81=9F=E8=B2=AC=E5=8B=99=E3=81=AE=E3=83=AD?= =?UTF-8?q?=E3=82=B8=E3=83=83=E3=82=AF=E3=82=92=E5=85=B1=E9=80=9A=E5=8C=96?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_android/ui/text/EmojiSpan.kt | 116 +++++++++--------- 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt index 6cd2306030..a27e1f86f8 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt @@ -45,32 +45,7 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : ): Int { val textHeight = paint.textSize - val drawable = imageDrawable - val size = key?.let { - drawableSizeCache[key] - } ?: drawable?.let { - // NOTE: drawableSizeCacheに画像のサイズが登録されていない場合は、drawableからサイズを取得する - EmojiSizeCache( - intrinsicHeight = it.intrinsicHeight, - intrinsicWidth = it.intrinsicWidth - ) - } ?: aspectRatio?.let { - // NOTE: drawableが読み込まれていない状態の時は、文字の高さと画像の比率から横幅のサイズを取得する - EmojiSizeCache( - intrinsicHeight = textHeight.toInt(), - intrinsicWidth = (textHeight * aspectRatio).toInt() - ) - } - - // NOTE: keyが存在しかつdrawableが存在する場合は、drawableSizeCacheを更新する - key?.run { - drawableSizeCache[key] ?: drawable?.let { - EmojiSizeCache( - intrinsicHeight = it.intrinsicHeight, - intrinsicWidth = it.intrinsicWidth - ) - } - } + val size = calculateEmojiSize(textHeight) val metrics = paint.fontMetricsInt if (fm != null) { fm.top = metrics.top @@ -84,20 +59,13 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : beforeTextSize = (paint.textSize * 1.2).toInt() return beforeTextSize } - key?.run { - drawableSizeCache[key] = size - } // NOTE: 暫定的なサイズではない場合はbeforeTextSizeを0にする必要性がある beforeTextSize = 0 - val imageWidth = size.intrinsicWidth - val imageHeight = size.intrinsicHeight + val imageWidth = size.first - // 画像がテキストの高さよりも大きい場合、画像をテキストと同じ高さに縮小する - val scale = textHeight / imageHeight.toFloat() - // テキストの高さに合わせた画像の幅 - return (imageWidth * scale).toInt() + return imageWidth.toInt() } override fun updateDrawState(ds: TextPaint) { @@ -138,27 +106,9 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : */ private fun updateImageDrawableSize(paint: Paint) { val emojiHeight = min((paint.textSize).toInt(), 128) - val drawable = imageDrawable - - // drawableSizeCacheあるいはdrawableあるいはaspectRatioと文字サイズから画像のwidth, heightを得る処理 - val size = key?.let { - drawableSizeCache[key] - } ?: drawable?.let { - EmojiSizeCache( - intrinsicWidth = it.intrinsicWidth, - intrinsicHeight = it.intrinsicHeight - ) - } ?: aspectRatio?.let { - EmojiSizeCache( - intrinsicHeight = emojiHeight, - intrinsicWidth = (emojiHeight.toFloat() * aspectRatio).toInt() - ) - } ?: return - key?.run { - drawableSizeCache[key] = size - } - val imageWidth = size.intrinsicWidth - val imageHeight = size.intrinsicHeight + val size = calculateEmojiSize(min((paint.textSize), 128f)) + val imageWidth = size?.first ?: -1f + val imageHeight = size?.second?: -1f // 計算された画像サイズが適切なものかチェックする val unknownEmojiSize = imageWidth <= 0 || imageHeight <= 0 @@ -173,14 +123,58 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : return } - val ratio = imageWidth.toFloat() / imageHeight.toFloat() - - val scaledImageWidth = (emojiHeight * ratio).toInt() - if (!isSizeComputed) { isSizeComputed = imageDrawable != null - imageDrawable?.setBounds(0, 0, scaledImageWidth, emojiHeight) + imageDrawable?.setBounds(0, 0, imageWidth.toInt(), imageHeight.toInt()) + } + } + + private fun calculateEmojiSize(textSize: Float): Pair? { + val drawable = imageDrawable + val size = key?.let { + drawableSizeCache[key] + } ?: drawable?.let { + // NOTE: drawableSizeCacheに画像のサイズが登録されていない場合は、drawableからサイズを取得する + EmojiSizeCache( + intrinsicHeight = it.intrinsicHeight, + intrinsicWidth = it.intrinsicWidth + ) + } ?: aspectRatio?.let { + // NOTE: drawableが読み込まれていない状態の時は、文字の高さと画像の比率から横幅のサイズを取得する + EmojiSizeCache( + intrinsicHeight = textSize.toInt(), + intrinsicWidth = (textSize * aspectRatio).toInt() + ) } + + // NOTE: keyが存在しかつdrawableが存在する場合は、drawableSizeCacheを更新する + key?.run { + drawableSizeCache[key] ?: drawable?.let { + EmojiSizeCache( + intrinsicHeight = it.intrinsicHeight, + intrinsicWidth = it.intrinsicWidth + ) + } + } + + // NOTE: 画像のサイズが不明なときはnullを返す + if (size == null) { + return null + } + key?.run { + drawableSizeCache[key] = size + } + + val imageWidth = size.intrinsicWidth + val imageHeight = size.intrinsicHeight + + // 画像がテキストの高さよりも大きい場合、画像をテキストと同じ高さに縮小する + val scale = textSize / imageHeight + + // テキストの高さに合わせた画像の幅 + val width = imageWidth * scale + + return width to textSize } } From 47b86169b83427954315a6cc71931455dafaa549 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 00:49:25 +0900 Subject: [PATCH 341/432] =?UTF-8?q?feat:=20scale=E3=82=92=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_android/ui/text/DrawableEmojiSpan.kt | 14 +++++++++----- .../milktea/common_android/ui/text/EmojiSpan.kt | 8 ++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DrawableEmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DrawableEmojiSpan.kt index 7fe5764f8d..ed29aa55b9 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DrawableEmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/DrawableEmojiSpan.kt @@ -9,12 +9,15 @@ import com.bumptech.glide.request.target.CustomTarget import com.bumptech.glide.request.transition.Transition import com.github.penfeizhou.animation.apng.APNGDrawable -class DrawableEmojiSpan(var adapter: EmojiAdapter?, k: Any?, aspectRatio: Float? = null) : EmojiSpan(k, aspectRatio = aspectRatio){ +class DrawableEmojiSpan( + var adapter: EmojiAdapter?, + k: Any?, + aspectRatio: Float? = null, + emojiScale: Float = 1f, +) : EmojiSpan(k, aspectRatio = aspectRatio, emojiScale = emojiScale) { //val weakReference: WeakReference = WeakReference(view) - - // /** // * invalidateSelfによって呼び出されるコールバックを実装することによって // * invalidateSelfが呼び出されたときに自信のview.invalidateを呼び出し再描画をする @@ -74,11 +77,11 @@ class DrawableEmojiSpan(var adapter: EmojiAdapter?, k: Any?, aspectRatio: Float? } private class DrawableEmojiTarget( - val span: DrawableEmojiSpan + val span: DrawableEmojiSpan, ) : CustomTarget() { override fun onResourceReady( resource: Drawable, - transition: Transition? + transition: Transition?, ) { span.imageDrawable = resource @@ -118,6 +121,7 @@ private class DrawableEmojiTarget( } } } + override fun onLoadCleared(placeholder: Drawable?) { } diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt index a27e1f86f8..4c02f4176b 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt @@ -11,7 +11,7 @@ import kotlin.math.min * @param key 画像の種別を識別するためのキー値で、画像のURLなどが入る * @param aspectRatio 画像の比率が入る */ -abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : ReplacementSpan(){ +abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null, val emojiScale: Float = 1f) : ReplacementSpan(){ companion object { /** @@ -45,7 +45,7 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : ): Int { val textHeight = paint.textSize - val size = calculateEmojiSize(textHeight) + val size = calculateEmojiSize(textHeight * emojiScale) val metrics = paint.fontMetricsInt if (fm != null) { fm.top = metrics.top @@ -105,8 +105,8 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null) : * Drawableのサイズを必要なサイズにリサイズを行う処理 */ private fun updateImageDrawableSize(paint: Paint) { - val emojiHeight = min((paint.textSize).toInt(), 128) - val size = calculateEmojiSize(min((paint.textSize), 128f)) + val emojiHeight = min((paint.textSize * emojiScale).toInt(), 128) + val size = calculateEmojiSize(min((paint.textSize * emojiScale), 128f)) val imageWidth = size?.first ?: -1f val imageHeight = size?.second?: -1f From ccd1ae74337d8adf7536984c3f18a96137a88313 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 00:57:38 +0900 Subject: [PATCH 342/432] =?UTF-8?q?feat:=20=E7=94=BB=E5=83=8F=E3=81=8C?= =?UTF-8?q?=E8=A2=AB=E3=82=89=E3=81=AA=E3=81=84=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/common_android/ui/text/EmojiSpan.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt index 4c02f4176b..4313e74d16 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/EmojiSpan.kt @@ -48,7 +48,7 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null, va val size = calculateEmojiSize(textHeight * emojiScale) val metrics = paint.fontMetricsInt if (fm != null) { - fm.top = metrics.top + fm.top = metrics.top - (textHeight * emojiScale - textHeight).toInt() fm.ascent = metrics.ascent fm.descent = metrics.descent fm.bottom = metrics.bottom @@ -56,7 +56,7 @@ abstract class EmojiSpan(val key: T, val aspectRatio: Float? = null, va // NOTE: 画像のサイズが不明かつ初めてサイズを取得しようとした時は暫定的なサイズを返す if (size == null || beforeTextSize != 0) { - beforeTextSize = (paint.textSize * 1.2).toInt() + beforeTextSize = (paint.textSize * emojiScale).toInt() return beforeTextSize } From 6071535f279d7d3c2fc41fe7ab795c5d330df41b Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 01:10:38 +0900 Subject: [PATCH 343/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E7=8A=B6?= =?UTF-8?q?=E6=85=8B=E3=81=AB=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0=E7=B5=B5?= =?UTF-8?q?=E6=96=87=E5=AD=97=E3=81=AE=E3=82=B9=E3=82=B1=E3=83=BC=E3=83=AB?= =?UTF-8?q?=E3=82=92=E4=BF=9D=E6=8C=81=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/settings/Config.kt | 6 ++++++ .../java/net/pantasystem/milktea/model/setting/Config.kt | 2 ++ .../main/java/net/pantasystem/milktea/model/setting/Keys.kt | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt index df015f1964..23fa1c1d9d 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt @@ -133,6 +133,9 @@ fun Config.Companion.from(map: Map): Config { noteReactionCounterFontSize = map.getValue( Keys.NoteReactionCounterFontSize )?.value ?: DefaultConfig.config.noteReactionCounterFontSize, + noteCustomEmojiScaleSizeInText = map.getValue( + Keys.NoteCustomEmojiScaleSizeInText + )?.value ?: DefaultConfig.config.noteCustomEmojiScaleSizeInText, ) } @@ -241,6 +244,9 @@ fun Config.pref(key: Keys): PrefType { Keys.NoteReactionCounterFontSize -> { PrefType.FloatPref(noteReactionCounterFontSize) } + Keys.NoteCustomEmojiScaleSizeInText -> { + PrefType.FloatPref(noteCustomEmojiScaleSizeInText) + } } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt index dd3451e6b7..4b09060efe 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt @@ -84,6 +84,7 @@ data class Config( val noteContentFontSize: Float, val isDisplayTimestampsAsAbsoluteDates: Boolean, val noteReactionCounterFontSize: Float, + val noteCustomEmojiScaleSizeInText: Float, ) { companion object @@ -143,6 +144,7 @@ object DefaultConfig { noteHeaderFontSize = 15f, isDisplayTimestampsAsAbsoluteDates = false, noteReactionCounterFontSize = 15f, + noteCustomEmojiScaleSizeInText = 1.2f, ) fun getRememberVisibilityConfig(accountId: Long): RememberVisibility.Remember { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt index 6c4612175d..aacac1dcf1 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt @@ -33,6 +33,7 @@ val Keys.Companion.allKeys by lazy { Keys.NoteContentFontSize, Keys.IsDisplayTimestampsAsAbsoluteDates, Keys.NoteReactionCounterFontSize, + Keys.NoteCustomEmojiScaleSizeInText, ) } @@ -94,6 +95,8 @@ sealed interface Keys { object NoteReactionCounterFontSize : Keys + object NoteCustomEmojiScaleSizeInText : Keys + companion object } @@ -130,5 +133,6 @@ fun Keys.str(): String { is Keys.NoteHeaderFontSize -> "NoteHeaderFontSize" is Keys.IsDisplayTimestampsAsAbsoluteDates -> "IsDisplayTimestampsAsAbsoluteDates" is Keys.NoteReactionCounterFontSize -> "NoteReactionCounterFontSize" + is Keys.NoteCustomEmojiScaleSizeInText -> "NoteCustomEmojiScaleSizeInText" } } From b02c4e2561652798222103777b661cbcc7cd2aef Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 01:14:47 +0900 Subject: [PATCH 344/432] =?UTF-8?q?feat:=20=E8=A8=AD=E5=AE=9A=E3=81=AB?= =?UTF-8?q?=E5=BF=9C=E3=81=98=E3=81=A6=E3=82=AB=E3=82=B9=E3=82=BF=E3=83=A0?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE=E3=82=B9=E3=82=B1=E3=83=BC?= =?UTF-8?q?=E3=83=AB=E3=82=92=E5=A4=89=E3=81=88=E3=82=89=E3=82=8C=E3=82=8B?= =?UTF-8?q?=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common_android/ui/text/CustomEmojiDecorator.kt | 14 ++++++++++++-- .../common_android_ui/DecorateTextHelper.kt | 11 +++++++---- .../milktea/common_android_ui/MFMDecorator.kt | 7 +++++-- .../note/src/main/res/layout/item_detail_note.xml | 2 ++ .../src/main/res/layout/item_has_reply_to_note.xml | 1 + .../res/layout/item_note_editor_reply_to_note.xml | 2 ++ .../note/src/main/res/layout/item_simple_note.xml | 2 ++ 7 files changed, 31 insertions(+), 8 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt index fe219e3c49..ea92f40f0b 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt @@ -8,6 +8,7 @@ import net.pantasystem.milktea.model.emoji.CustomEmojiParsedResult import net.pantasystem.milktea.model.emoji.CustomEmojiParser import net.pantasystem.milktea.model.emoji.Emoji import net.pantasystem.milktea.model.emoji.EmojiResolvedType +import kotlin.math.max class CustomEmojiDecorator { @@ -78,6 +79,7 @@ class CustomEmojiDecorator { accountHost: String?, result: CustomEmojiParsedResult, view: TextView, + customEmojiScale: Float = 1f, ): Spanned { val emojiAdapter = EmojiAdapter(view) @@ -86,15 +88,23 @@ class CustomEmojiDecorator { result.emojis.filter { it.result is EmojiResolvedType.Resolved }.map { + val aspectRatio = (it.result as? EmojiResolvedType.Resolved)?.emoji?.aspectRatio val span = DrawableEmojiSpan( emojiAdapter, it.result.getUrl(accountHost), - (it.result as? EmojiResolvedType.Resolved)?.emoji?.aspectRatio + aspectRatio, + customEmojiScale, ) + val height = max(view.textSize * 0.75f, 10f) + val width = when(aspectRatio) { + null -> height + else -> height * aspectRatio + } + GlideApp.with(view) .asDrawable() .load(it.result.getUrl(accountHost)) - .override(view.textSize.toInt()) + .override((width * customEmojiScale).toInt(), (height * customEmojiScale).toInt()) .into(span.target) builder.setSpan(span, it.start, it.end, 0) } diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt index bbe9a14ef7..648c63d488 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/DecorateTextHelper.kt @@ -71,18 +71,21 @@ object DecorateTextHelper { } } - @BindingAdapter("textTypeSource") + @BindingAdapter("textTypeSource", "customEmojiScale") @JvmStatic - fun TextView.decorate(textType: TextType?) { + fun TextView.decorate(textType: TextType?, customEmojiScale: Float?) { textType ?: return stopDrawableAnimations(this) + + val emojiScale = customEmojiScale ?: 1.0f when (textType) { is TextType.Mastodon -> { val decoratedText = CustomEmojiDecorator().decorate( textType.html.spanned, textType.html.accountHost, textType.html.parserResult, - this + this, + emojiScale, ) this.text = decoratedText this.movementMethod = ClickListenableLinkMovementMethod { url -> @@ -141,7 +144,7 @@ object DecorateTextHelper { } is TextType.Misskey -> { this.movementMethod = LinkMovementMethod.getInstance() - this.text = MFMDecorator.decorate(this, textType.lazyDecorateResult) + this.text = MFMDecorator.decorate(this, textType.lazyDecorateResult, emojiScale) } } diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt index 1753c6247f..994f92a7c1 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt @@ -34,6 +34,7 @@ object MFMDecorator { fun decorate( textView: TextView, lazyDecorateResult: LazyDecorateResult?, + customEmojiScale: Float = 1f, skipEmojis: SkipEmojiHolder = SkipEmojiHolder(), ): Spanned? { lazyDecorateResult ?: return null @@ -45,6 +46,7 @@ object MFMDecorator { lazyDecorateResult, skipEmojis, emojiAdapter, + customEmojiScale, ).decorate() } @@ -289,6 +291,7 @@ object MFMDecorator { private val lazyDecorateResult: LazyDecorateResult, private val skipEmojis: SkipEmojiHolder, private val emojiAdapter: EmojiAdapter, + private val customEmojiScale: Float, ) { private val spannableString = SpannableString(lazyDecorateResult.spanned) @@ -309,7 +312,7 @@ object MFMDecorator { return } textView.get()?.let { textView -> - val emojiSpan = DrawableEmojiSpan(emojiAdapter, emojiElement.emoji.url, emojiElement.emoji.aspectRatio) + val emojiSpan = DrawableEmojiSpan(emojiAdapter, emojiElement.emoji.url, emojiElement.emoji.aspectRatio, customEmojiScale) spannableString.setSpan(emojiSpan, skippedEmoji.start, skippedEmoji.end, 0) val height = max(textView.textSize * 0.75f, 10f) val width = when(val aspectRatio = emojiElement.emoji.aspectRatio) { @@ -318,7 +321,7 @@ object MFMDecorator { } GlideApp.with(textView) .load(emojiElement.emoji.url) - .override(width.toInt(), height.toInt()) + .override((width * customEmojiScale).toInt(), (height * customEmojiScale).toInt()) .into(emojiSpan.target) } } diff --git a/modules/features/note/src/main/res/layout/item_detail_note.xml b/modules/features/note/src/main/res/layout/item_detail_note.xml index 5dc137debc..65187b5760 100644 --- a/modules/features/note/src/main/res/layout/item_detail_note.xml +++ b/modules/features/note/src/main/res/layout/item_detail_note.xml @@ -161,6 +161,7 @@ android:textSize="@dimen/note_content_text_size" android:visibility='@{note.text == null ? View.GONE : View.VISIBLE}' textTypeSource="@{note.textNode}" + customEmojiScale="@{note.config.noteCustomEmojiScaleSizeInText}" tools:text="aoiwefjowiaejiowajefihawoefoiawehfioawheoifawoiefioawejfowaoeifjawoiejfoaw" /> diff --git a/modules/features/note/src/main/res/layout/item_note_editor_reply_to_note.xml b/modules/features/note/src/main/res/layout/item_note_editor_reply_to_note.xml index 4fa559b402..d3f5eb37c5 100644 --- a/modules/features/note/src/main/res/layout/item_note_editor_reply_to_note.xml +++ b/modules/features/note/src/main/res/layout/item_note_editor_reply_to_note.xml @@ -190,6 +190,7 @@ android:layout_height="wrap_content" android:textSize="@dimen/note_content_text_size" textTypeSource="@{note.textNode}" + customEmojiScale="@{note.config.noteCustomEmojiScaleSizeInText}" tools:text="aoiwefjowiaejiowajefihawoefoiawehfioawheoifawoiefioawejfowaoeifjawoiejfoaw" android:visibility='@{note.text == null ? View.GONE : View.VISIBLE}' /> @@ -347,6 +348,7 @@ android:id="@+id/subNoteText" tools:text="aowjfoiwajehofijawioefjioawejfiowajeiofhawoifahwoiefwaioe" textTypeSource="@{note.subNoteTextNode}" + customEmojiScale="@{note.config.noteCustomEmojiScaleSizeInText}" app:layout_constraintTop_toBottomOf="@id/subContentFoldingButton" app:layout_constraintStart_toStartOf="parent" android:visibility="@{ note.subContentFolding || note.subNote.note.text == null ? View.GONE : View.VISIBLE }"/> diff --git a/modules/features/note/src/main/res/layout/item_simple_note.xml b/modules/features/note/src/main/res/layout/item_simple_note.xml index 4544f5fd90..067839b026 100644 --- a/modules/features/note/src/main/res/layout/item_simple_note.xml +++ b/modules/features/note/src/main/res/layout/item_simple_note.xml @@ -196,6 +196,7 @@ android:layout_height="wrap_content" android:textSize="@dimen/note_content_text_size" textTypeSource="@{note.textNode}" + customEmojiScale="@{note.config.noteCustomEmojiScaleSizeInText}" tools:text="aoiwefjowiaejiowajefihawoefoiawehfioawheoifawoiefioawejfowaoeifjawoiejfoaw" memoVisibility='@{note.text == null ? View.GONE : View.VISIBLE}' @@ -354,6 +355,7 @@ android:id="@+id/subNoteText" tools:text="aowjfoiwajehofijawioefjioawejfiowajeiofhawoifahwoiefwaioe" textTypeSource="@{note.subNoteTextNode}" + customEmojiScale="@{note.config.noteCustomEmojiScaleSizeInText}" app:layout_constraintTop_toBottomOf="@id/subContentFoldingButton" app:layout_constraintStart_toStartOf="parent" memoVisibility="@{ note.subContentFolding || note.subNote.note.text == null ? View.GONE : View.VISIBLE }"/> From 907bb54e73f9706f53bfd23995196a537f6c1c0a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 01:26:08 +0900 Subject: [PATCH 345/432] =?UTF-8?q?feat:=20=E6=9C=80=E7=B5=82=E7=9A=84?= =?UTF-8?q?=E3=81=ABRelativeSizeSpan=E3=82=92=E7=94=A8=E3=81=84=E3=81=A6?= =?UTF-8?q?=E3=82=B9=E3=82=B1=E3=83=BC=E3=83=AB=E3=82=92=E8=AA=BF=E6=95=B4?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/text/CustomEmojiDecorator.kt | 3 ++- .../milktea/common_android_ui/MFMDecorator.kt | 3 ++- .../activities/SettingAppearanceActivity.kt | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt index ea92f40f0b..2c87fce59c 100644 --- a/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt +++ b/modules/common_android/src/main/java/net/pantasystem/milktea/common_android/ui/text/CustomEmojiDecorator.kt @@ -2,6 +2,7 @@ package net.pantasystem.milktea.common_android.ui.text import android.text.SpannableStringBuilder import android.text.Spanned +import android.text.style.RelativeSizeSpan import android.widget.TextView import net.pantasystem.milktea.common.glide.GlideApp import net.pantasystem.milktea.model.emoji.CustomEmojiParsedResult @@ -93,7 +94,6 @@ class CustomEmojiDecorator { emojiAdapter, it.result.getUrl(accountHost), aspectRatio, - customEmojiScale, ) val height = max(view.textSize * 0.75f, 10f) val width = when(aspectRatio) { @@ -107,6 +107,7 @@ class CustomEmojiDecorator { .override((width * customEmojiScale).toInt(), (height * customEmojiScale).toInt()) .into(span.target) builder.setSpan(span, it.start, it.end, 0) + builder.setSpan(RelativeSizeSpan(customEmojiScale), it.start, it.end, 0) } diff --git a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt index 994f92a7c1..1b4b710460 100644 --- a/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt +++ b/modules/common_android_ui/src/main/java/net/pantasystem/milktea/common_android_ui/MFMDecorator.kt @@ -312,8 +312,9 @@ object MFMDecorator { return } textView.get()?.let { textView -> - val emojiSpan = DrawableEmojiSpan(emojiAdapter, emojiElement.emoji.url, emojiElement.emoji.aspectRatio, customEmojiScale) + val emojiSpan = DrawableEmojiSpan(emojiAdapter, emojiElement.emoji.url, emojiElement.emoji.aspectRatio) spannableString.setSpan(emojiSpan, skippedEmoji.start, skippedEmoji.end, 0) + spannableString.setSpan(RelativeSizeSpan(customEmojiScale), skippedEmoji.start, skippedEmoji.end, 0) val height = max(textView.textSize * 0.75f, 10f) val width = when(val aspectRatio = emojiElement.emoji.aspectRatio) { null -> height diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index 2d29ccceb7..32e9d24dd2 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -365,6 +365,27 @@ class SettingAppearanceActivity : AppCompatActivity() { ) + Text( + "ノート本文中のカスタム絵文字のスケール", + modifier = Modifier.padding(horizontal = 16.dp) + ) + Text( + stringResource(id = R.string.settings_app_restart_required), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colors.error, + fontSize = 14.sp + ) + Slider( + value = currentConfigState.noteCustomEmojiScaleSizeInText, + valueRange = 0.5f..2f, + onValueChange = { + currentConfigState = + currentConfigState.copy(noteCustomEmojiScaleSizeInText = it) + }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + + SettingSwitchTile( checked = currentConfigState.isEnableNoteDivider, onChanged = { From f96c6cb0e22402191937b31d0582316a671a0fac Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 01:30:07 +0900 Subject: [PATCH 346/432] fix: test --- .../milktea/data/infrastructure/settings/ConfigKtTest.kt | 4 ++++ .../milktea/data/infrastructure/settings/KeysKtTest.kt | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt index db07060cbd..6d3dafbae5 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/ConfigKtTest.kt @@ -155,6 +155,10 @@ class ConfigKtTest { config.noteReactionCounterFontSize, (u as PrefType.FloatPref).value ) + Keys.NoteCustomEmojiScaleSizeInText -> Assertions.assertEquals( + config.noteCustomEmojiScaleSizeInText, + (u as PrefType.FloatPref).value + ) } } } diff --git a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt index 6eaa1475f3..f6e3e1bd6a 100644 --- a/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt +++ b/modules/data/src/test/java/net/pantasystem/milktea/data/infrastructure/settings/KeysKtTest.kt @@ -116,6 +116,10 @@ class KeysKtTest { "NoteReactionCounterFontSize", key.str() ) + Keys.NoteCustomEmojiScaleSizeInText -> Assertions.assertEquals( + "NoteCustomEmojiScaleSizeInText", + key.str() + ) } } } @@ -123,8 +127,8 @@ class KeysKtTest { @Test fun checkAllKeysCount() { - Assertions.assertEquals(31, Keys.allKeys.size) - Assertions.assertEquals(31, Keys.allKeys.map { it.str() }.toSet().size) + Assertions.assertEquals(32, Keys.allKeys.size) + Assertions.assertEquals(32, Keys.allKeys.map { it.str() }.toSet().size) } From 8fd9849ce55e71126cc27870964a33efb9be0679 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 01:37:26 +0900 Subject: [PATCH 347/432] =?UTF-8?q?refactor:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=81=A8=E3=81=97=E3=81=A6?= =?UTF-8?q?=E5=88=87=E3=82=8A=E5=87=BA=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/res/values-ja/strings.xml | 1 + .../src/main/res/values-zh/strings.xml | 1 + .../src/main/res/values/strings.xml | 1 + .../activities/SettingAppearanceActivity.kt | 186 ++++++++++-------- 4 files changed, 103 insertions(+), 86 deletions(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 8b41d93955..990ffd18de 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -595,6 +595,7 @@ ノートコンテンツ文字サイズ(%fsp) ノートヘッダー文字サイズ(%fsp) ノートリアクション件数表示の絵文字および文字サイズ(%fsp, %fps) + ノートの本文中のカスタム絵文字の倍率(%f倍) おすすめユーザ %d人が投稿 引用として添付しますか? diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 8600e9fb8d..5346eef743 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -589,6 +589,7 @@ Note content font size(%fsp) Note header font size(%fsp) Note reaction counter font size(%fsp, %fps) + Magnification for custom emoji in text(%fX) Suggestions %d people posting Attach as a quote post? diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index dd3c402af9..9e5970c567 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -571,6 +571,7 @@ Note content font size(%fsp) Note header font size(%fsp) Note reaction counter font size(%fsp, %fsp) + Magnification for custom emoji in text(%fX) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt index 32e9d24dd2..cf7e047eb4 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingAppearanceActivity.kt @@ -294,96 +294,110 @@ class SettingAppearanceActivity : AppCompatActivity() { modifier = Modifier.padding(horizontal = 16.dp) ) - Text( - stringResource( - id = R.string.settings_note_header_font_size, - currentConfigState.noteHeaderFontSize - ), - modifier = Modifier.padding(horizontal = 16.dp) - ) - Text( - stringResource(id = R.string.settings_app_restart_required), - modifier = Modifier.padding(horizontal = 16.dp), - color = MaterialTheme.colors.error, - fontSize = 14.sp - ) - Slider( - value = currentConfigState.noteHeaderFontSize, - valueRange = 10f..24f, - onValueChange = { - currentConfigState = - currentConfigState.copy(noteHeaderFontSize = it) - }, - modifier = Modifier.padding(horizontal = 16.dp) - ) + Column(Modifier.fillMaxWidth()) { + Text( + stringResource( + id = R.string.settings_note_header_font_size, + currentConfigState.noteHeaderFontSize + ), + modifier = Modifier.padding(horizontal = 16.dp) + ) + Text( + stringResource(id = R.string.settings_app_restart_required), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colors.error, + fontSize = 14.sp + ) + Slider( + value = currentConfigState.noteHeaderFontSize, + valueRange = 10f..24f, + onValueChange = { + currentConfigState = + currentConfigState.copy(noteHeaderFontSize = it) + }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + } - Text( - stringResource( - id = R.string.settings_note_content_font_size, - currentConfigState.noteContentFontSize - ), - modifier = Modifier.padding(horizontal = 16.dp) - ) - Text( - stringResource(id = R.string.settings_app_restart_required), - modifier = Modifier.padding(horizontal = 16.dp), - color = MaterialTheme.colors.error, - fontSize = 14.sp - ) - Slider( - value = currentConfigState.noteContentFontSize, - valueRange = 10f..24f, - onValueChange = { - currentConfigState = - currentConfigState.copy(noteContentFontSize = it) - }, - modifier = Modifier.padding(horizontal = 16.dp) - ) + Column(Modifier.fillMaxWidth()) { + Text( + stringResource( + id = R.string.settings_note_content_font_size, + currentConfigState.noteContentFontSize + ), + modifier = Modifier.padding(horizontal = 16.dp) + ) + Text( + stringResource(id = R.string.settings_app_restart_required), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colors.error, + fontSize = 14.sp + ) + Slider( + value = currentConfigState.noteContentFontSize, + valueRange = 10f..24f, + onValueChange = { + currentConfigState = + currentConfigState.copy(noteContentFontSize = it) + }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + } - Text( - stringResource( - id = R.string.settings_note_reaction_counter_font_size, - currentConfigState.noteReactionCounterFontSize * 1.2f, - currentConfigState.noteReactionCounterFontSize - ), - modifier = Modifier.padding(horizontal = 16.dp) - ) - Text( - stringResource(id = R.string.settings_app_restart_required), - modifier = Modifier.padding(horizontal = 16.dp), - color = MaterialTheme.colors.error, - fontSize = 14.sp - ) - Slider( - value = currentConfigState.noteReactionCounterFontSize, - valueRange = 10f..24f, - onValueChange = { - currentConfigState = - currentConfigState.copy(noteReactionCounterFontSize = it) - }, - modifier = Modifier.padding(horizontal = 16.dp) - ) + + Column(Modifier.fillMaxWidth()) { + Text( + stringResource( + id = R.string.settings_note_reaction_counter_font_size, + currentConfigState.noteReactionCounterFontSize * 1.2f, + currentConfigState.noteReactionCounterFontSize + ), + modifier = Modifier.padding(horizontal = 16.dp) + ) + Text( + stringResource(id = R.string.settings_app_restart_required), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colors.error, + fontSize = 14.sp + ) + Slider( + value = currentConfigState.noteReactionCounterFontSize, + valueRange = 10f..24f, + onValueChange = { + currentConfigState = + currentConfigState.copy(noteReactionCounterFontSize = it) + }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + + } + + Column(Modifier.fillMaxWidth()) { + Text( + stringResource( + id = R.string.settings_note_custom_emoji_scale_size_in_text, + configState.noteCustomEmojiScaleSizeInText, + ), + modifier = Modifier.padding(horizontal = 16.dp) + ) + Text( + stringResource(id = R.string.settings_app_restart_required), + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colors.error, + fontSize = 14.sp + ) + Slider( + value = currentConfigState.noteCustomEmojiScaleSizeInText, + valueRange = 0.5f..2f, + onValueChange = { + currentConfigState = + currentConfigState.copy(noteCustomEmojiScaleSizeInText = it) + }, + modifier = Modifier.padding(horizontal = 16.dp) + ) + } - Text( - "ノート本文中のカスタム絵文字のスケール", - modifier = Modifier.padding(horizontal = 16.dp) - ) - Text( - stringResource(id = R.string.settings_app_restart_required), - modifier = Modifier.padding(horizontal = 16.dp), - color = MaterialTheme.colors.error, - fontSize = 14.sp - ) - Slider( - value = currentConfigState.noteCustomEmojiScaleSizeInText, - valueRange = 0.5f..2f, - onValueChange = { - currentConfigState = - currentConfigState.copy(noteCustomEmojiScaleSizeInText = it) - }, - modifier = Modifier.padding(horizontal = 16.dp) - ) SettingSwitchTile( From b4a328faccb25569ea4ee865af9d566d534a7e1a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 03:41:40 +0900 Subject: [PATCH 348/432] feat --- .../pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt index 1713c5384e..ff6fc878f7 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/NoteReactionViewHelper.kt @@ -52,7 +52,7 @@ object NoteReactionViewHelper { imageAspectRatio ) reactionImageTypeView.applySizeByAspectRatio( - baseHeightPx, + baseHeightPx * 1.2f, imageAspectRatio ) From 49dc6aff63694be03d3e08e01c9e5a6f756656db Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sat, 17 Jun 2023 23:29:48 +0900 Subject: [PATCH 349/432] =?UTF-8?q?feat:=20=E4=B8=8B=E3=82=B9=E3=83=AF?= =?UTF-8?q?=E3=82=A4=E3=83=97=E3=81=A7=E3=82=82=E7=94=BB=E5=83=8F=E3=83=93?= =?UTF-8?q?=E3=83=A5=E3=83=BC=E3=82=A2=E3=83=BC=E3=82=92=E4=BF=AE=E4=BA=86?= =?UTF-8?q?=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97?= =?UTF-8?q?=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/net/pantasystem/milktea/media/SwipeFinishLayout.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/features/media/src/main/java/net/pantasystem/milktea/media/SwipeFinishLayout.kt b/modules/features/media/src/main/java/net/pantasystem/milktea/media/SwipeFinishLayout.kt index 0012b19b66..5193e77518 100644 --- a/modules/features/media/src/main/java/net/pantasystem/milktea/media/SwipeFinishLayout.kt +++ b/modules/features/media/src/main/java/net/pantasystem/milktea/media/SwipeFinishLayout.kt @@ -92,7 +92,7 @@ class SwipeFinishLayout : FrameLayout { try { val diffY = e2.y.minus(e1.y) if (abs(diffY) > SWIPE_THRESHOLD && abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { - if (diffY <= 0) { + if (abs(diffY) >= 0) { onSwipeTop() result = true } From 82cfc81c0d2dc68668759d8c7b9f551ead9b472a Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 18 Jun 2023 14:00:50 +0900 Subject: [PATCH 350/432] =?UTF-8?q?feat:=20=E5=A4=96=E9=83=A8=E3=81=8B?= =?UTF-8?q?=E3=82=89=E3=83=99=E3=83=BC=E3=82=B9=E3=81=A8=E3=81=AA=E3=82=8B?= =?UTF-8?q?=E7=B5=B5=E6=96=87=E5=AD=97=E3=81=AE=E3=82=B5=E3=82=A4=E3=82=BA?= =?UTF-8?q?=E3=82=92=E6=8C=87=E5=AE=9A=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/reaction/choices/EmojiListItemsAdapter.kt | 12 +++++++++--- .../note/src/main/res/layout/item_emoji_choice.xml | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt index 75c476ab63..a200f20fed 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt @@ -24,6 +24,7 @@ class EmojiListItemsAdapter( private val isApplyImageAspectRatio: Boolean, private val onEmojiSelected: (EmojiType) -> Unit, private val onEmojiLongClicked: (EmojiType) -> Boolean, + private val baseItemSizeDp: Int = 28, ) : ListAdapter( DiffUtilItemCallback() ) { @@ -53,7 +54,11 @@ class EmojiListItemsAdapter( } sealed class VH(view: View) : RecyclerView.ViewHolder(view) - class EmojiVH(val binding: ItemEmojiChoiceBinding, private val isApplyImageAspectRatio: Boolean) : VH(binding.root) { + class EmojiVH( + val binding: ItemEmojiChoiceBinding, + private val isApplyImageAspectRatio: Boolean, + private val baseItemSizeDp: Int, + ) : VH(binding.root) { fun onBind( item: EmojiType, onEmojiSelected: (EmojiType) -> Unit, @@ -63,7 +68,7 @@ class EmojiListItemsAdapter( is EmojiType.CustomEmoji -> { if (isApplyImageAspectRatio) { binding.reactionImagePreview.applySizeByAspectRatio( - 28, + baseItemSizeDp, item.emoji.aspectRatio ?: ImageAspectRatioCache.get( item.emoji.url ?: item.emoji.uri ) @@ -136,7 +141,8 @@ class EmojiListItemsAdapter( ) return EmojiVH( binding, - isApplyImageAspectRatio + isApplyImageAspectRatio, + baseItemSizeDp ) } } diff --git a/modules/features/note/src/main/res/layout/item_emoji_choice.xml b/modules/features/note/src/main/res/layout/item_emoji_choice.xml index 4a7d97314f..b09f959035 100644 --- a/modules/features/note/src/main/res/layout/item_emoji_choice.xml +++ b/modules/features/note/src/main/res/layout/item_emoji_choice.xml @@ -4,7 +4,7 @@ Date: Sun, 18 Jun 2023 14:04:13 +0900 Subject: [PATCH 351/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E3=82=B5?= =?UTF-8?q?=E3=82=A4=E3=82=BA=E3=82=82=E5=90=88=E3=82=8F=E3=81=9B=E3=81=A6?= =?UTF-8?q?=E5=A4=89=E6=9B=B4=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/note/reaction/choices/EmojiListItemsAdapter.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt index a200f20fed..0985b6c0d7 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/reaction/choices/EmojiListItemsAdapter.kt @@ -1,5 +1,6 @@ package net.pantasystem.milktea.note.reaction.choices +import android.util.TypedValue import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -90,12 +91,14 @@ class EmojiListItemsAdapter( binding.reactionImagePreview.setMemoVisibility(View.VISIBLE) } is EmojiType.Legacy -> { + binding.reactionStringPreview.setTextSize(TypedValue.COMPLEX_UNIT_DIP, baseItemSizeDp * 0.8f) binding.reactionImagePreview.setMemoVisibility(View.GONE) binding.reactionStringPreview.setMemoVisibility(View.VISIBLE) binding.reactionStringPreview.text = requireNotNull(LegacyReaction.reactionMap[item.type]) } is EmojiType.UtfEmoji -> { + binding.reactionStringPreview.setTextSize(TypedValue.COMPLEX_UNIT_DIP, baseItemSizeDp * 0.8f) binding.reactionStringPreview.setMemoVisibility(View.VISIBLE) binding.reactionImagePreview.setMemoVisibility(View.GONE) binding.reactionStringPreview.text = item.code From 89f6c626464b989f0442f085920b6178ec2e0369 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 18 Jun 2023 14:13:24 +0900 Subject: [PATCH 352/432] =?UTF-8?q?feat:=20=E3=83=AA=E3=82=A2=E3=82=AF?= =?UTF-8?q?=E3=82=B7=E3=83=A7=E3=83=B3=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC?= =?UTF-8?q?=E3=81=A7=E3=81=AF=E3=81=AA=E3=81=8F=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=81=A8=E8=A1=A8=E8=A8=98?= =?UTF-8?q?=E3=81=99=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 2 ++ modules/common_resource/src/main/res/values/strings.xml | 2 +- .../pantasystem/milktea/setting/activities/SettingsActivity.kt | 2 +- .../setting/src/main/res/layout/activity_reaction_setting.xml | 2 +- 5 files changed, 6 insertions(+), 3 deletions(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 990ffd18de..31634c7154 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -575,6 +575,7 @@ ファイルが添付されたノートのみ表示する Milkteaについて ソースコード(GitHub) + 絵文字ピッカー diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 5346eef743..92161f8ee9 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -568,6 +568,8 @@ Only Media 关于Milktea Source code(GitHub) + Emoji picker + diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 9e5970c567..c747ad1e12 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -572,7 +572,7 @@ Note header font size(%fsp) Note reaction counter font size(%fsp, %fsp) Magnification for custom emoji in text(%fX) - + Emoji picker diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingsActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingsActivity.kt index b32ce0dbab..45875d299c 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingsActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/SettingsActivity.kt @@ -151,7 +151,7 @@ class SettingsActivity : AppCompatActivity() { ) } ) { - Text(stringResource(id = R.string.reaction)) + Text(stringResource(id = R.string.settings_emoji_picker)) } SettingListTileLayout( diff --git a/modules/features/setting/src/main/res/layout/activity_reaction_setting.xml b/modules/features/setting/src/main/res/layout/activity_reaction_setting.xml index 5301f1e2c6..ab7f21efd7 100644 --- a/modules/features/setting/src/main/res/layout/activity_reaction_setting.xml +++ b/modules/features/setting/src/main/res/layout/activity_reaction_setting.xml @@ -20,7 +20,7 @@ app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" - app:title="@string/reaction_picker"/> + app:title="@string/settings_emoji_picker"/> Date: Sun, 18 Jun 2023 14:27:01 +0900 Subject: [PATCH 353/432] =?UTF-8?q?feat:=20=E6=96=87=E5=AD=97=E5=88=97?= =?UTF-8?q?=E3=83=AA=E3=82=BD=E3=83=BC=E3=82=B9=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/common_resource/src/main/res/values-ja/strings.xml | 1 + modules/common_resource/src/main/res/values-zh/strings.xml | 2 +- modules/common_resource/src/main/res/values/strings.xml | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/common_resource/src/main/res/values-ja/strings.xml b/modules/common_resource/src/main/res/values-ja/strings.xml index 31634c7154..12683f46b1 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -576,6 +576,7 @@ Milkteaについて ソースコード(GitHub) 絵文字ピッカー + 絵文字の表示サイズ diff --git a/modules/common_resource/src/main/res/values-zh/strings.xml b/modules/common_resource/src/main/res/values-zh/strings.xml index 92161f8ee9..00504f8da5 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -569,7 +569,7 @@ 关于Milktea Source code(GitHub) Emoji picker - + Emoji display size diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index c747ad1e12..b7b3fa759b 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -573,6 +573,7 @@ Note reaction counter font size(%fsp, %fsp) Magnification for custom emoji in text(%fX) Emoji picker + Emoji display size From 86fa634e9e29f027186e63921547cacc96bed746 Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 18 Jun 2023 14:39:02 +0900 Subject: [PATCH 354/432] =?UTF-8?q?feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=81=AE=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=81=AE=E3=82=B5=E3=82=A4=E3=82=BA=E3=82=92=E4=BF=9D?= =?UTF-8?q?=E6=8C=81=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88=E3=81=86=E3=81=AB?= =?UTF-8?q?=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../milktea/data/infrastructure/settings/Config.kt | 6 ++++++ .../java/net/pantasystem/milktea/model/setting/Config.kt | 3 +++ .../main/java/net/pantasystem/milktea/model/setting/Keys.kt | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt index 23fa1c1d9d..c23dffebd9 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/settings/Config.kt @@ -136,6 +136,9 @@ fun Config.Companion.from(map: Map): Config { noteCustomEmojiScaleSizeInText = map.getValue( Keys.NoteCustomEmojiScaleSizeInText )?.value ?: DefaultConfig.config.noteCustomEmojiScaleSizeInText, + emojiPickerEmojiDisplaySize = map.getValue( + Keys.EmojiPickerEmojiDisplaySize + )?.value ?: DefaultConfig.config.emojiPickerEmojiDisplaySize, ) } @@ -247,6 +250,9 @@ fun Config.pref(key: Keys): PrefType { Keys.NoteCustomEmojiScaleSizeInText -> { PrefType.FloatPref(noteCustomEmojiScaleSizeInText) } + Keys.EmojiPickerEmojiDisplaySize -> { + PrefType.IntPref(emojiPickerEmojiDisplaySize) + } } } diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt index 4b09060efe..6c2c8da31f 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Config.kt @@ -53,6 +53,7 @@ data class IsAnalyticsCollectionEnabled( * @param noteHeaderFontSize ノートのヘッダー部分のテキストサイズ * @param noteContentFontSize ノートのコンテンツ部分のテキストサイズ * @param noteReactionCounterFontSize ノートのリアクションカウンターのカスタム絵文字、絵文字と件数表示のフォントサイズ + * @param emojiPickerEmojiDisplaySize 絵文字ピッカーの絵文字の表示サイズ */ data class Config( val isSimpleEditorEnabled: Boolean, @@ -85,6 +86,7 @@ data class Config( val isDisplayTimestampsAsAbsoluteDates: Boolean, val noteReactionCounterFontSize: Float, val noteCustomEmojiScaleSizeInText: Float, + val emojiPickerEmojiDisplaySize: Int, ) { companion object @@ -145,6 +147,7 @@ object DefaultConfig { isDisplayTimestampsAsAbsoluteDates = false, noteReactionCounterFontSize = 15f, noteCustomEmojiScaleSizeInText = 1.2f, + emojiPickerEmojiDisplaySize = 28, ) fun getRememberVisibilityConfig(accountId: Long): RememberVisibility.Remember { diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt index aacac1dcf1..46e0f72685 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/setting/Keys.kt @@ -34,6 +34,7 @@ val Keys.Companion.allKeys by lazy { Keys.IsDisplayTimestampsAsAbsoluteDates, Keys.NoteReactionCounterFontSize, Keys.NoteCustomEmojiScaleSizeInText, + Keys.EmojiPickerEmojiDisplaySize, ) } @@ -97,6 +98,8 @@ sealed interface Keys { object NoteCustomEmojiScaleSizeInText : Keys + object EmojiPickerEmojiDisplaySize : Keys + companion object } @@ -134,5 +137,6 @@ fun Keys.str(): String { is Keys.IsDisplayTimestampsAsAbsoluteDates -> "IsDisplayTimestampsAsAbsoluteDates" is Keys.NoteReactionCounterFontSize -> "NoteReactionCounterFontSize" is Keys.NoteCustomEmojiScaleSizeInText -> "NoteCustomEmojiScaleSizeInText" + is Keys.EmojiPickerEmojiDisplaySize -> "EmojiPickerEmojiDisplaySize" } } From fa33e17408dc6f002f16fa0d98acc8735f63df4e Mon Sep 17 00:00:00 2001 From: Yuichiro Kinoshita Date: Sun, 18 Jun 2023 14:47:55 +0900 Subject: [PATCH 355/432] =?UTF-8?q?feat:=20=E7=B5=B5=E6=96=87=E5=AD=97?= =?UTF-8?q?=E3=83=94=E3=83=83=E3=82=AB=E3=83=BC=E3=81=AE=E7=B5=B5=E6=96=87?= =?UTF-8?q?=E5=AD=97=E3=81=AE=E8=A1=A8=E7=A4=BA=E3=82=B5=E3=82=A4=E3=82=BA?= =?UTF-8?q?=E3=82=92=E5=A4=89=E6=9B=B4=E3=81=A7=E3=81=8D=E3=82=8B=E3=82=88?= =?UTF-8?q?=E3=81=86=E3=81=AB=E3=81=97=E3=81=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../activities/ReactionSettingActivity.kt | 92 +++++++++++++------ .../ReactionPickerSettingViewModel.kt | 28 +++++- .../res/layout/activity_reaction_setting.xml | 18 +++- 3 files changed, 105 insertions(+), 33 deletions(-) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/ReactionSettingActivity.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/ReactionSettingActivity.kt index ed2f054a6d..6ec1af6236 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/ReactionSettingActivity.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/activities/ReactionSettingActivity.kt @@ -6,6 +6,7 @@ import android.view.KeyEvent import android.view.MenuItem import android.view.View import android.widget.AdapterView +import android.widget.ArrayAdapter import androidx.activity.viewModels import androidx.appcompat.app.AppCompatActivity import androidx.databinding.DataBindingUtil @@ -37,9 +38,11 @@ class ReactionSettingActivity : AppCompatActivity() { val mReactionPickerSettingViewModel: ReactionPickerSettingViewModel by viewModels() - @Inject lateinit var metaRepository: MetaRepository + @Inject + lateinit var metaRepository: MetaRepository - @Inject lateinit var accountStore: AccountStore + @Inject + lateinit var accountStore: AccountStore @Inject lateinit var applyTheme: ApplyTheme @@ -48,7 +51,8 @@ class ReactionSettingActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) applyTheme() - val binding = DataBindingUtil.setContentView(this, + val binding = DataBindingUtil.setContentView( + this, R.layout.activity_reaction_setting ) binding.lifecycleOwner = this @@ -68,8 +72,8 @@ class ReactionSettingActivity : AppCompatActivity() { binding.reactionSettingListView.addItemDecoration(touchHelper) binding.reactionPickerSettingViewModel = mReactionPickerSettingViewModel val reactionsAdapter = ReactionChoicesAdapter( - mReactionPickerSettingViewModel - ) + mReactionPickerSettingViewModel + ) binding.reactionSettingListView.adapter = reactionsAdapter mReactionPickerSettingViewModel.reactionSettingsList.observe(this) { list -> reactionsAdapter.submitList(list.map { rus -> @@ -90,9 +94,9 @@ class ReactionSettingActivity : AppCompatActivity() { it?.emojis }.onEach { emojis -> val reactionAutoCompleteArrayAdapter = ReactionAutoCompleteArrayAdapter( - emojis, - this - ) + emojis, + this + ) binding.reactionSettingField.setAdapter(reactionAutoCompleteArrayAdapter) binding.reactionSettingField.setOnItemClickListener { _, _, position, _ -> val emoji = reactionAutoCompleteArrayAdapter.suggestions[position] @@ -103,9 +107,9 @@ class ReactionSettingActivity : AppCompatActivity() { binding.reactionSettingField.setOnEditorActionListener { textView, _, keyEvent -> val text = textView.text - if(keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER && text != null){ - if(keyEvent.action == KeyEvent.ACTION_UP){ - if(text.isNotBlank()){ + if (keyEvent?.keyCode == KeyEvent.KEYCODE_ENTER && text != null) { + if (keyEvent.action == KeyEvent.ACTION_UP) { + if (text.isNotBlank()) { mReactionPickerSettingViewModel.addReaction(text.toString()) binding.reactionSettingField.setText("") @@ -117,19 +121,21 @@ class ReactionSettingActivity : AppCompatActivity() { } - binding.reactionPickerType.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{ - override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { - val pickerType = when(p2){ - 0 -> ReactionPickerType.LIST - 1 -> ReactionPickerType.SIMPLE - else -> throw IllegalArgumentException("error") + binding.reactionPickerType.onItemSelectedListener = + object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) { + val pickerType = when (p2) { + 0 -> ReactionPickerType.LIST + 1 -> ReactionPickerType.SIMPLE + else -> throw IllegalArgumentException("error") + } + mReactionPickerSettingViewModel.setReactionPickerType(pickerType) } - mReactionPickerSettingViewModel.setReactionPickerType(pickerType) - } - override fun onNothingSelected(p0: AdapterView<*>?) { + override fun onNothingSelected(p0: AdapterView<*>?) { + + } } - } binding.importReactionFromWebButton.setOnClickListener { @@ -139,23 +145,51 @@ class ReactionSettingActivity : AppCompatActivity() { finish() } + val emojiSizes = (18..48).toList() + val emojiSizeSelection = emojiSizes.map { + "${it}dp" + } + + binding.emojiDisplaySizeSelection.adapter = ArrayAdapter( + this, + android.R.layout.simple_spinner_dropdown_item, + emojiSizeSelection, + ) + binding.emojiDisplaySizeSelection.setSelection(mReactionPickerSettingViewModel.config.value.emojiPickerEmojiDisplaySize - 18) + binding.emojiDisplaySizeSelection.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected( + parent: AdapterView<*>?, + view: View?, + position: Int, + id: Long, + ) { + mReactionPickerSettingViewModel.onEmojiSizeSelected(emojiSizes[position]) + } + + override fun onNothingSelected(parent: AdapterView<*>?) { + + } + } } - override fun onStop(){ + override fun onStop() { super.onStop() mReactionPickerSettingViewModel.save() } - inner class ItemTouchCallback : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT, ItemTouchHelper.ACTION_STATE_IDLE){ + inner class ItemTouchCallback : ItemTouchHelper.SimpleCallback( + ItemTouchHelper.UP or ItemTouchHelper.DOWN or ItemTouchHelper.RIGHT or ItemTouchHelper.LEFT, + ItemTouchHelper.ACTION_STATE_IDLE + ) { override fun onMove( recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, - target: RecyclerView.ViewHolder + target: RecyclerView.ViewHolder, ): Boolean { val from = viewHolder.absoluteAdapterPosition val to = target.absoluteAdapterPosition - val exList = mReactionPickerSettingViewModel.reactionSettingsList.value?: emptyList() + val exList = mReactionPickerSettingViewModel.reactionSettingsList.value ?: emptyList() val list = ArrayList(exList) val d = list.removeAt(from) list.add(to, d) @@ -169,21 +203,21 @@ class ReactionSettingActivity : AppCompatActivity() { } - private fun showConfirmDeleteReactionDialog(reaction: String){ + private fun showConfirmDeleteReactionDialog(reaction: String) { MaterialAlertDialogBuilder(this) .setTitle(getString(R.string.confirm_delete_reaction)) .setMessage(getString(R.string.delete_reaction) + " $reaction") - .setNegativeButton(android.R.string.cancel) { _, _-> + .setNegativeButton(android.R.string.cancel) { _, _ -> } - .setPositiveButton(android.R.string.ok){ _, _ -> + .setPositiveButton(android.R.string.ok) { _, _ -> mReactionPickerSettingViewModel.deleteReaction(reaction) } .show() } override fun onOptionsItemSelected(item: MenuItem): Boolean { - when(item.itemId){ + when (item.itemId) { android.R.id.home -> finish() } return super.onOptionsItemSelected(item) diff --git a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/reaction/ReactionPickerSettingViewModel.kt b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/reaction/ReactionPickerSettingViewModel.kt index ba9f9f1329..e17be319e5 100644 --- a/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/reaction/ReactionPickerSettingViewModel.kt +++ b/modules/features/setting/src/main/java/net/pantasystem/milktea/setting/viewmodel/reaction/ReactionPickerSettingViewModel.kt @@ -6,16 +6,20 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import net.pantasystem.milktea.app_store.account.AccountStore import net.pantasystem.milktea.app_store.setting.SettingStore import net.pantasystem.milktea.common_android.eventbus.EventBus +import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.usercustom.ReactionUserSetting +import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.usercustom.ReactionUserSettingDao import net.pantasystem.milktea.model.account.Account import net.pantasystem.milktea.model.notes.reaction.LegacyReaction import net.pantasystem.milktea.model.notes.reaction.ReactionSelection -import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.usercustom.ReactionUserSetting -import net.pantasystem.milktea.data.infrastructure.notes.reaction.impl.usercustom.ReactionUserSettingDao +import net.pantasystem.milktea.model.setting.DefaultConfig +import net.pantasystem.milktea.model.setting.LocalConfigRepository import net.pantasystem.milktea.model.setting.ReactionPickerType import javax.inject.Inject @@ -24,6 +28,7 @@ class ReactionPickerSettingViewModel @Inject constructor( private val reactionUserSettingDao: ReactionUserSettingDao, private val settingStore: SettingStore, val accountStore: AccountStore, + private val configRepository: LocalConfigRepository, ) : ViewModel(), ReactionSelection { @@ -37,6 +42,12 @@ class ReactionPickerSettingViewModel @Inject constructor( private var mExistingSettingList: List? = null private val mReactionSettingReactionNameMap = LinkedHashMap() + val config = configRepository.observe().stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + configRepository.get().getOrNull() ?: DefaultConfig.config + ) + init { viewModelScope.launch(Dispatchers.IO) { accountStore.observeCurrentAccount.filterNotNull().collect { @@ -136,10 +147,21 @@ class ReactionPickerSettingViewModel @Inject constructor( reactionPickerType = type } + fun onEmojiSizeSelected(size: Int) { + viewModelScope.launch { + val c = configRepository.get().getOrNull() ?: DefaultConfig.config + configRepository.save( + c.copy( + emojiPickerEmojiDisplaySize = size + ) + ) + } + } + private fun toReactionUserSettingFromTextTypeReaction( account: Account, index: Int, - reaction: String + reaction: String, ): ReactionUserSetting { return ReactionUserSetting( reaction, diff --git a/modules/features/setting/src/main/res/layout/activity_reaction_setting.xml b/modules/features/setting/src/main/res/layout/activity_reaction_setting.xml index ab7f21efd7..743f746be7 100644 --- a/modules/features/setting/src/main/res/layout/activity_reaction_setting.xml +++ b/modules/features/setting/src/main/res/layout/activity_reaction_setting.xml @@ -50,6 +50,19 @@ android:layout_marginBottom="16dp" android:entries="@array/reaction_picker_type"/> + + + + + /> + +