Skip to content

Commit

Permalink
Merge pull request #1943 from samunohito/feature/support_renote_in_ch…
Browse files Browse the repository at this point in the history
…annel

feat: チャンネル内のノートをチャンネル内・チャンネル外にリノート出来るようメニューを拡張
  • Loading branch information
pantasystem authored Nov 6, 2023
2 parents 6c13fde + 1515ca9 commit 14a9f53
Show file tree
Hide file tree
Showing 21 changed files with 282 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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,
)
}
}
Expand Down
2 changes: 2 additions & 0 deletions modules/common_resource/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
<string name="following">フォロー中</string>
<string name="follower">フォロワー</string>
<string name="renote">リポスト</string>
<string name="renote_in_channel">チャンネル内にリポスト</string>
<string name="quote_renote">引用</string>
<string name="quote_renote_in_channel">チャンネル内に引用</string>
<string name="discovery">discovery</string>
<string name="renote_was">リポストしました</string>
<string name="input_custom_emoji">絵文字/カスタム絵文字を入力</string>
Expand Down
2 changes: 2 additions & 0 deletions modules/common_resource/src/main/res/values-zh/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
<string name="following">关注中</string>
<string name="follower">关注者</string>
<string name="renote">转发</string>
<string name="renote_in_channel">频道内转发</string>
<string name="quote_renote">引用转发</string>
<string name="quote_renote_in_channel">频道内引用转发</string>
<string name="discovery">发现</string>
<string name="renote_was">转发</string>
<string name="input_custom_emoji">自定义表情</string>
Expand Down
2 changes: 2 additions & 0 deletions modules/common_resource/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
<string name="following">Following</string>
<string name="follower">Followers</string>
<string name="renote">Renote</string>
<string name="renote_in_channel">Renote in channel</string>
<string name="quote_renote">Quote renote</string>
<string name="quote_renote_in_channel">Quote renote in channel</string>
<string name="discovery">Discovery</string>
<string name="renote_was">Renote</string>
<string name="input_custom_emoji">Emoji</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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): RenoteResultType
suspend fun renote(createRenote: CreateRenote): RenoteResultType

suspend fun vote(noteId: Note.Id, choice: Poll.Choice, target: Note)

Expand All @@ -59,16 +60,17 @@ 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,
filePropertyDataSource = filePropertyDataSource,
uploader = uploader,
loggerFactory = loggerFactory,
)

Account.InstanceType.MASTODON, Account.InstanceType.PLEROMA -> NoteApiAdapterMastodonPattern(
uploader = uploader,
mastodonAPIProvider = mastodonAPIProvider,
Expand All @@ -77,6 +79,7 @@ internal class NoteApiAdapterFactoryImpl @Inject constructor(
}
}
}

private class NoteApiAdapterMisskeyPattern(
val accountRepository: AccountRepository,
val misskeyAPIProvider: MisskeyAPIProvider,
Expand Down Expand Up @@ -150,14 +153,14 @@ private class NoteApiAdapterMisskeyPattern(
return ToggleThreadMuteResultType.Misskey
}

override suspend fun renote(target: Note): RenoteResultType {
val account = accountRepository.get(target.id.accountId).getOrThrow()
override suspend fun renote(createRenote: CreateRenote): RenoteResultType {
return create(
CreateNote(
author = account, renoteId = target.id,
author = createRenote.author,
renoteId = createRenote.renoteId,
channelId = createRenote.channelId,
text = null,
visibility = target.visibility,
channelId = target.channelId,
visibility = createRenote.visibility,
)
)
}
Expand Down Expand Up @@ -213,6 +216,7 @@ private class NoteApiAdapterMastodonPattern(
uploader.get(createNote.author)
.upload(UploadSource.LocalFile(appFile), false).id
}

is AppFile.Remote -> appFile.id
}
}
Expand Down Expand Up @@ -276,9 +280,9 @@ private class NoteApiAdapterMastodonPattern(
return ToggleThreadMuteResultType.Mastodon(requireNotNull(body))
}

override suspend fun renote(target: Note): 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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -41,11 +42,9 @@ class NoteRepositoryImpl @Inject constructor(
}
}

override suspend fun renote(noteId: Note.Id): Result<Note> = runCancellableCatching {
override suspend fun renote(createRenote: CreateRenote): Result<Note> = runCancellableCatching {
withContext(ioDispatcher) {
val account = getAccount.get(noteId.accountId)
val n = find(noteId).getOrThrow()
convertAndAdd(account, noteApiAdapterFactory.create(account).renote(n))
convertAndAdd(createRenote.author, noteApiAdapterFactory.create(createRenote.author).renote(createRenote))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ fun PreviewChannelCard() {
userId = User.Id(0, "userId"),
usersCount = 4,
isFollowing = true,
hasUnreadNote = true
hasUnreadNote = true,
allowRenoteToExternal = true,
),
isPaged = true,
)
Expand All @@ -243,7 +244,8 @@ fun PreviewChannelCard() {
userId = User.Id(0, "userId"),
usersCount = 4,
isFollowing = false,
hasUnreadNote = false
hasUnreadNote = false,
allowRenoteToExternal = true,
), isPaged = false
)
}
Expand All @@ -260,7 +262,8 @@ fun PreviewChannelCard() {
userId = User.Id(0, "userId"),
usersCount = 4,
isFollowing = false,
hasUnreadNote = false
hasUnreadNote = false,
allowRenoteToExternal = true,
), isPaged = false
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ class RenoteBottomSheetDialog : BottomSheetDialogFragment() {
container: ViewGroup?,
savedInstanceState: Bundle?
): View {

return ComposeView(requireContext()).apply {
setContent {
MilkteaStyleConfigApplyAndTheme(configRepository = configRepository) {
Expand All @@ -81,14 +80,23 @@ class RenoteBottomSheetDialog : BottomSheetDialogFragment() {
viewModel.renote()
dismiss()
},
onQuoteRenoteButtonClicked = {
notesViewModel.showQuoteNoteEditor(noteId)
dismiss()
},
onRenoteInChannelButtonClicked = {
viewModel.renoteToChannel()
dismiss()
},
onQuoteInChannelRenoteButtonClicked = {
notesViewModel.showQuoteToChannelNoteEditor(noteId)
dismiss()
},
onDeleteRenoteButtonCLicked = {
viewModel.unRenote()
dismiss()
},
onQuoteRenoteButtonClicked = {
notesViewModel.showQuoteNoteEditor(noteId)
dismiss()
})
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ fun RenoteDialogContent(
onToggleAddAccount: (Long) -> Unit,
onRenoteButtonClicked: () -> Unit,
onQuoteRenoteButtonClicked: () -> Unit,
onRenoteInChannelButtonClicked: () -> Unit,
onQuoteInChannelRenoteButtonClicked: () -> Unit,
onDeleteRenoteButtonCLicked: () -> Unit,
) {
Surface(
Expand All @@ -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) {
Expand All @@ -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)
)
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ data class RenoteViewModelUiState(
),
val accounts: List<AccountInfo> = emptyList(),
val canQuote: Boolean = true,
val isRenoteButtonVisible: Boolean = true,
val isChannelRenoteButtonVisible: Boolean = false,
)

data class RenoteViewModelTargetNoteState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -30,6 +32,7 @@ class RenoteUiStateBuilder @Inject constructor(
fun buildState(
targetNoteIdFlow: StateFlow<Note.Id?>,
noteFlow: Flow<NoteRelation?>,
channelFlow: Flow<Channel?>,
noteSyncState: Flow<ResultState<Unit>>,
selectedAccountIds: Flow<List<Long>>,
accountsFlow: Flow<List<Account>>,
Expand Down Expand Up @@ -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,
)
}
}
Expand Down
Loading

0 comments on commit 14a9f53

Please sign in to comment.