From 5439746ac29b316650d3d003619273119254c3da Mon Sep 17 00:00:00 2001 From: osamu <46447427+sam-osamu@users.noreply.github.com> Date: Sun, 5 Nov 2023 13:55:07 +0900 Subject: [PATCH 1/6] =?UTF-8?q?=E3=83=81=E3=83=A3=E3=83=B3=E3=83=8D?= =?UTF-8?q?=E3=83=AB=E3=81=AE=E3=83=AA=E3=83=8E=E3=83=BC=E3=83=88=EF=BC=8F?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E3=83=AA=E3=83=8E=E3=83=BC=E3=83=88=E3=81=8A?= =?UTF-8?q?=E3=82=88=E3=81=B3=E3=83=AA=E3=83=8E=E3=83=BC=E3=83=88=E5=88=B6?= =?UTF-8?q?=E9=99=90=E3=81=AE=E3=81=82=E3=82=8B=E3=83=81=E3=83=A3=E3=83=B3?= =?UTF-8?q?=E3=83=8D=E3=83=AB=E3=81=AB=E5=AF=BE=E5=BF=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/misskey/v12/channel/ChannelDTOTest.kt | 2 +- .../model/channel/ChannelStateTest.kt | 3 +- .../api/misskey/v12/channel/channels.kt | 7 +++- .../src/main/res/values-ja/strings.xml | 2 ++ .../src/main/res/values-zh/strings.xml | 2 ++ .../src/main/res/values/strings.xml | 2 ++ .../note/impl/NoteApiAdapter.kt | 21 +++++++---- .../note/impl/NoteRepositoryImpl.kt | 4 +-- .../milktea/channel/ChannelCard.kt | 9 +++-- .../note/renote/RenoteBottomSheetDialog.kt | 18 +++++++--- .../milktea/note/renote/RenoteDialogLayout.kt | 36 +++++++++++++++---- .../milktea/note/renote/RenoteUiState.kt | 2 ++ .../note/renote/RenoteUiStateBuilder.kt | 14 ++++++-- .../milktea/note/renote/RenoteViewModel.kt | 21 ++++++++--- .../milktea/note/view/NoteActionHandler.kt | 18 +++++++--- .../milktea/note/viewmodel/NotesViewModel.kt | 6 ++-- .../milktea/note/viewmodel/QuoteRenoteData.kt | 8 +++++ .../milktea/model/channel/Channel.kt | 1 + .../milktea/model/note/NoteRepository.kt | 2 +- .../CreateRenoteMultipleAccountUseCase.kt | 15 +++++--- 20 files changed, 147 insertions(+), 46 deletions(-) create mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/QuoteRenoteData.kt diff --git a/app/src/test/java/jp/panta/misskeyandroidclient/api/misskey/v12/channel/ChannelDTOTest.kt b/app/src/test/java/jp/panta/misskeyandroidclient/api/misskey/v12/channel/ChannelDTOTest.kt index e2edec3c85..d31b66ec0c 100644 --- a/app/src/test/java/jp/panta/misskeyandroidclient/api/misskey/v12/channel/ChannelDTOTest.kt +++ b/app/src/test/java/jp/panta/misskeyandroidclient/api/misskey/v12/channel/ChannelDTOTest.kt @@ -49,7 +49,7 @@ class ChannelDTOTest { assertEquals(channelDTO.isFollowing, channel.isFollowing) assertEquals(channelDTO.hasUnreadNote, channel.hasUnreadNote) assertEquals(channelDTO.name, channel.name) - + assertEquals(channelDTO.allowRenoteToExternal, channel.allowRenoteToExternal) } @Test diff --git a/app/src/test/java/jp/panta/misskeyandroidclient/model/channel/ChannelStateTest.kt b/app/src/test/java/jp/panta/misskeyandroidclient/model/channel/ChannelStateTest.kt index a9c20e955a..a063ccc934 100644 --- a/app/src/test/java/jp/panta/misskeyandroidclient/model/channel/ChannelStateTest.kt +++ b/app/src/test/java/jp/panta/misskeyandroidclient/model/channel/ChannelStateTest.kt @@ -88,7 +88,8 @@ class ChannelStateTest { lastNotedAt = null, notesCount = 0, usersCount = 0, - userId = User.Id(id.accountId, "userId") + userId = User.Id(id.accountId, "userId"), + allowRenoteToExternal = true, ) } diff --git a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/v12/channel/channels.kt b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/v12/channel/channels.kt index 3572eeb79a..6c89af1c1b 100644 --- a/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/v12/channel/channels.kt +++ b/modules/api/src/main/java/net/pantasystem/milktea/api/misskey/v12/channel/channels.kt @@ -46,6 +46,10 @@ data class ChannelDTO( @SerialName("isFollowing") val isFollowing: Boolean? = null, + + // この値が含まれていなかった時は合わせてtrueにする(本体側のDB定義側はデフォルト値がtrueになっている) + @SerialName("allowRenoteToExternal") + val allowRenoteToExternal: Boolean = true ) { fun toModel(account: Account): Channel { return Channel( @@ -59,7 +63,8 @@ data class ChannelDTO( usersCount = usersCount, userId = userId?.let { User.Id(account.accountId, it) }, hasUnreadNote = hasUnreadNote, - isFollowing = isFollowing + isFollowing = isFollowing, + allowRenoteToExternal = allowRenoteToExternal, ) } } 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 ff1e57ee45..893f1faad8 100644 --- a/modules/common_resource/src/main/res/values-ja/strings.xml +++ b/modules/common_resource/src/main/res/values-ja/strings.xml @@ -23,7 +23,9 @@ フォロー中 フォロワー リポスト + チャンネル内にリポスト 引用 + チャンネル内に引用 discovery リポストしました 絵文字/カスタム絵文字を入力 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 355b17b9da..d19a2a42b9 100644 --- a/modules/common_resource/src/main/res/values-zh/strings.xml +++ b/modules/common_resource/src/main/res/values-zh/strings.xml @@ -23,7 +23,9 @@ 关注中 关注者 转发 + 频道内转发 引用转发 + 频道内引用转发 发现 转发 自定义表情 diff --git a/modules/common_resource/src/main/res/values/strings.xml b/modules/common_resource/src/main/res/values/strings.xml index 3d189c82d9..c6cfbd68f6 100644 --- a/modules/common_resource/src/main/res/values/strings.xml +++ b/modules/common_resource/src/main/res/values/strings.xml @@ -23,7 +23,9 @@ Following Followers Renote + Renote in channel Quote renote + Quote renote in channel Discovery Renote Emoji diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt index 1053bd7110..6a85258da6 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt @@ -43,7 +43,7 @@ interface NoteApiAdapter { suspend fun delete(noteId: Note.Id): DeleteNoteResultType suspend fun createThreadMute(noteId: Note.Id): ToggleThreadMuteResultType suspend fun deleteThreadMute(noteId: Note.Id): ToggleThreadMuteResultType - suspend fun renote(target: Note): RenoteResultType + suspend fun renote(target: Note, inChannel: Boolean): RenoteResultType suspend fun vote(noteId: Note.Id, choice: Poll.Choice, target: Note) @@ -59,9 +59,9 @@ internal class NoteApiAdapterFactoryImpl @Inject constructor( private val uploader: FileUploaderProvider, private val filePropertyDataSource: FilePropertyDataSource, private val loggerFactory: Logger.Factory, -): NoteApiAdapter.Factory { +) : NoteApiAdapter.Factory { override suspend fun create(account: Account): NoteApiAdapter { - return when(account.instanceType) { + return when (account.instanceType) { Account.InstanceType.MISSKEY, Account.InstanceType.FIREFISH -> NoteApiAdapterMisskeyPattern( accountRepository = accountRepository, misskeyAPIProvider = misskeyAPIProvider, @@ -69,6 +69,7 @@ internal class NoteApiAdapterFactoryImpl @Inject constructor( uploader = uploader, loggerFactory = loggerFactory, ) + Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> NoteApiAdapterMastodonPattern( uploader = uploader, mastodonAPIProvider = mastodonAPIProvider, @@ -77,6 +78,7 @@ internal class NoteApiAdapterFactoryImpl @Inject constructor( } } } + private class NoteApiAdapterMisskeyPattern( val accountRepository: AccountRepository, val misskeyAPIProvider: MisskeyAPIProvider, @@ -150,11 +152,17 @@ private class NoteApiAdapterMisskeyPattern( return ToggleThreadMuteResultType.Misskey } - override suspend fun renote(target: Note): RenoteResultType { + override suspend fun renote(target: Note, inChannel: Boolean): RenoteResultType { val account = accountRepository.get(target.id.accountId).getOrThrow() return create( CreateNote( - author = account, renoteId = target.id, + author = account, + renoteId = target.id, + channelId = if (inChannel) { + target.channelId + } else { + null + }, text = null, visibility = target.visibility ) @@ -212,6 +220,7 @@ private class NoteApiAdapterMastodonPattern( uploader.get(createNote.author) .upload(UploadSource.LocalFile(appFile), false).id } + is AppFile.Remote -> appFile.id } } @@ -275,7 +284,7 @@ private class NoteApiAdapterMastodonPattern( return ToggleThreadMuteResultType.Mastodon(requireNotNull(body)) } - override suspend fun renote(target: Note): RenoteResultType { + override suspend fun renote(target: Note, inChannel: Boolean): RenoteResultType { val account = accountRepository.get(target.id.accountId).getOrThrow() val toot = mastodonAPIProvider.get(account).reblog(target.id.noteId) .throwIfHasError() diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteRepositoryImpl.kt index 61d9ede6ad..f095c8152e 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteRepositoryImpl.kt @@ -41,11 +41,11 @@ class NoteRepositoryImpl @Inject constructor( } } - override suspend fun renote(noteId: Note.Id): Result = runCancellableCatching { + override suspend fun renote(noteId: Note.Id, inChannel: Boolean): Result = runCancellableCatching { withContext(ioDispatcher) { val account = getAccount.get(noteId.accountId) val n = find(noteId).getOrThrow() - convertAndAdd(account, noteApiAdapterFactory.create(account).renote(n)) + convertAndAdd(account, noteApiAdapterFactory.create(account).renote(n, inChannel)) } } diff --git a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelCard.kt b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelCard.kt index 0a2a09ceae..acb1c61aad 100644 --- a/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelCard.kt +++ b/modules/features/channel/src/main/java/net/pantasystem/milktea/channel/ChannelCard.kt @@ -225,7 +225,8 @@ fun PreviewChannelCard() { userId = User.Id(0, "userId"), usersCount = 4, isFollowing = true, - hasUnreadNote = true + hasUnreadNote = true, + allowRenoteToExternal = true, ), isPaged = true, ) @@ -243,7 +244,8 @@ fun PreviewChannelCard() { userId = User.Id(0, "userId"), usersCount = 4, isFollowing = false, - hasUnreadNote = false + hasUnreadNote = false, + allowRenoteToExternal = true, ), isPaged = false ) } @@ -260,7 +262,8 @@ fun PreviewChannelCard() { userId = User.Id(0, "userId"), usersCount = 4, isFollowing = false, - hasUnreadNote = false + hasUnreadNote = false, + allowRenoteToExternal = true, ), isPaged = false ) } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteBottomSheetDialog.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteBottomSheetDialog.kt index 6908fe3a8a..d527c8bf81 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteBottomSheetDialog.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteBottomSheetDialog.kt @@ -65,7 +65,6 @@ class RenoteBottomSheetDialog : BottomSheetDialogFragment() { container: ViewGroup?, savedInstanceState: Bundle? ): View { - return ComposeView(requireContext()).apply { setContent { MilkteaStyleConfigApplyAndTheme(configRepository = configRepository) { @@ -81,14 +80,23 @@ class RenoteBottomSheetDialog : BottomSheetDialogFragment() { viewModel.renote() dismiss() }, + onQuoteRenoteButtonClicked = { + notesViewModel.showQuoteNoteEditor(noteId, false) + dismiss() + }, + onRenoteInChannelButtonClicked = { + viewModel.renote(inChannel = true) + dismiss() + }, + onQuoteInChannelRenoteButtonClicked = { + notesViewModel.showQuoteNoteEditor(noteId, true) + dismiss() + }, onDeleteRenoteButtonCLicked = { viewModel.unRenote() dismiss() }, - onQuoteRenoteButtonClicked = { - notesViewModel.showQuoteNoteEditor(noteId) - dismiss() - }) + ) } } } 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 1eea0c9c8e..888087cc11 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 @@ -19,6 +19,8 @@ fun RenoteDialogContent( onToggleAddAccount: (Long) -> Unit, onRenoteButtonClicked: () -> Unit, onQuoteRenoteButtonClicked: () -> Unit, + onRenoteInChannelButtonClicked: () -> Unit, + onQuoteInChannelRenoteButtonClicked: () -> Unit, onDeleteRenoteButtonCLicked: () -> Unit, ) { Surface( @@ -33,11 +35,14 @@ fun RenoteDialogContent( accounts = uiState.accounts, onClick = onToggleAddAccount ) - NormalBottomSheetDialogSelectionLayout( - onClick = onRenoteButtonClicked, - icon = Icons.Default.Repeat, - text = stringResource(id = R.string.renote) - ) + + if (uiState.isRenoteButtonVisible) { + NormalBottomSheetDialogSelectionLayout( + onClick = onRenoteButtonClicked, + icon = Icons.Default.Repeat, + text = stringResource(id = R.string.renote) + ) + } Spacer(modifier = Modifier.height(8.dp)) if (isRenotedByMe) { @@ -50,14 +55,31 @@ fun RenoteDialogContent( } - - if (uiState.canQuote) { + if (uiState.isRenoteButtonVisible && uiState.canQuote) { NormalBottomSheetDialogSelectionLayout( onClick = onQuoteRenoteButtonClicked, icon = Icons.Default.FormatQuote, text = stringResource(id = R.string.quote_renote) ) } + + if (uiState.isChannelRenoteButtonVisible) { + Spacer(modifier = Modifier.height(8.dp)) + NormalBottomSheetDialogSelectionLayout( + onClick = onRenoteInChannelButtonClicked, + icon = Icons.Default.Repeat, + text = stringResource(id = R.string.renote_in_channel) + ) + + if (uiState.canQuote) { + Spacer(modifier = Modifier.height(8.dp)) + NormalBottomSheetDialogSelectionLayout( + onClick = onQuoteInChannelRenoteButtonClicked, + icon = Icons.Default.FormatQuote, + text = stringResource(id = R.string.quote_renote_in_channel) + ) + } + } } } } \ No newline at end of file diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteUiState.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteUiState.kt index 5b8566db4d..6da61c864a 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteUiState.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteUiState.kt @@ -13,6 +13,8 @@ data class RenoteViewModelUiState( ), val accounts: List = emptyList(), val canQuote: Boolean = true, + val isRenoteButtonVisible: Boolean = true, + val isChannelRenoteButtonVisible: Boolean = false, ) data class RenoteViewModelTargetNoteState( diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteUiStateBuilder.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteUiStateBuilder.kt index e7f6bf6b71..96e0dc1ee8 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteUiStateBuilder.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteUiStateBuilder.kt @@ -10,8 +10,10 @@ import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import net.pantasystem.milktea.common.ResultState +import net.pantasystem.milktea.common.coroutines.combine import net.pantasystem.milktea.common.initialState import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.channel.Channel import net.pantasystem.milktea.model.instance.InstanceInfoService import net.pantasystem.milktea.model.instance.InstanceInfoType import net.pantasystem.milktea.model.note.Note @@ -30,6 +32,7 @@ class RenoteUiStateBuilder @Inject constructor( fun buildState( targetNoteIdFlow: StateFlow, noteFlow: Flow, + channelFlow: Flow, noteSyncState: Flow>, selectedAccountIds: Flow>, accountsFlow: Flow>, @@ -106,17 +109,24 @@ class RenoteUiStateBuilder @Inject constructor( emptyList() ) + val isRenoteButtonVisibleFlow = channelFlow.map { it?.allowRenoteToExternal ?: true } + val isChannelRenoteButtonVisibleFlow = channelFlow.map { it != null } + return combine( targetNoteIdFlow, noteState, accountWithUsers, currentAccountInstanceInfo, - ) { noteId, syncState, accounts, instanceInfo -> + isRenoteButtonVisibleFlow, + isChannelRenoteButtonVisibleFlow, + ) { noteId, syncState, accounts, instanceInfo, isRenoteButtonVisible, isChannelRenoteButtonVisible -> RenoteViewModelUiState( targetNoteId = noteId, noteState = syncState, accounts = accounts, - canQuote = instanceInfo?.canQuote ?: false + canQuote = instanceInfo?.canQuote ?: false, + isRenoteButtonVisible = isRenoteButtonVisible, + isChannelRenoteButtonVisible = isChannelRenoteButtonVisible, ) } } 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 c3dd531abc..f9f0b7cf8e 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 @@ -13,6 +13,7 @@ import net.pantasystem.milktea.common.ResultState import net.pantasystem.milktea.common.asLoadingStateFlow import net.pantasystem.milktea.common.initialState import net.pantasystem.milktea.model.account.AccountRepository +import net.pantasystem.milktea.model.channel.ChannelRepository import net.pantasystem.milktea.model.instance.InstanceInfoService import net.pantasystem.milktea.model.note.* import net.pantasystem.milktea.model.note.repost.CreateRenoteMultipleAccountUseCase @@ -25,6 +26,7 @@ class RenoteViewModel @Inject constructor( val noteRepository: NoteRepository, val accountRepository: AccountRepository, val userRepository: UserRepository, + private val channelRepository: ChannelRepository, val accountStore: AccountStore, val userDataSource: UserDataSource, val renoteUseCase: CreateRenoteMultipleAccountUseCase, @@ -38,7 +40,6 @@ class RenoteViewModel @Inject constructor( private var _targetNoteId = MutableStateFlow(null) - private val _resultEvents = MutableSharedFlow( onBufferOverflow = BufferOverflow.DROP_OLDEST, extraBufferCapacity = 20 @@ -58,6 +59,16 @@ class RenoteViewModel @Inject constructor( null ) + @OptIn(ExperimentalCoroutinesApi::class) + val channel = note.filterNotNull() + .mapNotNull { it.note.channelId } + .flatMapLatest { flowOf(channelRepository.findOne(it).getOrNull()) } + .stateIn( + viewModelScope, + SharingStarted.WhileSubscribed(5_000), + null + ) + private val _selectedAccountIds = MutableStateFlow>(emptyList()) private val accounts = accountStore.observeAccounts.stateIn( viewModelScope, @@ -90,6 +101,7 @@ class RenoteViewModel @Inject constructor( val uiState = renoteUiStateBuilder.buildState( targetNoteIdFlow = _targetNoteId, noteFlow = note, + channelFlow = channel, noteSyncState = _syncState, selectedAccountIds = _selectedAccountIds, accountsFlow = accounts, @@ -123,12 +135,11 @@ class RenoteViewModel @Inject constructor( } } - fun renote() { - val noteId = _targetNoteId.value - ?: return + fun renote(inChannel: Boolean = false) { + val noteId = _targetNoteId.value ?: return val accountIds = _selectedAccountIds.value viewModelScope.launch { - val result = renoteUseCase(noteId, accountIds) + val result = renoteUseCase(noteId, accountIds, inChannel) _resultEvents.tryEmit( RenoteActionResultEvent.Renote(result, noteId, accountIds) ) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteActionHandler.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteActionHandler.kt index 81c291f99a..6baefef934 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteActionHandler.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteActionHandler.kt @@ -31,10 +31,20 @@ class NoteActionHandler( }.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.RESUMED) .launchIn(lifecycleOwner.lifecycleScope) - notesViewModel.quoteRenoteTarget.onEach { - val intent = NoteEditorActivity.newBundle(context, quoteTo = it.id) - context.startActivity(intent) - }.flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.RESUMED) + notesViewModel.quoteRenoteTarget + .onEach { + val intent = NoteEditorActivity.newBundle( + context, + quoteTo = it.note.id, + channelId = if (it.isRenoteToChannel) { + it.note.channelId + } else { + null + } + ) + context.startActivity(intent) + } + .flowWithLifecycle(lifecycleOwner.lifecycle, Lifecycle.State.RESUMED) .launchIn(lifecycleOwner.lifecycleScope) notesViewModel.openNoteEditorEvent.filterNotNull().onEach { note -> diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt index ec4cea701b..caf99bad00 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt @@ -55,7 +55,7 @@ class NotesViewModel @Inject constructor( private val _statusMessage = MutableSharedFlow(onBufferOverflow = BufferOverflow.DROP_OLDEST, extraBufferCapacity = 10) val statusMessage = _statusMessage.asSharedFlow() - val quoteRenoteTarget = MutableSharedFlow(onBufferOverflow = BufferOverflow.DROP_OLDEST, extraBufferCapacity = 10) + val quoteRenoteTarget = MutableSharedFlow(onBufferOverflow = BufferOverflow.DROP_OLDEST, extraBufferCapacity = 10) val confirmDeletionEvent = MutableSharedFlow(onBufferOverflow = BufferOverflow.DROP_OLDEST, extraBufferCapacity = 10) @@ -66,11 +66,11 @@ class NotesViewModel @Inject constructor( private val _openNoteEditorEvent = MutableSharedFlow(onBufferOverflow = BufferOverflow.DROP_OLDEST, extraBufferCapacity = 10) val openNoteEditorEvent = _openNoteEditorEvent.asSharedFlow() - fun showQuoteNoteEditor(noteId: Note.Id) { + fun showQuoteNoteEditor(noteId: Note.Id, isRenoteToChannel: Boolean) { viewModelScope.launch { recursiveSearchHasContentNote(noteId).onSuccess { note -> withContext(Dispatchers.Main) { - quoteRenoteTarget.tryEmit(note) + quoteRenoteTarget.tryEmit(QuoteRenoteData(note, isRenoteToChannel)) } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/QuoteRenoteData.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/QuoteRenoteData.kt new file mode 100644 index 0000000000..236425bb1f --- /dev/null +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/QuoteRenoteData.kt @@ -0,0 +1,8 @@ +package net.pantasystem.milktea.note.viewmodel + +import net.pantasystem.milktea.model.note.Note + +data class QuoteRenoteData( + val note: Note, + val isRenoteToChannel: Boolean, +) \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/channel/Channel.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/channel/Channel.kt index 1d7f932a92..c53be3ab8d 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/channel/Channel.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/channel/Channel.kt @@ -21,6 +21,7 @@ data class Channel( val userId: User.Id?, val isFollowing: Boolean?, val hasUnreadNote: Boolean?, + val allowRenoteToExternal: Boolean, ) { companion object; diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/note/NoteRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/note/NoteRepository.kt index b023df767c..9f1dd78fa5 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/note/NoteRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/note/NoteRepository.kt @@ -9,7 +9,7 @@ interface NoteRepository { suspend fun create(createNote: CreateNote): Result - suspend fun renote(noteId: Note.Id): Result + suspend fun renote(noteId: Note.Id, inChannel: Boolean): Result suspend fun unrenote(noteId: Note.Id): Result diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenoteMultipleAccountUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenoteMultipleAccountUseCase.kt index 23bed7517d..1beabdc33b 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenoteMultipleAccountUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenoteMultipleAccountUseCase.kt @@ -21,6 +21,7 @@ class CreateRenoteMultipleAccountUseCase @Inject constructor( suspend operator fun invoke( noteId: Note.Id, accountIds: List, + inChannel: Boolean, ): Result>> = runCancellableCatching { coroutineScope { val accounts = accountIds.map { @@ -28,12 +29,16 @@ class CreateRenoteMultipleAccountUseCase @Inject constructor( } val note = recursiveSearchHasContentNote(noteId).getOrThrow() accounts.map { - resolveAndRenote(note, it.accountId) + resolveAndRenote(note, it.accountId, inChannel) } } } - private suspend fun resolveAndRenote(sourceNote: Note, accountId: Long): Result = + private suspend fun resolveAndRenote( + sourceNote: Note, + accountId: Long, + inChannel: Boolean, + ): Result = runCancellableCatching { val account = accountRepository.get(accountId).getOrThrow() val relatedSourceNoteAccount = @@ -44,17 +49,17 @@ class CreateRenoteMultipleAccountUseCase @Inject constructor( noteRepository.find(Note.Id(account.accountId, sourceNote.id.noteId)).getOrThrow() } - renote(relatedNote).getOrThrow() + renote(relatedNote, inChannel).getOrThrow() } - private suspend fun renote(note: Note): Result = + private suspend fun renote(note: Note, inChannel: Boolean): Result = runCancellableCatching { if (checkCanRepostService .canRepost(note.id) .getOrElse { false } ) { - noteRepository.renote(note.id).getOrThrow() + noteRepository.renote(note.id, inChannel).getOrThrow() } else { throw IllegalArgumentException() } From 4a91ad7f7a80d28d2dbdfba889e1b4f8f468eb2e Mon Sep 17 00:00:00 2001 From: osamu <46447427+sam-osamu@users.noreply.github.com> Date: Sun, 5 Nov 2023 14:18:44 +0900 Subject: [PATCH 2/6] fix buildError --- .../milktea/data/infrastructure/note/impl/NoteApiAdapter.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt index a1b6073d9e..34f6d34662 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt @@ -165,7 +165,6 @@ private class NoteApiAdapterMisskeyPattern( }, text = null, visibility = target.visibility, - channelId = target.channelId, ) ) } From 949bcb3d2505971edb800ea038563411235cf6bd Mon Sep 17 00:00:00 2001 From: osamu <46447427+sam-osamu@users.noreply.github.com> Date: Sun, 5 Nov 2023 16:37:46 +0900 Subject: [PATCH 3/6] =?UTF-8?q?=E4=B8=80=E5=BA=A6=E3=83=81=E3=83=A3?= =?UTF-8?q?=E3=83=B3=E3=83=8D=E3=83=AB=E5=86=85=E3=81=A7=E3=83=AA=E3=83=8E?= =?UTF-8?q?=E3=83=BC=E3=83=88=E3=81=97=E3=82=88=E3=81=86=E3=81=A8=E3=81=99?= =?UTF-8?q?=E3=82=8B=E3=81=A8=E3=80=81=E3=81=9D=E3=81=AE=E3=81=A8=E3=81=8D?= =?UTF-8?q?=E3=81=AE=E9=81=B8=E6=8A=9E=E5=8F=AF=E5=90=A6=E7=8A=B6=E6=85=8B?= =?UTF-8?q?=E3=81=8C=E6=AE=8B=E3=82=8B=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 --- .../milktea/note/renote/RenoteViewModel.kt | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 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 f9f0b7cf8e..b4daa656c6 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 @@ -38,7 +38,14 @@ class RenoteViewModel @Inject constructor( val logger = loggerFactory.create("RenoteDialogViewModel") - private var _targetNoteId = MutableStateFlow(null) + private val _targetNoteId = MutableStateFlow(null) + + @OptIn(ExperimentalCoroutinesApi::class) + private val _targetNote = _targetNoteId.filterNotNull().flatMapLatest { + noteRepository.observeOne(it).filterNotNull().map { note -> + noteRelationGetter.get(note).getOrNull() + } + } private val _resultEvents = MutableSharedFlow( onBufferOverflow = BufferOverflow.DROP_OLDEST, @@ -47,22 +54,16 @@ class RenoteViewModel @Inject constructor( val resultEvents = _resultEvents.asSharedFlow() - - @OptIn(ExperimentalCoroutinesApi::class) - val note = _targetNoteId.filterNotNull().flatMapLatest { - noteRepository.observeOne(it).filterNotNull().map { note -> - noteRelationGetter.get(note).getOrNull() - } - }.stateIn( + val note = _targetNote.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), null ) @OptIn(ExperimentalCoroutinesApi::class) - val channel = note.filterNotNull() - .mapNotNull { it.note.channelId } - .flatMapLatest { flowOf(channelRepository.findOne(it).getOrNull()) } + val channel = note + .map { it?.note?.channelId } + .flatMapLatest { flowOf(it?.let { ch -> channelRepository.findOne(ch).getOrNull() }) } .stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), From 2e54020bce895cf83ebfb1025a75485985067fc6 Mon Sep 17 00:00:00 2001 From: osamu <46447427+sam-osamu@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:01:44 +0900 Subject: [PATCH 4/6] =?UTF-8?q?withContext=E3=81=AE=E5=89=8A=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pantasystem/milktea/note/viewmodel/NotesViewModel.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt index caf99bad00..9c4071cc2c 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt @@ -3,12 +3,10 @@ package net.pantasystem.milktea.note.viewmodel import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import net.pantasystem.milktea.app_store.notes.NoteTranslationStore import net.pantasystem.milktea.common.Logger import net.pantasystem.milktea.common_android.resource.StringSource @@ -69,11 +67,8 @@ class NotesViewModel @Inject constructor( fun showQuoteNoteEditor(noteId: Note.Id, isRenoteToChannel: Boolean) { viewModelScope.launch { recursiveSearchHasContentNote(noteId).onSuccess { note -> - withContext(Dispatchers.Main) { - quoteRenoteTarget.tryEmit(QuoteRenoteData(note, isRenoteToChannel)) - } + quoteRenoteTarget.tryEmit(QuoteRenoteData(note, isRenoteToChannel)) } - } } From 3e908e49419d283637b6b7084fb22c7e8c049e74 Mon Sep 17 00:00:00 2001 From: osamu <46447427+sam-osamu@users.noreply.github.com> Date: Mon, 6 Nov 2023 09:07:36 +0900 Subject: [PATCH 5/6] =?UTF-8?q?channel=E3=81=AEstate=E4=BD=9C=E6=88=90?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/pantasystem/milktea/note/renote/RenoteViewModel.kt | 6 ++---- 1 file changed, 2 insertions(+), 4 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 b4daa656c6..2bfa8169d1 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 @@ -60,10 +60,8 @@ class RenoteViewModel @Inject constructor( null ) - @OptIn(ExperimentalCoroutinesApi::class) - val channel = note - .map { it?.note?.channelId } - .flatMapLatest { flowOf(it?.let { ch -> channelRepository.findOne(ch).getOrNull() }) } + val channel = _targetNote + .map { it?.note?.channelId?.let { id -> channelRepository.findOne(id).getOrNull() } } .stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), From 1515ca9cbbd887789debd85462aeb813e1c58094 Mon Sep 17 00:00:00 2001 From: osamu <46447427+sam-osamu@users.noreply.github.com> Date: Mon, 6 Nov 2023 14:53:11 +0900 Subject: [PATCH 6/6] =?UTF-8?q?=E3=83=AA=E3=83=8E=E3=83=BC=E3=83=88?= =?UTF-8?q?=E6=99=82=E3=81=AE=E3=83=91=E3=83=A9=E3=83=A1=E3=83=BC=E3=82=BF?= =?UTF-8?q?=E5=BC=95=E3=81=8D=E5=9B=9E=E3=81=97=E6=96=B9=E6=B3=95=E3=82=92?= =?UTF-8?q?=E5=B0=82=E7=94=A8=E3=81=AE=E3=83=A2=E3=83=87=E3=83=AB=E3=82=AF?= =?UTF-8?q?=E3=83=A9=E3=82=B9=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../note/impl/NoteApiAdapter.kt | 24 ++--- .../note/impl/NoteRepositoryImpl.kt | 7 +- .../note/renote/RenoteBottomSheetDialog.kt | 6 +- .../milktea/note/renote/RenoteViewModel.kt | 17 +++- .../milktea/note/view/NoteActionHandler.kt | 8 +- .../milktea/note/viewmodel/NotesViewModel.kt | 21 +++- .../milktea/note/viewmodel/QuoteRenoteData.kt | 8 -- .../milktea/model/note/NoteRepository.kt | 3 +- .../milktea/model/note/repost/CreateRenote.kt | 35 +++++++ .../CreateRenoteMultipleAccountUseCase.kt | 97 ++++++++++++------- .../model/note/repost/QuoteRenoteData.kt | 25 +++++ 11 files changed, 175 insertions(+), 76 deletions(-) delete mode 100644 modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/QuoteRenoteData.kt create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenote.kt create mode 100644 modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/QuoteRenoteData.kt diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt index 34f6d34662..4487ab7f81 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteApiAdapter.kt @@ -27,6 +27,7 @@ import net.pantasystem.milktea.model.note.CreateNote import net.pantasystem.milktea.model.note.Note import net.pantasystem.milktea.model.note.NoteState import net.pantasystem.milktea.model.note.poll.Poll +import net.pantasystem.milktea.model.note.repost.CreateRenote import net.pantasystem.milktea.model.note.type4Mastodon import javax.inject.Inject @@ -43,7 +44,7 @@ interface NoteApiAdapter { suspend fun delete(noteId: Note.Id): DeleteNoteResultType suspend fun createThreadMute(noteId: Note.Id): ToggleThreadMuteResultType suspend fun deleteThreadMute(noteId: Note.Id): ToggleThreadMuteResultType - suspend fun renote(target: Note, inChannel: Boolean): RenoteResultType + suspend fun renote(createRenote: CreateRenote): RenoteResultType suspend fun vote(noteId: Note.Id, choice: Poll.Choice, target: Note) @@ -152,19 +153,14 @@ private class NoteApiAdapterMisskeyPattern( return ToggleThreadMuteResultType.Misskey } - override suspend fun renote(target: Note, inChannel: Boolean): RenoteResultType { - val account = accountRepository.get(target.id.accountId).getOrThrow() + override suspend fun renote(createRenote: CreateRenote): RenoteResultType { return create( CreateNote( - author = account, - renoteId = target.id, - channelId = if (inChannel) { - target.channelId - } else { - null - }, + author = createRenote.author, + renoteId = createRenote.renoteId, + channelId = createRenote.channelId, text = null, - visibility = target.visibility, + visibility = createRenote.visibility, ) ) } @@ -284,9 +280,9 @@ private class NoteApiAdapterMastodonPattern( return ToggleThreadMuteResultType.Mastodon(requireNotNull(body)) } - override suspend fun renote(target: Note, inChannel: Boolean): RenoteResultType { - val account = accountRepository.get(target.id.accountId).getOrThrow() - val toot = mastodonAPIProvider.get(account).reblog(target.id.noteId) + override suspend fun renote(createRenote: CreateRenote): RenoteResultType { + val account = accountRepository.get(createRenote.author.accountId).getOrThrow() + val toot = mastodonAPIProvider.get(account).reblog(createRenote.renoteId.noteId) .throwIfHasError() .body() return NoteResultType.Mastodon(requireNotNull(toot)) diff --git a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteRepositoryImpl.kt b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteRepositoryImpl.kt index f095c8152e..46eb25be9c 100644 --- a/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteRepositoryImpl.kt +++ b/modules/data/src/main/java/net/pantasystem/milktea/data/infrastructure/note/impl/NoteRepositoryImpl.kt @@ -23,6 +23,7 @@ import net.pantasystem.milktea.model.note.NoteResult import net.pantasystem.milktea.model.note.NoteState import net.pantasystem.milktea.model.note.NoteThreadContext import net.pantasystem.milktea.model.note.poll.Poll +import net.pantasystem.milktea.model.note.repost.CreateRenote import javax.inject.Inject class NoteRepositoryImpl @Inject constructor( @@ -41,11 +42,9 @@ class NoteRepositoryImpl @Inject constructor( } } - override suspend fun renote(noteId: Note.Id, inChannel: Boolean): Result = runCancellableCatching { + override suspend fun renote(createRenote: CreateRenote): Result = runCancellableCatching { withContext(ioDispatcher) { - val account = getAccount.get(noteId.accountId) - val n = find(noteId).getOrThrow() - convertAndAdd(account, noteApiAdapterFactory.create(account).renote(n, inChannel)) + convertAndAdd(createRenote.author, noteApiAdapterFactory.create(createRenote.author).renote(createRenote)) } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteBottomSheetDialog.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteBottomSheetDialog.kt index d527c8bf81..04bf8ff965 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteBottomSheetDialog.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/renote/RenoteBottomSheetDialog.kt @@ -81,15 +81,15 @@ class RenoteBottomSheetDialog : BottomSheetDialogFragment() { dismiss() }, onQuoteRenoteButtonClicked = { - notesViewModel.showQuoteNoteEditor(noteId, false) + notesViewModel.showQuoteNoteEditor(noteId) dismiss() }, onRenoteInChannelButtonClicked = { - viewModel.renote(inChannel = true) + viewModel.renoteToChannel() dismiss() }, onQuoteInChannelRenoteButtonClicked = { - notesViewModel.showQuoteNoteEditor(noteId, true) + notesViewModel.showQuoteToChannelNoteEditor(noteId) dismiss() }, onDeleteRenoteButtonCLicked = { 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 2bfa8169d1..055e8f47cd 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 @@ -29,7 +29,7 @@ class RenoteViewModel @Inject constructor( private val channelRepository: ChannelRepository, val accountStore: AccountStore, val userDataSource: UserDataSource, - val renoteUseCase: CreateRenoteMultipleAccountUseCase, + private val renoteUseCase: CreateRenoteMultipleAccountUseCase, val noteRelationGetter: NoteRelationGetter, private val instanceInfoService: InstanceInfoService, renoteUiStateBuilder: RenoteUiStateBuilder, @@ -134,11 +134,22 @@ class RenoteViewModel @Inject constructor( } } - fun renote(inChannel: Boolean = false) { + fun renote() { val noteId = _targetNoteId.value ?: return val accountIds = _selectedAccountIds.value viewModelScope.launch { - val result = renoteUseCase(noteId, accountIds, inChannel) + val result = renoteUseCase.renote(noteId, accountIds) + _resultEvents.tryEmit( + RenoteActionResultEvent.Renote(result, noteId, accountIds) + ) + } + } + + fun renoteToChannel() { + val noteId = _targetNoteId.value ?: return + val accountIds = _selectedAccountIds.value + viewModelScope.launch { + val result = renoteUseCase.renoteToChannel(noteId, accountIds) _resultEvents.tryEmit( RenoteActionResultEvent.Renote(result, noteId, accountIds) ) diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteActionHandler.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteActionHandler.kt index 6baefef934..fe8b4a679f 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteActionHandler.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/view/NoteActionHandler.kt @@ -35,12 +35,8 @@ class NoteActionHandler( .onEach { val intent = NoteEditorActivity.newBundle( context, - quoteTo = it.note.id, - channelId = if (it.isRenoteToChannel) { - it.note.channelId - } else { - null - } + quoteTo = it.noteId, + channelId = it.channelId ) context.startActivity(intent) } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt index 9c4071cc2c..eec6d7dc3a 100644 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt +++ b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/NotesViewModel.kt @@ -25,6 +25,7 @@ import net.pantasystem.milktea.model.note.poll.Poll import net.pantasystem.milktea.model.note.poll.VoteUseCase import net.pantasystem.milktea.model.note.reaction.DeleteReactionsUseCase import net.pantasystem.milktea.model.note.reaction.ToggleReactionUseCase +import net.pantasystem.milktea.model.note.repost.QuoteRenoteData import net.pantasystem.milktea.model.user.report.Report import net.pantasystem.milktea.note.R import javax.inject.Inject @@ -64,10 +65,24 @@ class NotesViewModel @Inject constructor( private val _openNoteEditorEvent = MutableSharedFlow(onBufferOverflow = BufferOverflow.DROP_OLDEST, extraBufferCapacity = 10) val openNoteEditorEvent = _openNoteEditorEvent.asSharedFlow() - fun showQuoteNoteEditor(noteId: Note.Id, isRenoteToChannel: Boolean) { + fun showQuoteNoteEditor(noteId: Note.Id) { viewModelScope.launch { - recursiveSearchHasContentNote(noteId).onSuccess { note -> - quoteRenoteTarget.tryEmit(QuoteRenoteData(note, isRenoteToChannel)) + // 引用リノートしようとしたノートが第三者によりリノートされたものである可能性もあるので、 + // リノートIDを辿って大本のノートを探し出す + recursiveSearchHasContentNote(noteId).onSuccess { + quoteRenoteTarget.tryEmit( + QuoteRenoteData.ofTimeline(it.id) + ) + } + } + } + + fun showQuoteToChannelNoteEditor(noteId: Note.Id) { + viewModelScope.launch { + recursiveSearchHasContentNote(noteId).onSuccess { + quoteRenoteTarget.tryEmit( + QuoteRenoteData.ofChannel(it.id, requireNotNull(it.channelId)) + ) } } } diff --git a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/QuoteRenoteData.kt b/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/QuoteRenoteData.kt deleted file mode 100644 index 236425bb1f..0000000000 --- a/modules/features/note/src/main/java/net/pantasystem/milktea/note/viewmodel/QuoteRenoteData.kt +++ /dev/null @@ -1,8 +0,0 @@ -package net.pantasystem.milktea.note.viewmodel - -import net.pantasystem.milktea.model.note.Note - -data class QuoteRenoteData( - val note: Note, - val isRenoteToChannel: Boolean, -) \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/note/NoteRepository.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/note/NoteRepository.kt index 9f1dd78fa5..d38801a612 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/note/NoteRepository.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/note/NoteRepository.kt @@ -2,6 +2,7 @@ package net.pantasystem.milktea.model.note import kotlinx.coroutines.flow.Flow import net.pantasystem.milktea.model.note.poll.Poll +import net.pantasystem.milktea.model.note.repost.CreateRenote interface NoteRepository { @@ -9,7 +10,7 @@ interface NoteRepository { suspend fun create(createNote: CreateNote): Result - suspend fun renote(noteId: Note.Id, inChannel: Boolean): Result + suspend fun renote(createRenote: CreateRenote): Result suspend fun unrenote(noteId: Note.Id): Result diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenote.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenote.kt new file mode 100644 index 0000000000..0300ab1433 --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenote.kt @@ -0,0 +1,35 @@ +package net.pantasystem.milktea.model.note.repost + + +import net.pantasystem.milktea.model.account.Account +import net.pantasystem.milktea.model.channel.Channel +import net.pantasystem.milktea.model.note.Note +import net.pantasystem.milktea.model.note.Visibility + + +data class CreateRenote( + val author: Account, + val renoteId: Note.Id, + val channelId: Channel.Id?, + val visibility: Visibility, +) { + companion object { + fun ofTimeline(author: Account, target: Note) : CreateRenote { + return CreateRenote( + author = author, + renoteId = target.id, + channelId = null, + visibility = target.visibility, + ) + } + + fun ofChannel(author: Account, target: Note): CreateRenote { + return CreateRenote( + author = author, + renoteId = target.id, + channelId = target.channelId, + visibility = target.visibility, + ) + } + } +} \ No newline at end of file diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenoteMultipleAccountUseCase.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenoteMultipleAccountUseCase.kt index 1beabdc33b..e83c73a6bf 100644 --- a/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenoteMultipleAccountUseCase.kt +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/CreateRenoteMultipleAccountUseCase.kt @@ -3,6 +3,7 @@ package net.pantasystem.milktea.model.note.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.ApResolverService import net.pantasystem.milktea.model.note.Note @@ -18,53 +19,81 @@ class CreateRenoteMultipleAccountUseCase @Inject constructor( private val apResolverService: ApResolverService, ) : UseCase { - suspend operator fun invoke( - noteId: Note.Id, - accountIds: List, - inChannel: Boolean, + /** + * [targetNoteId]を持つノートをHTL/LTL/STL/GTLへリノートする。 + * チャンネル内に投稿されたノートの場合、チャンネル内にはリノートされず各種TLへリノートされる。 + * チャンネル内にリノートしたい場合は[renoteToChannel]を使用する。 + * + * @param targetNoteId リノート対象の[Note.Id] + * @param publisherAccountIds このアカウントからリノートを行う + */ + suspend fun renote( + targetNoteId: Note.Id, + publisherAccountIds: List, ): Result>> = runCancellableCatching { coroutineScope { - val accounts = accountIds.map { - accountRepository.get(it).getOrThrow() + val note = recursiveSearchHasContentNote(targetNoteId).getOrThrow() + publisherAccountIds.map { + resolveAndRenote(note, it) { a, n -> + CreateRenote.ofTimeline(a, n) + } } - val note = recursiveSearchHasContentNote(noteId).getOrThrow() - accounts.map { - resolveAndRenote(note, it.accountId, inChannel) + } + } + + /** + * [targetNoteId]を持つノートが投稿されたチャンネルと同じチャンネルにリノートする。 + * [targetNoteId]がチャンネル外に投稿されたノートの場合、特定のチャンネル内にリノートということにはならず[renote]と同等の挙動となる。 + * チャンネル外にリノートしたい場合は[renote]を使用する。 + * + * @param targetNoteId リノート対象の[Note.Id] + * @param publisherAccountIds このアカウントからリノートを行う + */ + suspend fun renoteToChannel( + targetNoteId: Note.Id, + publisherAccountIds: List, + ): Result>> = runCancellableCatching { + coroutineScope { + val note = recursiveSearchHasContentNote(targetNoteId).getOrThrow() + publisherAccountIds.map { + resolveAndRenote(note, it) { a, n -> + CreateRenote.ofChannel(a, n) + } } } } private suspend fun resolveAndRenote( - sourceNote: Note, - accountId: Long, - inChannel: Boolean, - ): Result = - runCancellableCatching { - val account = accountRepository.get(accountId).getOrThrow() - val relatedSourceNoteAccount = - accountRepository.get(sourceNote.id.accountId).getOrThrow() - val relatedNote = if (account.getHost() != relatedSourceNoteAccount.getHost()) { - apResolverService.resolve(sourceNote.id, accountId).getOrThrow() - } else { - noteRepository.find(Note.Id(account.accountId, sourceNote.id.noteId)).getOrThrow() - } + targetNote: Note, + publisherAccountId: Long, + paramFactory: (Account, Note) -> CreateRenote + ): Result = runCancellableCatching { + val publisherAccount = accountRepository.get(publisherAccountId).getOrThrow() + val relatedTargetNoteAccount = accountRepository.get(targetNote.id.accountId).getOrThrow() - renote(relatedNote, inChannel).getOrThrow() + val resolvedNote = if (publisherAccount.getHost() != relatedTargetNoteAccount.getHost()) { + // リノート発信アカウントと対象ノートのホストが異なる場合、 + // AP経由でリノート発信アカウントのあるホストに対象ノートを取り寄せ、ホストに複製されたノートをリノートする + apResolverService + .resolve(targetNote.id, publisherAccountId) + .getOrThrow() + } else { + noteRepository + .find(Note.Id(publisherAccount.accountId, targetNote.id.noteId)) + .getOrThrow() } - - private suspend fun renote(note: Note, inChannel: Boolean): Result = - runCancellableCatching { - if (checkCanRepostService - .canRepost(note.id) - .getOrElse { false } - ) { - noteRepository.renote(note.id, inChannel).getOrThrow() - } else { - throw IllegalArgumentException() - } + if (!checkCanRepostService.canRepost(resolvedNote.id).getOrElse { false }) { + throw IllegalArgumentException() } + val resolvedAccount = accountRepository.get(resolvedNote.id.accountId).getOrThrow() + + noteRepository + .renote(paramFactory.invoke(resolvedAccount, resolvedNote)) + .getOrThrow() + } + private suspend fun recursiveSearchHasContentNote(noteId: Note.Id): Result = runCancellableCatching { val note = noteRepository.find(noteId).getOrThrow() diff --git a/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/QuoteRenoteData.kt b/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/QuoteRenoteData.kt new file mode 100644 index 0000000000..1aa2117dce --- /dev/null +++ b/modules/model/src/main/java/net/pantasystem/milktea/model/note/repost/QuoteRenoteData.kt @@ -0,0 +1,25 @@ +package net.pantasystem.milktea.model.note.repost + +import net.pantasystem.milktea.model.channel.Channel +import net.pantasystem.milktea.model.note.Note + +data class QuoteRenoteData( + val noteId: Note.Id, + val channelId: Channel.Id?, +) { + companion object { + fun ofTimeline(noteId: Note.Id): QuoteRenoteData { + return QuoteRenoteData( + noteId = noteId, + channelId = null, + ) + } + + fun ofChannel(noteId: Note.Id, channelId: Channel.Id): QuoteRenoteData { + return QuoteRenoteData( + noteId = noteId, + channelId = channelId, + ) + } + } +} \ No newline at end of file