diff --git a/app/src/main/java/dev/dimension/flare/ui/route/Route.kt b/app/src/main/java/dev/dimension/flare/ui/route/Route.kt index dbab564c1..1c1fbe968 100644 --- a/app/src/main/java/dev/dimension/flare/ui/route/Route.kt +++ b/app/src/main/java/dev/dimension/flare/ui/route/Route.kt @@ -101,6 +101,9 @@ internal sealed interface Route : NavKey { @Serializable data object AppearanceLayout : Settings + @Serializable + data object PostActionLayout : Settings + @Serializable data object AppearanceDisplay : Settings diff --git a/app/src/main/java/dev/dimension/flare/ui/screen/settings/AppearanceLayoutScreen.kt b/app/src/main/java/dev/dimension/flare/ui/screen/settings/AppearanceLayoutScreen.kt index 38e89a37b..506c4c85c 100644 --- a/app/src/main/java/dev/dimension/flare/ui/screen/settings/AppearanceLayoutScreen.kt +++ b/app/src/main/java/dev/dimension/flare/ui/screen/settings/AppearanceLayoutScreen.kt @@ -45,7 +45,10 @@ import moe.tlaster.precompose.molecule.producePresenter @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable -internal fun AppearanceLayoutScreen(onBack: () -> Unit) { +internal fun AppearanceLayoutScreen( + onBack: () -> Unit, + toPostActionLayout: () -> Unit, +) { val topAppBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() val state by producePresenter { appearancePresenter() } val globalAppearance = LocalGlobalAppearance.current @@ -231,26 +234,40 @@ internal fun AppearanceLayoutScreen(onBack: () -> Unit) { }, ) AnimatedVisibility(timelineAppearance.postActionStyle != PostActionStyle.Hidden) { - SegmentedListItem( - onClick = { - state.update(AppearanceKeys.ShowNumbers, !timelineAppearance.showNumbers) - }, - shapes = ListItemDefaults.last(), - content = { - Text(text = stringResource(id = R.string.settings_appearance_show_numbers)) - }, - supportingContent = { - Text(text = stringResource(id = R.string.settings_appearance_show_numbers_description)) - }, - trailingContent = { - Switch( - checked = timelineAppearance.showNumbers, - onCheckedChange = { - state.update(AppearanceKeys.ShowNumbers, it) - }, - ) - }, - ) + Column( + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { + SegmentedListItem( + onClick = { + state.update(AppearanceKeys.ShowNumbers, !timelineAppearance.showNumbers) + }, + shapes = ListItemDefaults.item(), + content = { + Text(text = stringResource(id = R.string.settings_appearance_show_numbers)) + }, + supportingContent = { + Text(text = stringResource(id = R.string.settings_appearance_show_numbers_description)) + }, + trailingContent = { + Switch( + checked = timelineAppearance.showNumbers, + onCheckedChange = { + state.update(AppearanceKeys.ShowNumbers, it) + }, + ) + }, + ) + SegmentedListItem( + onClick = toPostActionLayout, + shapes = ListItemDefaults.last(), + content = { + Text(text = stringResource(id = R.string.settings_appearance_post_action_layout)) + }, + supportingContent = { + Text(text = stringResource(id = R.string.settings_appearance_post_action_layout_description)) + }, + ) + } } } } diff --git a/app/src/main/java/dev/dimension/flare/ui/screen/settings/PostActionLayoutScreen.kt b/app/src/main/java/dev/dimension/flare/ui/screen/settings/PostActionLayoutScreen.kt new file mode 100644 index 000000000..d468da788 --- /dev/null +++ b/app/src/main/java/dev/dimension/flare/ui/screen/settings/PostActionLayoutScreen.kt @@ -0,0 +1,639 @@ +package dev.dimension.flare.ui.screen.settings + +import androidx.compose.animation.core.animateDpAsState +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.IconButton +import androidx.compose.material3.ListItemDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SegmentedListItem +import androidx.compose.material3.Switch +import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBarDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import compose.icons.FontAwesomeIcons +import compose.icons.fontawesomeicons.Solid +import compose.icons.fontawesomeicons.solid.Bars +import compose.icons.fontawesomeicons.solid.EllipsisVertical +import dev.dimension.flare.R +import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily +import dev.dimension.flare.data.datasource.microblog.PostActionLayoutConfig +import dev.dimension.flare.data.model.appearance.AppearanceKeys +import dev.dimension.flare.ui.component.BackButton +import dev.dimension.flare.ui.component.FAIcon +import dev.dimension.flare.ui.component.FlareLargeFlexibleTopAppBar +import dev.dimension.flare.ui.component.FlareScaffold +import dev.dimension.flare.ui.component.LocalTimelineAppearance +import dev.dimension.flare.ui.component.status.StatusItem +import dev.dimension.flare.ui.component.toImageVector +import dev.dimension.flare.ui.model.UiIcon +import dev.dimension.flare.ui.model.UiNumber +import dev.dimension.flare.ui.model.UiTimelineV2 +import dev.dimension.flare.ui.model.isSuccess +import dev.dimension.flare.ui.model.onSuccess +import dev.dimension.flare.ui.theme.first +import dev.dimension.flare.ui.theme.last +import dev.dimension.flare.ui.theme.screenHorizontalPadding +import dev.dimension.flare.ui.theme.segmentedShapes2 +import dev.dimension.flare.ui.theme.single +import kotlinx.collections.immutable.toPersistentList +import moe.tlaster.precompose.molecule.producePresenter +import sh.calvin.reorderable.ReorderableItem +import sh.calvin.reorderable.rememberReorderableLazyListState + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) +@Composable +internal fun PostActionLayoutScreen(onBack: () -> Unit) { + val topAppBarScrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() + val state by producePresenter { appearancePresenter() } + val timelineAppearance = LocalTimelineAppearance.current + val persistedConfig = timelineAppearance.postActionLayout.normalizedForEdit() + val configState = remember { mutableStateOf(persistedConfig) } + var config by configState + var isDraggingAction by remember { mutableStateOf(false) } + val lazyListState = rememberLazyListState() + val haptics = LocalHapticFeedback.current + + LaunchedEffect(persistedConfig) { + if (!isDraggingAction) { + configState.value = persistedConfig + } + } + + fun updateConfig( + value: PostActionLayoutConfig, + persist: Boolean = true, + ) { + val normalized = value.normalizedForEdit() + configState.value = normalized + if (persist && normalized != persistedConfig) { + state.update(AppearanceKeys.PostActionLayout, normalized) + } + } + + fun commitConfig() { + updateConfig(configState.value, persist = true) + } + + val reorderableState = + rememberReorderableLazyListState(lazyListState) { from, to -> + val fromFamily = from.key.asPostActionFamilyOrNull() ?: return@rememberReorderableLazyListState + val toFamily = to.key.asPostActionFamilyOrNull() ?: return@rememberReorderableLazyListState + val currentConfig = configState.value + val fromPlacement = currentConfig.placementOf(fromFamily) + val toPlacement = currentConfig.placementOf(toFamily) + if (fromPlacement == toPlacement) { + updateConfig( + currentConfig.moveWithin(fromPlacement, fromFamily, toFamily), + persist = false, + ) + haptics.performHapticFeedback(HapticFeedbackType.LongPress) + } + } + + FlareScaffold( + topBar = { + FlareLargeFlexibleTopAppBar( + title = { + Text(text = stringResource(id = R.string.settings_post_action_layout_title)) + }, + navigationIcon = { + BackButton(onBack = onBack) + }, + scrollBehavior = topAppBarScrollBehavior, + ) + }, + modifier = Modifier.nestedScroll(topAppBarScrollBehavior.nestedScrollConnection), + ) { contentPadding -> + LazyColumn( + state = lazyListState, + contentPadding = contentPadding, + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + modifier = Modifier.padding(horizontal = screenHorizontalPadding), + ) { + item(key = "preview_and_enabled") { + Column( + verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap), + ) { + val hasPreview = state.sampleStatus.isSuccess + state.sampleStatus.onSuccess { sample -> + SegmentedListItem( + onClick = {}, + shapes = ListItemDefaults.first(), + contentPadding = PaddingValues(0.dp), + ) { + CompositionLocalProvider( + LocalTimelineAppearance provides timelineAppearance.copy(postActionLayout = config), + ) { + StatusItem( + sample.withPostActionLayoutPreviewActions(), + modifier = Modifier.background(MaterialTheme.colorScheme.surface), + ) + } + } + } + + SegmentedListItem( + onClick = { + updateConfig(config.copy(enabled = !config.enabled)) + }, + shapes = if (hasPreview) ListItemDefaults.last() else ListItemDefaults.single(), + content = { + Text(text = stringResource(id = R.string.settings_post_action_layout_enable)) + }, + supportingContent = { + Text(text = stringResource(id = R.string.settings_post_action_layout_enable_description)) + }, + trailingContent = { + Switch( + checked = config.enabled, + onCheckedChange = { + updateConfig(config.copy(enabled = it)) + }, + ) + }, + ) + } + } + + if (config.enabled) { + postActionSection( + titleRes = R.string.settings_post_action_layout_button_row, + placement = PostActionPlacement.ButtonRow, + config = config, + onConfigChanged = { + updateConfig(it) + }, + onDragStarted = { + isDraggingAction = true + }, + onDragStopped = { + isDraggingAction = false + commitConfig() + }, + reorderableState = reorderableState, + ) + postActionSection( + titleRes = R.string.settings_post_action_layout_more_menu, + placement = PostActionPlacement.MoreMenu, + config = config, + onConfigChanged = { + updateConfig(it) + }, + onDragStarted = { + isDraggingAction = true + }, + onDragStopped = { + isDraggingAction = false + commitConfig() + }, + reorderableState = reorderableState, + ) + postActionSection( + titleRes = R.string.settings_post_action_layout_hidden, + placement = PostActionPlacement.Hidden, + config = config, + onConfigChanged = { + updateConfig(it) + }, + onDragStarted = { + isDraggingAction = true + }, + onDragStopped = { + isDraggingAction = false + commitConfig() + }, + reorderableState = reorderableState, + ) + } + } + } +} + +private fun LazyListScope.postActionSection( + titleRes: Int, + placement: PostActionPlacement, + config: PostActionLayoutConfig, + onConfigChanged: (PostActionLayoutConfig) -> Unit, + onDragStarted: () -> Unit, + onDragStopped: () -> Unit, + reorderableState: sh.calvin.reorderable.ReorderableLazyListState, +) { + val families = config.familiesFor(placement) + item(key = "${placement.name}_title") { + Text( + text = stringResource(id = titleRes), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.secondary, + modifier = + Modifier + .padding(top = 16.dp) + .padding(horizontal = 16.dp), + ) + } + if (families.isEmpty()) { + item(key = "${placement.name}_empty") { + SegmentedListItem( + onClick = {}, + shapes = ListItemDefaults.single(), + content = { + Text(text = stringResource(id = R.string.settings_post_action_layout_empty)) + }, + ) + } + } else { + itemsIndexed( + items = families, + key = { _, family -> family.saveableKey }, + ) { index, family -> + PostActionFamilyRow( + family = family, + placement = placement, + index = index, + totalCount = families.size, + config = config, + onConfigChanged = onConfigChanged, + onDragStarted = onDragStarted, + onDragStopped = onDragStopped, + reorderableState = reorderableState, + ) + } + } +} + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +private fun LazyItemScope.PostActionFamilyRow( + family: PostActionFamily, + placement: PostActionPlacement, + index: Int, + totalCount: Int, + config: PostActionLayoutConfig, + onConfigChanged: (PostActionLayoutConfig) -> Unit, + onDragStarted: () -> Unit, + onDragStopped: () -> Unit, + reorderableState: sh.calvin.reorderable.ReorderableLazyListState, +) { + val haptics = LocalHapticFeedback.current + val key = family.saveableKey + ReorderableItem(reorderableState, key = key) { isDragging -> + val elevation by animateDpAsState(if (isDragging) 4.dp else 0.dp) + var showMenu by remember { mutableStateOf(false) } + SegmentedListItem( + elevation = ListItemDefaults.elevation(elevation), + selected = isDragging, + onClick = { + showMenu = true + }, + shapes = ListItemDefaults.segmentedShapes2(index, totalCount), + leadingContent = { + FAIcon( + imageVector = family.icon.toImageVector(), + contentDescription = null, + ) + }, + content = { + Text(text = stringResource(id = family.labelRes)) + }, + trailingContent = { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + IconButton( + modifier = + Modifier.draggableHandle( + onDragStarted = { + onDragStarted() + haptics.performHapticFeedback(HapticFeedbackType.Confirm) + }, + onDragStopped = { + onDragStopped() + haptics.performHapticFeedback(HapticFeedbackType.Confirm) + }, + ), + onClick = {}, + ) { + FAIcon( + imageVector = FontAwesomeIcons.Solid.Bars, + contentDescription = stringResource(id = R.string.tab_settings_drag), + ) + } + Box { + IconButton(onClick = { showMenu = true }) { + FAIcon( + imageVector = FontAwesomeIcons.Solid.EllipsisVertical, + contentDescription = stringResource(id = R.string.more), + ) + } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false }, + ) { + enumValues().forEach { targetPlacement -> + if (targetPlacement != placement) { + DropdownMenuItem( + text = { + Text(text = stringResource(id = targetPlacement.moveLabelRes)) + }, + onClick = { + showMenu = false + onConfigChanged(config.moveTo(family, targetPlacement)) + }, + ) + } + } + DropdownMenuItem( + text = { + Text(text = stringResource(id = R.string.settings_post_action_layout_move_up)) + }, + enabled = index > 0, + onClick = { + showMenu = false + onConfigChanged(config.moveBy(family, -1)) + }, + ) + DropdownMenuItem( + text = { + Text(text = stringResource(id = R.string.settings_post_action_layout_move_down)) + }, + enabled = index < totalCount - 1, + onClick = { + showMenu = false + onConfigChanged(config.moveBy(family, 1)) + }, + ) + } + } + } + }, + ) + } +} + +private fun UiTimelineV2.Post.withPostActionLayoutPreviewActions(): UiTimelineV2.Post { + var replacedMore = false + val previewActions = + actions + .map { action -> + val item = action as? ActionMenu.Item + if (item?.isMoreMenuDisplayItem() == true) { + replacedMore = true + previewMoreGroup(item) + } else if (item?.actionFamily == PostActionFamily.Repost) { + previewRepostGroup(item) + } else { + action + } + }.let { actions -> + if (replacedMore) actions else actions + previewMoreGroup() + } + return copy(actions = previewActions.toPersistentList()) +} + +private fun ActionMenu.Item.isMoreMenuDisplayItem(): Boolean = + actionFamily == null && + icon == UiIcon.More && + text == ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.More) + +private fun previewMoreGroup( + displayItem: ActionMenu.Item = + ActionMenu.Item( + icon = UiIcon.More, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.More), + ), +): ActionMenu.Group = + ActionMenu.Group( + displayItem = displayItem, + actions = + listOf( + ActionMenu.Item( + icon = UiIcon.Translate, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Translate), + actionFamily = PostActionFamily.Translate, + ), + ActionMenu.Item( + icon = UiIcon.Bookmark, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Bookmark), + count = UiNumber(4), + actionFamily = PostActionFamily.Bookmark, + ), + ActionMenu.Item( + icon = UiIcon.Share, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Share), + actionFamily = PostActionFamily.Share, + ), + ).toPersistentList(), + ) + +private fun previewRepostGroup(displayItem: ActionMenu.Item): ActionMenu.Group = + ActionMenu.Group( + displayItem = displayItem, + actions = + listOf( + displayItem, + ActionMenu.Item( + icon = UiIcon.Quote, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Quote), + count = UiNumber(2), + actionFamily = PostActionFamily.Quote, + ), + ).toPersistentList(), + ) + +private enum class PostActionPlacement { + ButtonRow, + MoreMenu, + Hidden, +} + +private const val POST_ACTION_FAMILY_KEY_PREFIX = "post_action_family:" + +private val PostActionFamily.saveableKey: String + get() = "$POST_ACTION_FAMILY_KEY_PREFIX$name" + +private fun Any?.asPostActionFamilyOrNull(): PostActionFamily? { + val key = this as? String ?: return null + if (!key.startsWith(POST_ACTION_FAMILY_KEY_PREFIX)) return null + val name = key.removePrefix(POST_ACTION_FAMILY_KEY_PREFIX) + return runCatching { enumValueOf(name) }.getOrNull() +} + +private val allPostActionFamilies: List = + listOf( + PostActionFamily.Reply, + PostActionFamily.Comment, + PostActionFamily.Repost, + PostActionFamily.Like, + PostActionFamily.React, + PostActionFamily.Translate, + PostActionFamily.Bookmark, + PostActionFamily.Favorite, + PostActionFamily.Share, + PostActionFamily.Delete, + PostActionFamily.Report, + PostActionFamily.MuteUser, + PostActionFamily.BlockUser, + ) + +private fun PostActionLayoutConfig.normalizedForEdit(): PostActionLayoutConfig { + val primary = primary.cleanFamilies() + val hidden = hidden.cleanFamilies().filterNot { it in primary } + val overflow = + ( + overflow.cleanFamilies().filterNot { it in primary || it in hidden } + + allPostActionFamilies.filterNot { it in primary || it in hidden || it in overflow } + ).distinct() + return copy( + primary = primary.toPersistentList(), + overflow = overflow.toPersistentList(), + hidden = hidden.toPersistentList(), + ) +} + +private fun Iterable.cleanFamilies(): List = filter { it in allPostActionFamilies }.distinct() + +private fun PostActionLayoutConfig.familiesFor(placement: PostActionPlacement): List = + when (placement) { + PostActionPlacement.ButtonRow -> primary.toList() + PostActionPlacement.MoreMenu -> overflow.toList() + PostActionPlacement.Hidden -> hidden.toList() + } + +private fun PostActionLayoutConfig.placementOf(family: PostActionFamily): PostActionPlacement = + when { + family in primary -> PostActionPlacement.ButtonRow + family in hidden -> PostActionPlacement.Hidden + else -> PostActionPlacement.MoreMenu + } + +private fun PostActionLayoutConfig.moveTo( + family: PostActionFamily, + placement: PostActionPlacement, +): PostActionLayoutConfig { + val primary = primary.filterNot { it == family }.toMutableList() + val overflow = overflow.filterNot { it == family }.toMutableList() + val hidden = hidden.filterNot { it == family }.toMutableList() + when (placement) { + PostActionPlacement.ButtonRow -> primary += family + PostActionPlacement.MoreMenu -> overflow += family + PostActionPlacement.Hidden -> hidden += family + } + return copy( + primary = primary.toPersistentList(), + overflow = overflow.toPersistentList(), + hidden = hidden.toPersistentList(), + ).normalizedForEdit() +} + +private fun PostActionLayoutConfig.moveWithin( + placement: PostActionPlacement, + from: PostActionFamily, + to: PostActionFamily, +): PostActionLayoutConfig { + if (from == to) return this + return when (placement) { + PostActionPlacement.ButtonRow -> copy(primary = primary.toMutableList().move(from, to).toPersistentList()) + PostActionPlacement.MoreMenu -> copy(overflow = overflow.toMutableList().move(from, to).toPersistentList()) + PostActionPlacement.Hidden -> copy(hidden = hidden.toMutableList().move(from, to).toPersistentList()) + }.normalizedForEdit() +} + +private fun PostActionLayoutConfig.moveBy( + family: PostActionFamily, + offset: Int, +): PostActionLayoutConfig { + val placement = placementOf(family) + val families = familiesFor(placement) + val fromIndex = families.indexOf(family) + if (fromIndex == -1) return this + val toIndex = (fromIndex + offset).coerceIn(families.indices) + if (fromIndex == toIndex) return this + return moveWithin(placement, family, families[toIndex]) +} + +private fun MutableList.move( + from: PostActionFamily, + to: PostActionFamily, +): MutableList { + val fromIndex = indexOf(from) + val toIndex = indexOf(to) + if (fromIndex == -1 || toIndex == -1) return this + add(toIndex, removeAt(fromIndex)) + return this +} + +private val PostActionPlacement.moveLabelRes: Int + get() = + when (this) { + PostActionPlacement.ButtonRow -> R.string.settings_post_action_layout_move_to_button_row + PostActionPlacement.MoreMenu -> R.string.settings_post_action_layout_move_to_more_menu + PostActionPlacement.Hidden -> R.string.settings_post_action_layout_hide_action + } + +private val PostActionFamily.icon: UiIcon + get() = + when (this) { + PostActionFamily.Reply -> UiIcon.Reply + PostActionFamily.Comment -> UiIcon.Comment + PostActionFamily.Repost -> UiIcon.Retweet + PostActionFamily.Quote -> UiIcon.Quote + PostActionFamily.Like -> UiIcon.Like + PostActionFamily.React -> UiIcon.React + PostActionFamily.Translate -> UiIcon.Translate + PostActionFamily.Bookmark -> UiIcon.Bookmark + PostActionFamily.Favorite -> UiIcon.Favourite + PostActionFamily.Share -> UiIcon.Share + PostActionFamily.FxShare -> UiIcon.Share + PostActionFamily.Delete -> UiIcon.Delete + PostActionFamily.Report -> UiIcon.Report + PostActionFamily.MuteUser -> UiIcon.Mute + PostActionFamily.BlockUser -> UiIcon.Block + } + +private val PostActionFamily.labelRes: Int + get() = + when (this) { + PostActionFamily.Reply -> R.string.settings_post_action_family_reply + PostActionFamily.Comment -> R.string.settings_post_action_family_comment + PostActionFamily.Repost -> R.string.settings_post_action_family_repost + PostActionFamily.Quote -> R.string.settings_post_action_family_quote + PostActionFamily.Like -> R.string.settings_post_action_family_like + PostActionFamily.React -> R.string.settings_post_action_family_react + PostActionFamily.Translate -> R.string.settings_post_action_family_translate + PostActionFamily.Bookmark -> R.string.settings_post_action_family_bookmark + PostActionFamily.Favorite -> R.string.settings_post_action_family_favorite + PostActionFamily.Share -> R.string.settings_post_action_family_share + PostActionFamily.FxShare -> R.string.settings_post_action_family_fx_share + PostActionFamily.Delete -> R.string.settings_post_action_family_delete + PostActionFamily.Report -> R.string.settings_post_action_family_report + PostActionFamily.MuteUser -> R.string.settings_post_action_family_mute_user + PostActionFamily.BlockUser -> R.string.settings_post_action_family_block_user + } diff --git a/app/src/main/java/dev/dimension/flare/ui/screen/settings/SettingsSelectEntryBuilder.kt b/app/src/main/java/dev/dimension/flare/ui/screen/settings/SettingsSelectEntryBuilder.kt index de9dbf900..c206481f2 100644 --- a/app/src/main/java/dev/dimension/flare/ui/screen/settings/SettingsSelectEntryBuilder.kt +++ b/app/src/main/java/dev/dimension/flare/ui/screen/settings/SettingsSelectEntryBuilder.kt @@ -95,6 +95,19 @@ internal fun EntryProviderScope.settingsSelectEntryBuilder( ) ) { AppearanceLayoutScreen( + onBack = onBack, + toPostActionLayout = { + navigate(Route.Settings.PostActionLayout) + } + ) + } + + entry( + metadata = ListDetailSceneStrategy.detailPane( + sceneKey = "Settings" + ) + ) { + PostActionLayoutScreen( onBack = onBack ) } diff --git a/app/src/main/res/values-af-rZA/strings.xml b/app/src/main/res/values-af-rZA/strings.xml index 3b31ee1ad..032de156b 100644 --- a/app/src/main/res/values-af-rZA/strings.xml +++ b/app/src/main/res/values-af-rZA/strings.xml @@ -207,6 +207,35 @@ Links belyn Regs belyn Rek + Pas aksies aan + Kies die volgorde en sigbaarheid van plasingaksieknoppies + Plasingaksies + Pas aksies aan + Kies watter aksies in die ry, Meer-kieslys of versteek bly + Knoppiery + Meer-kieslys + Versteek + Geen aksies + Skuif na knoppiery + Skuif na Meer-kieslys + Versteek aksie + Skuif op + Skuif af + Antwoord + Kommentaar + Herplaas + Haal aan + Hou van + Voeg reaksie by + Vertaal + Voeg boekmerk by + Gunsteling + Deel + Deel via FxEmbed + Vee uit + Rapporteer + Demp + Blokkeer Wys media Wys media-aanhangsels in poste Wys getalle diff --git a/app/src/main/res/values-ar-rSA/strings.xml b/app/src/main/res/values-ar-rSA/strings.xml index d95f4a392..22b226698 100644 --- a/app/src/main/res/values-ar-rSA/strings.xml +++ b/app/src/main/res/values-ar-rSA/strings.xml @@ -263,6 +263,35 @@ محاذاة لليسار محاذاة لليمين تمديد + تخصيص الإجراءات + اختر ترتيب أزرار إجراءات المنشور وإمكانية ظهورها + إجراءات المنشور + تخصيص الإجراءات + اختر الإجراءات التي تظهر في صف الأزرار أو قائمة المزيد أو تبقى مخفية + صف الأزرار + قائمة المزيد + مخفي + لا توجد إجراءات + نقل إلى صف الأزرار + نقل إلى قائمة المزيد + إخفاء الإجراء + نقل لأعلى + نقل لأسفل + رد + تعليق + إعادة نشر + اقتباس + إعجاب + إضافة تفاعل + ترجمة + إضافة إشارة مرجعية + تفضيل + مشاركة + مشاركة عبر FxEmbed + حذف + إبلاغ + كتم + حظر إظهار الوسائط إظهار مرفقات الوسائط في المنشورات إظهار الأرقام diff --git a/app/src/main/res/values-bg-rBG/strings.xml b/app/src/main/res/values-bg-rBG/strings.xml index f08fb2d16..2f46690b2 100644 --- a/app/src/main/res/values-bg-rBG/strings.xml +++ b/app/src/main/res/values-bg-rBG/strings.xml @@ -211,6 +211,35 @@ Подравнени вляво Подравнени вдясно Разтегнати + Персонализиране на действията + Изберете реда и видимостта на бутоните за действия с публикация + Действия с публикация + Персонализиране на действията + Изберете кои действия да се показват в реда с бутони, менюто „Още“ или да останат скрити + Ред с бутони + Меню „Още“ + Скрити + Няма действия + Преместване в реда с бутони + Преместване в менюто „Още“ + Скриване на действието + Преместване нагоре + Преместване надолу + Отговор + Коментар + Препубликуване + Цитат + Харесване + Добави реакция + Превод + Добави отметка + Любими + Споделяне + Споделяне чрез FxEmbed + Изтриване + Докладване + Заглушаване + Блокиране Показване на медия Показване на медийни прикачени файлове в публикациите Показване на числа diff --git a/app/src/main/res/values-ca-rES/strings.xml b/app/src/main/res/values-ca-rES/strings.xml index 971a70f08..ba28c8e0e 100644 --- a/app/src/main/res/values-ca-rES/strings.xml +++ b/app/src/main/res/values-ca-rES/strings.xml @@ -207,6 +207,35 @@ Alineat a l\'esquerra Alineat a la dreta Estira + Personalitza les accions + Tria l’ordre i la visibilitat dels botons d’acció de la publicació + Accions de la publicació + Personalitza les accions + Tria quines accions apareixen a la fila, al menú Més o romanen amagades + Fila de botons + Menú Més + Amagat + Cap acció + Mou a la fila de botons + Mou al menú Més + Amaga l’acció + Mou amunt + Mou avall + Respondre + Comenta + Republicar + Citar + M\'agrada + Afegir reacció + Traduir + Afegir marcador + Preferit + Compartir + Compartir via FxEmbed + Elimina + Informar + Silenciar + Bloquejar Mostra el contingut multimèdia Mostra els fitxers adjunts multimèdia a les publicacions Mostra els nombres diff --git a/app/src/main/res/values-cs-rCZ/strings.xml b/app/src/main/res/values-cs-rCZ/strings.xml index 2b14a016a..1708adf4e 100644 --- a/app/src/main/res/values-cs-rCZ/strings.xml +++ b/app/src/main/res/values-cs-rCZ/strings.xml @@ -261,6 +261,35 @@ Zarovnané doleva Zarovnané doprava Roztažené + Přizpůsobit akce + Zvolte pořadí a viditelnost tlačítek akcí příspěvku + Akce příspěvku + Přizpůsobit akce + Zvolte, které akce se zobrazí v řádku, v nabídce Další nebo zůstanou skryté + Řádek tlačítek + Nabídka Další + Skryté + Žádné akce + Přesunout do řádku tlačítek + Přesunout do nabídky Další + Skrýt akci + Přesunout nahoru + Přesunout dolů + Odpovědět + Komentovat + Přesdílet + Citovat + To se mi líbí + Přidat reakci + Přeložit + Přidat do záložek + Oblíbené + Sdílet + Sdílet přes FxEmbed + Smazat + Nahlásit + Skrýt + Blokovat Zobrazit média Zobrazit mediální přílohy v příspěvcích Zobrazit čísla diff --git a/app/src/main/res/values-da-rDK/strings.xml b/app/src/main/res/values-da-rDK/strings.xml index f5c7f2fac..51fd2c274 100644 --- a/app/src/main/res/values-da-rDK/strings.xml +++ b/app/src/main/res/values-da-rDK/strings.xml @@ -259,6 +259,35 @@ Venstrestillet Højrestillet Stræk + Tilpas handlinger + Vælg rækkefølge og synlighed for handlingsknapper til indlæg + Indlægshandlinger + Tilpas handlinger + Vælg hvilke handlinger der vises i rækken, menuen Mere eller forbliver skjult + Knaprække + Menuen Mere + Skjult + Ingen handlinger + Flyt til knaprække + Flyt til menuen Mere + Skjul handling + Flyt op + Flyt ned + Svar + Kommentar + Post igen + Citér + Synes godt om + Tilføj reaktion + Oversæt + Tilføj bogmærke + Favorit + Del + Del via FxEmbed + Slet + Anmeld + Lydløs + Bloker Vis medier Vis medievedhæftninger i indlæg Vis tal diff --git a/app/src/main/res/values-de-rDE/strings.xml b/app/src/main/res/values-de-rDE/strings.xml index 568e6976e..4ee3e9703 100644 --- a/app/src/main/res/values-de-rDE/strings.xml +++ b/app/src/main/res/values-de-rDE/strings.xml @@ -259,6 +259,35 @@ Linksbündig Rechtsbündig Gestreckt + Aktionen anpassen + Reihenfolge und Sichtbarkeit der Beitragsaktions-Schaltflächen wählen + Beitragsaktionen + Aktionen anpassen + Wähle, welche Aktionen in der Zeile, im Mehr-Menü oder ausgeblendet bleiben + Schaltflächenzeile + Mehr-Menü + Ausgeblendet + Keine Aktionen + In die Schaltflächenzeile verschieben + Ins Mehr-Menü verschieben + Aktion ausblenden + Nach oben verschieben + Nach unten verschieben + Antworten + Kommentieren + Teilen + Zitieren + Gefällt mir + Reaktion hinzufügen + Übersetzen + Lesezeichen hinzufügen + Favorisieren + Teilen + Über FxEmbed teilen + Löschen + Melden + Stummschalten + Blockieren Medien anzeigen Medienanhänge in Beiträgen anzeigen Zahlen anzeigen diff --git a/app/src/main/res/values-el-rGR/strings.xml b/app/src/main/res/values-el-rGR/strings.xml index 2c1fe7959..dccd44c2c 100644 --- a/app/src/main/res/values-el-rGR/strings.xml +++ b/app/src/main/res/values-el-rGR/strings.xml @@ -243,6 +243,35 @@ Στοίχιση αριστερά Στοίχιση δεξιά Επέκταση + Προσαρμογή ενεργειών + Επιλέξτε τη σειρά και την ορατότητα των κουμπιών ενεργειών δημοσίευσης + Ενέργειες δημοσίευσης + Προσαρμογή ενεργειών + Επιλέξτε ποιες ενέργειες εμφανίζονται στη σειρά, στο μενού Περισσότερα ή παραμένουν κρυφές + Σειρά κουμπιών + Μενού Περισσότερα + Κρυφό + Καμία ενέργεια + Μετακίνηση στη σειρά κουμπιών + Μετακίνηση στο μενού Περισσότερα + Απόκρυψη ενέργειας + Μετακίνηση πάνω + Μετακίνηση κάτω + Απάντηση + Σχόλιο + Αναδημοσίευση + Παράθεση + Μου αρέσει + Προσθήκη αντίδρασης + Μετάφραση + Προσθήκη σελιδοδείκτη + Αγαπημένο + Κοινοποίηση + Κοινοποίηση μέσω FxEmbed + Διαγραφή + Αναφορά + Σίγαση + Αποκλεισμός Εμφάνιση μέσων Εμφάνιση συνημμένων μέσων στις δημοσιεύσεις Εμφάνιση αριθμών diff --git a/app/src/main/res/values-en-rUS/strings.xml b/app/src/main/res/values-en-rUS/strings.xml index 692198663..6f286ef3c 100644 --- a/app/src/main/res/values-en-rUS/strings.xml +++ b/app/src/main/res/values-en-rUS/strings.xml @@ -226,6 +226,35 @@ Left aligned Right aligned Stretch + Customize actions + Choose the order and visibility of post action buttons + Post actions + Customize actions + Choose which actions appear in the row, More menu, or stay hidden + Button row + More menu + Hidden + No actions + Move to button row + Move to More menu + Hide action + Move up + Move down + Reply + Comment + Repost + Quote + Like + React + Translate + Bookmark + Favorite + Share + Fx share + Delete + Report + Mute user + Block user Show media Show media attachments in posts Show numbers diff --git a/app/src/main/res/values-es-rES/strings.xml b/app/src/main/res/values-es-rES/strings.xml index 9671350d2..9f8255954 100644 --- a/app/src/main/res/values-es-rES/strings.xml +++ b/app/src/main/res/values-es-rES/strings.xml @@ -259,6 +259,35 @@ Alineado a la izquierda Alineado a la derecha Estirar + Personalizar acciones + Elige el orden y la visibilidad de los botones de acción de la publicación + Acciones de la publicación + Personalizar acciones + Elige qué acciones aparecen en la fila, en el menú Más o permanecen ocultas + Fila de botones + Menú Más + Oculto + Sin acciones + Mover a la fila de botones + Mover al menú Más + Ocultar acción + Mover arriba + Mover abajo + Responder + Comentario + Compartir + Citar + Me gusta + Añadir reacción + Traducir + Añadir marcador + Favorito + Compartir + Compartir vía FxEmbed + Eliminar + Reportar + Silenciar + Bloquear Mostrar multimedia Mostrar archivos adjuntos en los posts Mostrar números diff --git a/app/src/main/res/values-fi-rFI/strings.xml b/app/src/main/res/values-fi-rFI/strings.xml index a87064c5f..7157b8551 100644 --- a/app/src/main/res/values-fi-rFI/strings.xml +++ b/app/src/main/res/values-fi-rFI/strings.xml @@ -252,6 +252,35 @@ Vasemmalle tasattu Oikealle tasattu Venytä + Mukauta toimintoja + Valitse julkaisun toimintopainikkeiden järjestys ja näkyvyys + Julkaisun toiminnot + Mukauta toimintoja + Valitse mitkä toiminnot näkyvät rivillä, Lisää-valikossa tai pysyvät piilossa + Painikerivi + Lisää-valikko + Piilotettu + Ei toimintoja + Siirrä painikeriville + Siirrä Lisää-valikkoon + Piilota toiminto + Siirrä ylös + Siirrä alas + Vastaa + Kommentti + Jaa + Lainaa + Tykkää + Lisää reaktio + Käännä + Lisää kirjanmerkki + Suosikki + Jaa + Jaa FxEmbedin kautta + Poista + Raportoi + Mykistä + Estä Näytä media Näytä medialiitteet julkaisuissa Näytä luvut diff --git a/app/src/main/res/values-fr-rFR/strings.xml b/app/src/main/res/values-fr-rFR/strings.xml index 27892801a..476a48904 100644 --- a/app/src/main/res/values-fr-rFR/strings.xml +++ b/app/src/main/res/values-fr-rFR/strings.xml @@ -253,6 +253,35 @@ Aligné à gauche Aligné à droite Étiré + Personnaliser les actions + Choisir l’ordre et la visibilité des boutons d’action du post + Actions du post + Personnaliser les actions + Choisir les actions affichées dans la rangée, le menu Plus ou masquées + Rangée de boutons + Menu Plus + Masqué + Aucune action + Déplacer vers la rangée de boutons + Déplacer vers le menu Plus + Masquer l’action + Monter + Descendre + Répondre + Commenter + Repartager + Citer + J\'aime + Ajouter une réaction + Traduire + Ajouter un signet + Favori + Partager + Partager via FxEmbed + Supprimer + Signaler + Masquer + Bloquer Afficher les médias Afficher les pièces jointes dans les posts Afficher les nombres diff --git a/app/src/main/res/values-hu-rHU/strings.xml b/app/src/main/res/values-hu-rHU/strings.xml index d3a39e0e5..5ceb399b8 100644 --- a/app/src/main/res/values-hu-rHU/strings.xml +++ b/app/src/main/res/values-hu-rHU/strings.xml @@ -211,6 +211,35 @@ Balra igazított Jobbra igazított Nyújtott + Műveletek testreszabása + Válaszd ki a bejegyzésművelet-gombok sorrendjét és láthatóságát + Bejegyzésműveletek + Műveletek testreszabása + Válaszd ki, mely műveletek jelenjenek meg a sorban, a Továbbiak menüben vagy maradjanak rejtve + Gombsor + Továbbiak menü + Rejtett + Nincsenek műveletek + Áthelyezés a gombsorba + Áthelyezés a Továbbiak menübe + Művelet elrejtése + Mozgatás fel + Mozgatás le + Válasz + Hozzászólás + Újraközlés + Idézés + Kedvelés + Reakció hozzáadása + Fordítás + Könyvjelző hozzáadása + Kedvenc + Megosztás + Megosztás FxEmbed-en keresztül + Törlés + Jelentés + Némítás + Tiltás Média megjelenítése Média mellékletek megjelenítése a bejegyzésekben Számok megjelenítése diff --git a/app/src/main/res/values-it-rIT/strings.xml b/app/src/main/res/values-it-rIT/strings.xml index f932b356f..cb74e8f6c 100644 --- a/app/src/main/res/values-it-rIT/strings.xml +++ b/app/src/main/res/values-it-rIT/strings.xml @@ -259,6 +259,35 @@ Allineato a sinistra Allineato a destra Esteso + Personalizza azioni + Scegli ordine e visibilità dei pulsanti di azione del post + Azioni del post + Personalizza azioni + Scegli quali azioni appaiono nella riga, nel menu Altro o restano nascoste + Riga pulsanti + Menu Altro + Nascosto + Nessuna azione + Sposta nella riga pulsanti + Sposta nel menu Altro + Nascondi azione + Sposta su + Sposta giù + Rispondi + Commenta + Ripubblica + Cita + Mi piace + Aggiungi reazione + Traduci + Aggiungi segnalibro + Preferito + Condividi + Condividi tramite FxEmbed + Elimina + Segnala + Silenzia + Blocca Mostra media Mostra gli allegati multimediali nei post Mostra numeri diff --git a/app/src/main/res/values-iw-rIL/strings.xml b/app/src/main/res/values-iw-rIL/strings.xml index 22be386ea..746bd3a24 100644 --- a/app/src/main/res/values-iw-rIL/strings.xml +++ b/app/src/main/res/values-iw-rIL/strings.xml @@ -211,6 +211,35 @@ מיושר לשמאל מיושר לימין פריסה רחבה + התאמה אישית של פעולות + בחר את הסדר והנראות של כפתורי הפעולה בפוסט + פעולות פוסט + התאמה אישית של פעולות + בחר אילו פעולות יופיעו בשורה, בתפריט עוד או יישארו מוסתרות + שורת כפתורים + תפריט עוד + מוסתר + אין פעולות + העבר לשורת הכפתורים + העבר לתפריט עוד + הסתר פעולה + העבר למעלה + העבר למטה + תגובה + תגובה + פרסום מחדש + ציטוט + אהבתי + הוסף תגובה + תרגום + הוסף סימנייה + מועדף + שיתוף + שתף דרך FxEmbed + מחק + דיווח + השתקה + חסימה הצג מדיה הצג קבצי מדיה מצורפים בפוסטים הצג מספרים diff --git a/app/src/main/res/values-ja-rJP/strings.xml b/app/src/main/res/values-ja-rJP/strings.xml index 61230326a..7f2a79da3 100644 --- a/app/src/main/res/values-ja-rJP/strings.xml +++ b/app/src/main/res/values-ja-rJP/strings.xml @@ -239,6 +239,35 @@ 左寄せ 右寄せ 引き伸ばす + アクションをカスタマイズ + 投稿アクションボタンの順序と表示を選択 + 投稿アクション + アクションをカスタマイズ + ボタン行、その他メニュー、非表示にするアクションを選択 + ボタン行 + その他メニュー + 非表示 + アクションなし + ボタン行へ移動 + その他メニューへ移動 + アクションを非表示 + 上へ移動 + 下へ移動 + 返信 + コメント + リポスト + 引用 + いいね + リアクション + 翻訳 + ブックマーク + お気に入り + 共有 + Fx共有 + 削除 + 報告 + ユーザーをミュート + ユーザーをブロック メディアを表示 投稿内のメディア添付ファイルを表示する 件数を表示 diff --git a/app/src/main/res/values-ko-rKR/strings.xml b/app/src/main/res/values-ko-rKR/strings.xml index 3b5338e36..66ec89c53 100644 --- a/app/src/main/res/values-ko-rKR/strings.xml +++ b/app/src/main/res/values-ko-rKR/strings.xml @@ -208,6 +208,35 @@ 왼쪽 정렬 오른쪽 정렬 늘이기 + 동작 사용자화 + 게시물 동작 버튼의 순서와 표시 여부 선택 + 게시물 동작 + 동작 사용자화 + 버튼 행, 더 보기 메뉴 또는 숨김에 표시할 동작 선택 + 버튼 행 + 더 보기 메뉴 + 숨김 + 동작 없음 + 버튼 행으로 이동 + 더 보기 메뉴로 이동 + 동작 숨기기 + 위로 이동 + 아래로 이동 + 답글 + 댓글 + 리포스트 + 인용 + 좋아요 + 반응 + 번역 + 북마크 + 즐겨찾기 + 공유 + Fx 공유 + 삭제 + 신고 + 사용자 뮤트 + 사용자 차단 미디어 표시 게시물의 미디어 첨부 파일 표시 숫자 표시 diff --git a/app/src/main/res/values-nl-rNL/strings.xml b/app/src/main/res/values-nl-rNL/strings.xml index 07612f7a4..c15ec7b92 100644 --- a/app/src/main/res/values-nl-rNL/strings.xml +++ b/app/src/main/res/values-nl-rNL/strings.xml @@ -251,6 +251,35 @@ Links uitgelijnd Rechts uitgelijnd Uitgerekt + Acties aanpassen + Kies de volgorde en zichtbaarheid van actieknoppen voor berichten + Berichtacties + Acties aanpassen + Kies welke acties in de rij, het Meer-menu of verborgen blijven + Knoppenrij + Meer-menu + Verborgen + Geen acties + Naar knoppenrij verplaatsen + Naar Meer-menu verplaatsen + Actie verbergen + Omhoog verplaatsen + Omlaag verplaatsen + Beantwoorden + Reactie + Delen + Citeren + Vind ik leuk + Reactie toevoegen + Vertalen + Bladwijzer toevoegen + Favoriet + Delen + Delen via FxEmbed + Verwijderen + Rapporteren + Dempen + Blokkeren Media tonen Toon mediabijlagen in berichten Aantallen tonen diff --git a/app/src/main/res/values-no-rNO/strings.xml b/app/src/main/res/values-no-rNO/strings.xml index a5941f6b3..812b68afb 100644 --- a/app/src/main/res/values-no-rNO/strings.xml +++ b/app/src/main/res/values-no-rNO/strings.xml @@ -252,6 +252,35 @@ Venstrejustert Høyrejustert Strekk + Tilpass handlinger + Velg rekkefølge og synlighet for handlingsknapper på innlegg + Innleggshandlinger + Tilpass handlinger + Velg hvilke handlinger som vises i raden, Mer-menyen eller holdes skjult + Knapperad + Mer-meny + Skjult + Ingen handlinger + Flytt til knapperad + Flytt til Mer-meny + Skjul handling + Flytt opp + Flytt ned + Svar + Kommentar + Reposter + Siter + Lik + Legg til reaksjon + Oversett + Legg til bokmerke + Favoritt + Del + Del via FxEmbed + Slett + Rapporter + Demp + Blokker Vis media Vis medievedlegg i innlegg Vis antall diff --git a/app/src/main/res/values-pl-rPL/strings.xml b/app/src/main/res/values-pl-rPL/strings.xml index 7e0cc743f..02183d047 100644 --- a/app/src/main/res/values-pl-rPL/strings.xml +++ b/app/src/main/res/values-pl-rPL/strings.xml @@ -260,6 +260,35 @@ Do lewej Do prawej Rozciągnięte + Dostosuj akcje + Wybierz kolejność i widoczność przycisków akcji wpisu + Akcje wpisu + Dostosuj akcje + Wybierz, które akcje pojawią się w wierszu, menu Więcej albo pozostaną ukryte + Wiersz przycisków + Menu Więcej + Ukryte + Brak akcji + Przenieś do wiersza przycisków + Przenieś do menu Więcej + Ukryj akcję + Przenieś w górę + Przenieś w dół + Odpowiedz + Komentarz + Podaj dalej + Cytat + Lubię to! + Dodaj reakcję + Przetłumacz + Dodaj zakładkę + Ulubione + Udostępnij + Udostępnij przez FxEmbed + Usuń + Zgłoś + Wycisz + Zablokuj Pokaż multimedia Pokazuj załączniki multimedialne we wpisach Pokaż liczby diff --git a/app/src/main/res/values-pt-rBR/strings.xml b/app/src/main/res/values-pt-rBR/strings.xml index f59888d18..a304716ab 100644 --- a/app/src/main/res/values-pt-rBR/strings.xml +++ b/app/src/main/res/values-pt-rBR/strings.xml @@ -259,6 +259,35 @@ Alinhado à esquerda Alinhado à direita Esticar + Personalizar ações + Escolha a ordem e a visibilidade dos botões de ação da postagem + Ações da postagem + Personalizar ações + Escolha quais ações aparecem na linha, no menu Mais ou ficam ocultas + Linha de botões + Menu Mais + Oculto + Nenhuma ação + Mover para a linha de botões + Mover para o menu Mais + Ocultar ação + Mover para cima + Mover para baixo + Responder + Comentar + Repostar + Citar + Curtir + Adicionar reação + Traduzir + Salvar favorito + Favoritar + Compartilhar + Compartilhar via FxEmbed + Excluir + Denunciar + Silenciar + Bloquear Mostrar mídia Mostrar anexos de mídia nas postagens Mostrar números diff --git a/app/src/main/res/values-pt-rPT/strings.xml b/app/src/main/res/values-pt-rPT/strings.xml index b5188e6c9..6efeb86c3 100644 --- a/app/src/main/res/values-pt-rPT/strings.xml +++ b/app/src/main/res/values-pt-rPT/strings.xml @@ -259,6 +259,35 @@ Alinhado à esquerda Alinhado à direita Esticar + Personalizar ações + Escolha a ordem e a visibilidade dos botões de ação da publicação + Ações da publicação + Personalizar ações + Escolha que ações aparecem na linha, no menu Mais ou ficam ocultas + Linha de botões + Menu Mais + Oculto + Sem ações + Mover para a linha de botões + Mover para o menu Mais + Ocultar ação + Mover para cima + Mover para baixo + Responder + Comentar + Republicar + Citar + Gostar + Adicionar reação + Traduzir + Adicionar marcador + Favorito + Partilhar + Partilhar via FxEmbed + Eliminar + Denunciar + Silenciar + Bloquear Mostrar multimédia Mostrar anexos multimédia nas publicações Mostrar números diff --git a/app/src/main/res/values-ro-rRO/strings.xml b/app/src/main/res/values-ro-rRO/strings.xml index 33b36ba1d..223ed0184 100644 --- a/app/src/main/res/values-ro-rRO/strings.xml +++ b/app/src/main/res/values-ro-rRO/strings.xml @@ -260,6 +260,35 @@ Aliniat la stânga Aliniat la dreapta Întins + Personalizează acțiunile + Alege ordinea și vizibilitatea butoanelor de acțiune ale postării + Acțiuni postare + Personalizează acțiunile + Alege ce acțiuni apar pe rând, în meniul Mai multe sau rămân ascunse + Rând de butoane + Meniu Mai multe + Ascuns + Nicio acțiune + Mută în rândul de butoane + Mută în meniul Mai multe + Ascunde acțiunea + Mută în sus + Mută în jos + Răspunde + Comentariu + Repostare + Citat + Apreciază + Adaugă reacție + Tradu + Adaugă semn de carte + Favorit + Partajează + Partajează prin FxEmbed + Șterge + Raportează + Ignoră + Blochează Arată media Arată fișierele media atașate postărilor Arată numerele diff --git a/app/src/main/res/values-ru-rRU/strings.xml b/app/src/main/res/values-ru-rRU/strings.xml index eb52bee5d..bf466a345 100644 --- a/app/src/main/res/values-ru-rRU/strings.xml +++ b/app/src/main/res/values-ru-rRU/strings.xml @@ -257,6 +257,35 @@ По левому краю По правому краю Растянуть + Настроить действия + Выберите порядок и видимость кнопок действий поста + Действия поста + Настроить действия + Выберите, какие действия будут в строке, меню Ещё или останутся скрытыми + Строка кнопок + Меню Ещё + Скрыто + Нет действий + Переместить в строку кнопок + Переместить в меню Ещё + Скрыть действие + Переместить вверх + Переместить вниз + Ответить + Комментировать + Репост + Цитировать + Лайк + Добавить реакцию + Перевести + Добавить в закладки + В избранное + Поделиться + Поделиться через FxEmbed + Удалить + Пожаловаться + Игнорировать + Заблокировать Показывать медиа Показывать медиавложения в постах Показывать числа diff --git a/app/src/main/res/values-sr-rSP/strings.xml b/app/src/main/res/values-sr-rSP/strings.xml index a9b918935..e55d4aed7 100644 --- a/app/src/main/res/values-sr-rSP/strings.xml +++ b/app/src/main/res/values-sr-rSP/strings.xml @@ -212,6 +212,35 @@ Лево поравнато Десно поравнато Развучено + Прилагоди радње + Изаберите редослед и видљивост дугмади радњи објаве + Радње објаве + Прилагоди радње + Изаберите које радње се приказују у реду, менију Још или остају скривене + Ред дугмади + Мени Још + Скривено + Нема радњи + Премести у ред дугмади + Премести у мени Још + Сакриј радњу + Премести нагоре + Премести надоле + Odgovori + Коментариши + Ponovo objavi + Citiraj + Sviđa mi se + Dodaj reakciju + Prevedi + Dodaj obeleživač + Omiljeno + Podeli + Podeli putem FxEmbed + Обриши + Prijavi + Utišaj + Blokiraj Прикажи медије Прикажи медијске прилоге у објавама Прикажи бројеве diff --git a/app/src/main/res/values-sv-rSE/strings.xml b/app/src/main/res/values-sv-rSE/strings.xml index 4b52e1f6b..d5f12c8da 100644 --- a/app/src/main/res/values-sv-rSE/strings.xml +++ b/app/src/main/res/values-sv-rSE/strings.xml @@ -259,6 +259,35 @@ Vänsterjusterad Högerjusterad Sträck ut + Anpassa åtgärder + Välj ordning och synlighet för inläggets åtgärdsknappar + Inläggsåtgärder + Anpassa åtgärder + Välj vilka åtgärder som visas i raden, menyn Mer eller förblir dolda + Knapprad + Menyn Mer + Dold + Inga åtgärder + Flytta till knapprad + Flytta till menyn Mer + Dölj åtgärd + Flytta upp + Flytta ned + Svara + Kommentera + Reposta + Citera + Gilla + Lägg till reaktion + Översätt + Lägg till bokmärke + Favoritmarkera + Dela + Dela via FxEmbed + Ta bort + Rapportera + Tysta + Blockera Visa media Visa mediabilagor i inlägg Visa siffror diff --git a/app/src/main/res/values-tr-rTR/strings.xml b/app/src/main/res/values-tr-rTR/strings.xml index 149a698cd..4a4fbb085 100644 --- a/app/src/main/res/values-tr-rTR/strings.xml +++ b/app/src/main/res/values-tr-rTR/strings.xml @@ -210,6 +210,35 @@ Sola hizalı Sağa hizalı Yay + Eylemleri özelleştir + Gönderi eylem düğmelerinin sırasını ve görünürlüğünü seçin + Gönderi eylemleri + Eylemleri özelleştir + Hangi eylemlerin satırda, Daha Fazla menüsünde ya da gizli kalacağını seçin + Düğme satırı + Daha Fazla menüsü + Gizli + Eylem yok + Düğme satırına taşı + Daha Fazla menüsüne taşı + Eylemi gizle + Yukarı taşı + Aşağı taşı + Yanıtla + Yorum yap + Yeniden paylaş + Alıntıla + Beğen + Tepki ekle + Çevir + Yer işareti ekle + Favorilere ekle + Paylaş + FxEmbed ile paylaş + Sil + Bildir + Sessize al + Engelle Medyayı göster Gönderilerde medya eklerini göster Sayıları göster diff --git a/app/src/main/res/values-uk-rUA/strings.xml b/app/src/main/res/values-uk-rUA/strings.xml index 3a30dcbc3..9d0896c27 100644 --- a/app/src/main/res/values-uk-rUA/strings.xml +++ b/app/src/main/res/values-uk-rUA/strings.xml @@ -251,6 +251,35 @@ За лівим краєм За правим краєм Розтягнути + Налаштувати дії + Виберіть порядок і видимість кнопок дій допису + Дії допису + Налаштувати дії + Виберіть, які дії відображати в рядку, меню Ще або залишити прихованими + Рядок кнопок + Меню Ще + Приховано + Немає дій + Перемістити до рядка кнопок + Перемістити до меню Ще + Приховати дію + Перемістити вгору + Перемістити вниз + Відповісти + Коментувати + Поширити + Цитата + Подобається + Додати реакцію + Перекласти + У закладки + У вибране + Поділитися + Поділитися через FxEmbed + Видалити + Скаржитися + Приглушити + Заблокувати Показувати медіа Показувати медіавкладення в дописах Показувати лічильники diff --git a/app/src/main/res/values-vi-rVN/strings.xml b/app/src/main/res/values-vi-rVN/strings.xml index f0b4e1d91..3ac9d99ce 100644 --- a/app/src/main/res/values-vi-rVN/strings.xml +++ b/app/src/main/res/values-vi-rVN/strings.xml @@ -209,6 +209,35 @@ Căn trái Căn phải Giãn đều + Tùy chỉnh hành động + Chọn thứ tự và khả năng hiển thị của các nút hành động bài đăng + Hành động bài đăng + Tùy chỉnh hành động + Chọn hành động xuất hiện trong hàng, menu Thêm hoặc vẫn bị ẩn + Hàng nút + Menu Thêm + Đã ẩn + Không có hành động + Chuyển vào hàng nút + Chuyển vào menu Thêm + Ẩn hành động + Chuyển lên + Chuyển xuống + Trả lời + Bình luận + Đăng lại + Trích dẫn + Thích + Thêm cảm xúc + Dịch + Thêm dấu trang + Yêu thích + Chia sẻ + Chia sẻ qua FxEmbed + Xóa + Báo cáo + Ẩn + Chặn Hiển thị phương tiện Hiển thị các tệp phương tiện đính kèm trong bài đăng Hiển thị số liệu diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4ae0b38eb..77f539c34 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -253,6 +253,35 @@ 左对齐 右对齐 铺满 + 自定义操作 + 选择动态操作按钮的顺序和可见性 + 动态操作 + 自定义操作 + 选择哪些操作显示在按钮行、更多菜单或保持隐藏 + 按钮行 + 更多菜单 + 隐藏 + 无操作 + 移动到按钮行 + 移动到更多菜单 + 隐藏操作 + 上移 + 下移 + 回复 + 评论 + 转发 + 引用 + + 回应 + 翻译 + 书签 + 收藏 + 分享 + Fx 分享 + 删除 + 举报 + 静音用户 + 屏蔽用户 显示媒体 在动态中显示媒体附件 显示计数 diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml index c066edaf5..f1beb8e97 100644 --- a/app/src/main/res/values-zh-rTW/strings.xml +++ b/app/src/main/res/values-zh-rTW/strings.xml @@ -216,6 +216,35 @@ 靠左對齊 靠右對齊 伸展 + 自訂操作 + 選擇貼文操作按鈕的順序與可見性 + 貼文操作 + 自訂操作 + 選擇哪些操作顯示在按鈕列、更多選單或保持隱藏 + 按鈕列 + 更多選單 + 隱藏 + 沒有操作 + 移至按鈕列 + 移至更多選單 + 隱藏操作 + 上移 + 下移 + 回覆 + 留言 + 轉發 + 引用 + 喜歡 + 反應 + 翻譯 + 書籤 + 收藏 + 分享 + Fx 分享 + 刪除 + 檢舉 + 靜音使用者 + 封鎖使用者 顯示媒體 在貼文中顯示媒體附件 顯示數字 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0521139eb..829645a6e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -292,10 +292,39 @@ Left aligned Right aligned Stretch + Customize actions + Choose the order and visibility of post action buttons Show media Show media attachments in posts Show numbers Show counts below each post + Post actions + Customize actions + Choose which actions appear in the row, More menu, or stay hidden + Button row + More menu + Hidden + No actions + Move to button row + Move to More menu + Hide action + Move up + Move down + Reply + Comment + Repost + Quote + Like + React + Translate + Bookmark + Favorite + Share + Fx share + Delete + Report + Mute user + Block user Show sensitive content Always reveal sensitive media in posts Show content behind content warnings diff --git a/compose-ui/src/commonMain/kotlin/dev/dimension/flare/ui/component/status/CommonStatusComponent.kt b/compose-ui/src/commonMain/kotlin/dev/dimension/flare/ui/component/status/CommonStatusComponent.kt index c1a7059d8..928ebacb6 100644 --- a/compose-ui/src/commonMain/kotlin/dev/dimension/flare/ui/component/status/CommonStatusComponent.kt +++ b/compose-ui/src/commonMain/kotlin/dev/dimension/flare/ui/component/status/CommonStatusComponent.kt @@ -105,6 +105,7 @@ import dev.dimension.flare.compose.ui.user_unblock import dev.dimension.flare.compose.ui.user_unmute import dev.dimension.flare.compose.ui.vote import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.applyPostActionLayout import dev.dimension.flare.data.model.PostActionStyle import dev.dimension.flare.ui.component.AdaptiveGrid import dev.dimension.flare.ui.component.AvatarComponent @@ -899,6 +900,10 @@ internal fun StatusActions( modifier: Modifier = Modifier, ) { val appearanceSettings = LocalTimelineAppearance.current + val displayItems = + remember(items, appearanceSettings.postActionLayout) { + items.applyPostActionLayout(appearanceSettings.postActionLayout) + } val haptics = LocalHapticFeedback.current val launcher = LocalUriHandler.current Row( @@ -913,8 +918,8 @@ internal fun StatusActions( }, // horizontalArrangement = Arrangement.spacedBy(4.dp), ) { - items.fastForEachIndexed { index, action -> - if (index == items.lastIndex && appearanceSettings.postActionStyle == PostActionStyle.LeftAligned) { + displayItems.fastForEachIndexed { index, action -> + if (index == displayItems.lastIndex && appearanceSettings.postActionStyle == PostActionStyle.LeftAligned) { Spacer(modifier = Modifier.weight(1f)) } when (action) { @@ -927,7 +932,7 @@ internal fun StatusActions( color = action.displayItem.color?.toComposeColor() ?: PlatformContentColor.current, - withTextMinWidth = index != items.lastIndex, + withTextMinWidth = action.displayItem.count != null && index != displayItems.lastIndex, ) { closeMenu, isMenuShown -> action.actions.fastForEach { subActions -> when (subActions) { @@ -956,7 +961,7 @@ internal fun StatusActions( // Fallback or handle null number = action.count, color = action.color?.toComposeColor() ?: PlatformContentColor.current, - withTextMinWidth = index != items.lastIndex, + withTextMinWidth = action.count != null && index != displayItems.lastIndex, onClicked = { action.onClicked.let { onClick -> haptics.performHapticFeedback(HapticFeedbackType.ContextClick) diff --git a/desktopApp/src/main/composeResources/values-af-rZA/strings.xml b/desktopApp/src/main/composeResources/values-af-rZA/strings.xml index ef362696f..2c303f78d 100644 --- a/desktopApp/src/main/composeResources/values-af-rZA/strings.xml +++ b/desktopApp/src/main/composeResources/values-af-rZA/strings.xml @@ -268,6 +268,35 @@ Links belyn Regs belyn Strek + Pas aksies aan + Kies die volgorde en sigbaarheid van plasingaksieknoppies + Plasingaksies + Pas aksies aan + Kies watter aksies in die ry, Meer-kieslys of versteek bly + Knoppiery + Meer-kieslys + Versteek + Geen aksies + Skuif na knoppiery + Skuif na Meer-kieslys + Versteek aksie + Skuif op + Skuif af + Antwoord + Kommentaar + Herplasing + Haal aan + Hou van + Voeg reaksie by + Vertaal + Voeg boekmerk by + Gunsteling + Deel + Deel via FxEmbed + Vee uit + Rapporteer + Demp + Blokkeer Absolute tydstempel Wys presiese tydstempels op plasings Wys bronplatform-ikone diff --git a/desktopApp/src/main/composeResources/values-ar-rSA/strings.xml b/desktopApp/src/main/composeResources/values-ar-rSA/strings.xml index 9d4402dd5..2b6b42eab 100644 --- a/desktopApp/src/main/composeResources/values-ar-rSA/strings.xml +++ b/desktopApp/src/main/composeResources/values-ar-rSA/strings.xml @@ -336,6 +336,35 @@ محاذاة لليسار محاذاة لليمين تمدد + تخصيص الإجراءات + اختر ترتيب أزرار إجراءات المنشور وإمكانية ظهورها + إجراءات المنشور + تخصيص الإجراءات + اختر الإجراءات التي تظهر في صف الأزرار أو قائمة المزيد أو تبقى مخفية + صف الأزرار + قائمة المزيد + مخفي + لا توجد إجراءات + نقل إلى صف الأزرار + نقل إلى قائمة المزيد + إخفاء الإجراء + نقل لأعلى + نقل لأسفل + رد + تعليق + إعادة نشر + اقتباس + إعجاب + إضافة تفاعل + ترجمة + إضافة إشارة مرجعية + تفضيل + مشاركة + مشاركة عبر FxEmbed + حذف + إبلاغ + كتم + حظر طابع زمني مطلق إظهار أوقات محددة على المنشورات إظهار أيقونات المنصة المصدر diff --git a/desktopApp/src/main/composeResources/values-bg-rBG/strings.xml b/desktopApp/src/main/composeResources/values-bg-rBG/strings.xml index 983b9d425..cf3987281 100644 --- a/desktopApp/src/main/composeResources/values-bg-rBG/strings.xml +++ b/desktopApp/src/main/composeResources/values-bg-rBG/strings.xml @@ -271,6 +271,35 @@ Подравнени вляво Подравнени вдясно Разпънати + Персонализиране на действията + Изберете реда и видимостта на бутоните за действия с публикация + Действия с публикация + Персонализиране на действията + Изберете кои действия да се показват в реда с бутони, менюто „Още“ или да останат скрити + Ред с бутони + Меню „Още“ + Скрити + Няма действия + Преместване в реда с бутони + Преместване в менюто „Още“ + Скриване на действието + Преместване нагоре + Преместване надолу + Отговор + Коментар + Повторно публикуване + Цитат + Харесване + Добавяне на реакция + Превод + Добавяне на отметка + Любими + Споделяне + Споделяне чрез FxEmbed + Изтриване + Докладване + Заглушаване + Блокиране Абсолютно времево клеймо Показване на точни часове в публикациите Показване на икони на платформата източник diff --git a/desktopApp/src/main/composeResources/values-ca-rES/strings.xml b/desktopApp/src/main/composeResources/values-ca-rES/strings.xml index 7dcbcbd09..235ae8071 100644 --- a/desktopApp/src/main/composeResources/values-ca-rES/strings.xml +++ b/desktopApp/src/main/composeResources/values-ca-rES/strings.xml @@ -269,6 +269,35 @@ Alineat a l\'esquerra Alineat a la dreta Estirat + Personalitza les accions + Tria l’ordre i la visibilitat dels botons d’acció de la publicació + Accions de la publicació + Personalitza les accions + Tria quines accions apareixen a la fila, al menú Més o romanen amagades + Fila de botons + Menú Més + Amagat + Cap acció + Mou a la fila de botons + Mou al menú Més + Amaga l’acció + Mou amunt + Mou avall + Respon + Comenta + Torna a publicar + Cita + M\'agrada + Afegeix una reacció + Traduir + Afegeix un marcador + Preferit + Compartir + Compartir via FxEmbed + Suprimeix + Informa + Silenciar + Bloquejar Marca horària absoluta Mostra marques horàries exactes a les publicacions Mostra les icones de la plataforma d\'origen diff --git a/desktopApp/src/main/composeResources/values-cs-rCZ/strings.xml b/desktopApp/src/main/composeResources/values-cs-rCZ/strings.xml index b3d34da37..5c7267b25 100644 --- a/desktopApp/src/main/composeResources/values-cs-rCZ/strings.xml +++ b/desktopApp/src/main/composeResources/values-cs-rCZ/strings.xml @@ -334,6 +334,35 @@ Zarovnané doleva Zarovnané doprava Roztažené + Přizpůsobit akce + Zvolte pořadí a viditelnost tlačítek akcí příspěvku + Akce příspěvku + Přizpůsobit akce + Zvolte, které akce se zobrazí v řádku, v nabídce Další nebo zůstanou skryté + Řádek tlačítek + Nabídka Další + Skryté + Žádné akce + Přesunout do řádku tlačítek + Přesunout do nabídky Další + Skrýt akci + Přesunout nahoru + Přesunout dolů + Odpovědět + Komentovat + Sdílet + Citovat + To se mi líbí + Přidat reakci + Přeložit + Přidat záložku + Oblíbené + Sdílet + Sdílet přes FxEmbed + Smazat + Nahlásit + Skrýt + Blokovat Absolutní časové razítko Zobrazovat u příspěvků přesné časy Zobrazit ikony zdrojové platformy diff --git a/desktopApp/src/main/composeResources/values-da-rDK/strings.xml b/desktopApp/src/main/composeResources/values-da-rDK/strings.xml index 7ddf76575..94bd5a5f4 100644 --- a/desktopApp/src/main/composeResources/values-da-rDK/strings.xml +++ b/desktopApp/src/main/composeResources/values-da-rDK/strings.xml @@ -332,6 +332,35 @@ Venstrestillet Højrestillet Stræk + Tilpas handlinger + Vælg rækkefølge og synlighed for handlingsknapper til indlæg + Indlægshandlinger + Tilpas handlinger + Vælg hvilke handlinger der vises i rækken, menuen Mere eller forbliver skjult + Knaprække + Menuen Mere + Skjult + Ingen handlinger + Flyt til knaprække + Flyt til menuen Mere + Skjul handling + Flyt op + Flyt ned + Svar + Kommentar + Genopslå + Citér + Synes godt om + Tilføj reaktion + Oversæt + Tilføj bogmærke + Favorit + Del + Del via FxEmbed + Slet + Anmeld + Lydløs + Bloker Absolut tidsstempel Vis nøjagtige tidsstempler på opslag Vis kildeplatform-ikoner diff --git a/desktopApp/src/main/composeResources/values-de-rDE/strings.xml b/desktopApp/src/main/composeResources/values-de-rDE/strings.xml index 62df3f698..5d402c3a5 100644 --- a/desktopApp/src/main/composeResources/values-de-rDE/strings.xml +++ b/desktopApp/src/main/composeResources/values-de-rDE/strings.xml @@ -332,6 +332,35 @@ Linksbündig Rechtsbündig Gestreckt + Aktionen anpassen + Reihenfolge und Sichtbarkeit der Beitragsaktions-Schaltflächen wählen + Beitragsaktionen + Aktionen anpassen + Wähle, welche Aktionen in der Zeile, im Mehr-Menü oder ausgeblendet bleiben + Schaltflächenzeile + Mehr-Menü + Ausgeblendet + Keine Aktionen + In die Schaltflächenzeile verschieben + Ins Mehr-Menü verschieben + Aktion ausblenden + Nach oben verschieben + Nach unten verschieben + Antworten + Kommentieren + Teilen + Zitieren + Gefällt mir + Reaktion hinzufügen + Übersetzen + Lesezeichen hinzufügen + Favorisieren + Teilen + Über FxEmbed teilen + Löschen + Melden + Stummschalten + Blockieren Absoluter Zeitstempel Genaue Zeitstempel in Beiträgen anzeigen Icons der Quellplattform anzeigen diff --git a/desktopApp/src/main/composeResources/values-el-rGR/strings.xml b/desktopApp/src/main/composeResources/values-el-rGR/strings.xml index e508ef5d2..0e66c64dc 100644 --- a/desktopApp/src/main/composeResources/values-el-rGR/strings.xml +++ b/desktopApp/src/main/composeResources/values-el-rGR/strings.xml @@ -317,6 +317,35 @@ Στοίχιση αριστερά Στοίχιση δεξιά Πλήρες πλάτος (Stretch) + Προσαρμογή ενεργειών + Επιλέξτε τη σειρά και την ορατότητα των κουμπιών ενεργειών δημοσίευσης + Ενέργειες δημοσίευσης + Προσαρμογή ενεργειών + Επιλέξτε ποιες ενέργειες εμφανίζονται στη σειρά, στο μενού Περισσότερα ή παραμένουν κρυφές + Σειρά κουμπιών + Μενού Περισσότερα + Κρυφό + Καμία ενέργεια + Μετακίνηση στη σειρά κουμπιών + Μετακίνηση στο μενού Περισσότερα + Απόκρυψη ενέργειας + Μετακίνηση πάνω + Μετακίνηση κάτω + Απάντηση + Σχόλιο + Αναδημοσίευση + Παράθεση + Μου αρέσει + Προσθήκη αντίδρασης + Μετάφραση + Προσθήκη σελιδοδείκτη + Αγαπημένο + Κοινοποίηση + Κοινοποίηση μέσω FxEmbed + Διαγραφή + Αναφορά + Σίγαση + Αποκλεισμός Απόλυτη χρονική σήμανση Εμφάνιση ακριβών χρονικών σημάνσεων στις δημοσιεύσεις Εμφάνιση εικονιδίων πλατφόρμας πηγής diff --git a/desktopApp/src/main/composeResources/values-es-rES/strings.xml b/desktopApp/src/main/composeResources/values-es-rES/strings.xml index 2ad41cce1..471e158a2 100644 --- a/desktopApp/src/main/composeResources/values-es-rES/strings.xml +++ b/desktopApp/src/main/composeResources/values-es-rES/strings.xml @@ -332,6 +332,35 @@ Alineado a la izquierda Alineado a la derecha Estirado + Personalizar acciones + Elige el orden y la visibilidad de los botones de acción de la publicación + Acciones de la publicación + Personalizar acciones + Elige qué acciones aparecen en la fila, en el menú Más o permanecen ocultas + Fila de botones + Menú Más + Oculto + Sin acciones + Mover a la fila de botones + Mover al menú Más + Ocultar acción + Mover arriba + Mover abajo + Responder + Comentar + Repostear + Citar + Me gusta + Añadir reacción + Traducir + Añadir marcador + Favorito + Compartir + Compartir vía FxEmbed + Eliminar + Reportar + Silenciar + Bloquear Marca de tiempo absoluta Mostrar marcas de tiempo exactas en las publicaciones Mostrar iconos de la plataforma de origen diff --git a/desktopApp/src/main/composeResources/values-fi-rFI/strings.xml b/desktopApp/src/main/composeResources/values-fi-rFI/strings.xml index 8ad625582..11cb4fad8 100644 --- a/desktopApp/src/main/composeResources/values-fi-rFI/strings.xml +++ b/desktopApp/src/main/composeResources/values-fi-rFI/strings.xml @@ -325,6 +325,35 @@ Vasemmalle tasattu Oikealle tasattu Venytytty + Mukauta toimintoja + Valitse julkaisun toimintopainikkeiden järjestys ja näkyvyys + Julkaisun toiminnot + Mukauta toimintoja + Valitse mitkä toiminnot näkyvät rivillä, Lisää-valikossa tai pysyvät piilossa + Painikerivi + Lisää-valikko + Piilotettu + Ei toimintoja + Siirrä painikeriville + Siirrä Lisää-valikkoon + Piilota toiminto + Siirrä ylös + Siirrä alas + Vastaa + Kommentti + Uudelleenpostaa + Lainaa + Tykkää + Lisää reaktio + Käännä + Lisää kirjanmerkki + Suosikki + Jaa + Jaa FxEmbedin kautta + Poista + Raportoi + Mykistä + Estä Absoluuttinen aikaleima Näytä tarkat aikaleimat postauksissa Näytä lähdepalvelimen kuvakkeet diff --git a/desktopApp/src/main/composeResources/values-fr-rFR/strings.xml b/desktopApp/src/main/composeResources/values-fr-rFR/strings.xml index 13892e933..5f4a7deba 100644 --- a/desktopApp/src/main/composeResources/values-fr-rFR/strings.xml +++ b/desktopApp/src/main/composeResources/values-fr-rFR/strings.xml @@ -327,6 +327,35 @@ Aligné à gauche Aligné à droite Étiré + Personnaliser les actions + Choisir l’ordre et la visibilité des boutons d’action du post + Actions du post + Personnaliser les actions + Choisir les actions affichées dans la rangée, le menu Plus ou masquées + Rangée de boutons + Menu Plus + Masqué + Aucune action + Déplacer vers la rangée de boutons + Déplacer vers le menu Plus + Masquer l’action + Monter + Descendre + Répondre + Commentaire + Republier + Citer + J\'aime + Ajouter une réaction + Traduire + Ajouter aux marque-pages + Favori + Partager + Partager via FxEmbed + Supprimer + Signaler + Masquer + Bloquer Horodatage absolu Afficher l\'heure exacte sur les messages Afficher les icônes de la plateforme source diff --git a/desktopApp/src/main/composeResources/values-hu-rHU/strings.xml b/desktopApp/src/main/composeResources/values-hu-rHU/strings.xml index da94e6f71..128f7bbf4 100644 --- a/desktopApp/src/main/composeResources/values-hu-rHU/strings.xml +++ b/desktopApp/src/main/composeResources/values-hu-rHU/strings.xml @@ -273,6 +273,35 @@ Balra zárt Jobbra zárt Kifeszített + Műveletek testreszabása + Válaszd ki a bejegyzésművelet-gombok sorrendjét és láthatóságát + Bejegyzésműveletek + Műveletek testreszabása + Válaszd ki, mely műveletek jelenjenek meg a sorban, a Továbbiak menüben vagy maradjanak rejtve + Gombsor + Továbbiak menü + Rejtett + Nincsenek műveletek + Áthelyezés a gombsorba + Áthelyezés a Továbbiak menübe + Művelet elrejtése + Mozgatás fel + Mozgatás le + Válasz + Hozzászólás + Újraküldés + Idézés + Kedvelés + Reakció hozzáadása + Fordítás + Könyvjelző hozzáadása + Kedvenc + Megosztás + Megosztás FxEmbed-en keresztül + Törlés + Jelentés + Némítás + Tiltás Abszolút időbélyeg Pontos időbélyegek megjelenítése a bejegyzéseken Forrásplatform ikonok megjelenítése diff --git a/desktopApp/src/main/composeResources/values-it-rIT/strings.xml b/desktopApp/src/main/composeResources/values-it-rIT/strings.xml index 02e363bb8..134f8989f 100644 --- a/desktopApp/src/main/composeResources/values-it-rIT/strings.xml +++ b/desktopApp/src/main/composeResources/values-it-rIT/strings.xml @@ -332,6 +332,35 @@ Allineato a sinistra Allineato a destra Esteso + Personalizza azioni + Scegli ordine e visibilità dei pulsanti di azione del post + Azioni del post + Personalizza azioni + Scegli quali azioni appaiono nella riga, nel menu Altro o restano nascoste + Riga pulsanti + Menu Altro + Nascosto + Nessuna azione + Sposta nella riga pulsanti + Sposta nel menu Altro + Nascondi azione + Sposta su + Sposta giù + Rispondi + Commenta + Ripubblica + Cita + Mi piace + Aggiungi reazione + Traduci + Aggiungi segnalibro + Preferito + Condividi + Condividi tramite FxEmbed + Elimina + Segnala + Silenzia + Blocca Timestamp assoluto Mostra l\'orario esatto sui post Mostra icone della piattaforma sorgente diff --git a/desktopApp/src/main/composeResources/values-iw-rIL/strings.xml b/desktopApp/src/main/composeResources/values-iw-rIL/strings.xml index 49ad1036e..050ae48e0 100644 --- a/desktopApp/src/main/composeResources/values-iw-rIL/strings.xml +++ b/desktopApp/src/main/composeResources/values-iw-rIL/strings.xml @@ -273,6 +273,35 @@ יישור לשמאל יישור לימין מתיחה + התאמה אישית של פעולות + בחר את הסדר והנראות של כפתורי הפעולה בפוסט + פעולות פוסט + התאמה אישית של פעולות + בחר אילו פעולות יופיעו בשורה, בתפריט עוד או יישארו מוסתרות + שורת כפתורים + תפריט עוד + מוסתר + אין פעולות + העבר לשורת הכפתורים + העבר לתפריט עוד + הסתר פעולה + העבר למעלה + העבר למטה + תגובה + תגובה + פרסום מחדש + ציטוט + לייק + הוספת תגובה (Reaction) + תרגום + הוספת סימניה + מועדף + שיתוף + שתף דרך FxEmbed + מחיקה + דיווח + השתקה + חסימה חותמת זמן מוחלטת הצגת חותמות זמן מדויקות על פוסטים הצגת סמלי פלטפורמת המקור diff --git a/desktopApp/src/main/composeResources/values-ja-rJP/strings.xml b/desktopApp/src/main/composeResources/values-ja-rJP/strings.xml index 68e150d18..c24da825f 100644 --- a/desktopApp/src/main/composeResources/values-ja-rJP/strings.xml +++ b/desktopApp/src/main/composeResources/values-ja-rJP/strings.xml @@ -313,6 +313,35 @@ 左寄せ 右寄せ 引き伸ばし + アクションをカスタマイズ + 投稿アクションボタンの順序と表示を選択 + 投稿アクション + アクションをカスタマイズ + ボタン行、その他メニュー、非表示にするアクションを選択 + ボタン行 + その他メニュー + 非表示 + アクションなし + ボタン行へ移動 + その他メニューへ移動 + アクションを非表示 + 上へ移動 + 下へ移動 + 返信 + コメント + リポスト + 引用 + いいね + リアクション + 翻訳 + ブックマーク + お気に入り + 共有 + Fx共有 + 削除 + 通報 + ユーザーをミュート + ユーザーをブロック 絶対時間表示 投稿に正確な時刻を表示します プラットフォームアイコンを表示 diff --git a/desktopApp/src/main/composeResources/values-ko-rKR/strings.xml b/desktopApp/src/main/composeResources/values-ko-rKR/strings.xml index 9c13a6ef1..335657932 100644 --- a/desktopApp/src/main/composeResources/values-ko-rKR/strings.xml +++ b/desktopApp/src/main/composeResources/values-ko-rKR/strings.xml @@ -271,6 +271,35 @@ 왼쪽 정렬 오른쪽 정렬 늘이기 + 동작 사용자화 + 게시물 동작 버튼의 순서와 표시 여부 선택 + 게시물 동작 + 동작 사용자화 + 버튼 행, 더 보기 메뉴 또는 숨김에 표시할 동작 선택 + 버튼 행 + 더 보기 메뉴 + 숨김 + 동작 없음 + 버튼 행으로 이동 + 더 보기 메뉴로 이동 + 동작 숨기기 + 위로 이동 + 아래로 이동 + 답글 + 댓글 + 다시 게시 + 인용하기 + 좋아요 + 반응 + 번역 + 북마크 + 즐겨찾기 + 공유 + Fx 공유 + 삭제 + 신고하기 + 사용자 뮤트 + 사용자 차단 절대 타임스탬프 게시물에 정확한 타임스탬프 표시 소스 플랫폼 아이콘 표시 diff --git a/desktopApp/src/main/composeResources/values-nl-rNL/strings.xml b/desktopApp/src/main/composeResources/values-nl-rNL/strings.xml index b5c447881..4775a3b78 100644 --- a/desktopApp/src/main/composeResources/values-nl-rNL/strings.xml +++ b/desktopApp/src/main/composeResources/values-nl-rNL/strings.xml @@ -324,6 +324,35 @@ Links uitgelijnd Rechts uitgelijnd Uitrekken + Acties aanpassen + Kies de volgorde en zichtbaarheid van actieknoppen voor berichten + Berichtacties + Acties aanpassen + Kies welke acties in de rij, het Meer-menu of verborgen blijven + Knoppenrij + Meer-menu + Verborgen + Geen acties + Naar knoppenrij verplaatsen + Naar Meer-menu verplaatsen + Actie verbergen + Omhoog verplaatsen + Omlaag verplaatsen + Beantwoorden + Reactie + Doorsturen + Citeren + Liken + Reactie toevoegen + Vertalen + Bladwijzer toevoegen + Favoriet + Delen + Delen via FxEmbed + Verwijderen + Rapporteren + Dempen + Blokkeren Absolute tijdstempel Exacte tijdstempels op berichten weergeven Bronplatform-iconen weergeven diff --git a/desktopApp/src/main/composeResources/values-no-rNO/strings.xml b/desktopApp/src/main/composeResources/values-no-rNO/strings.xml index 65b418a30..b28192910 100644 --- a/desktopApp/src/main/composeResources/values-no-rNO/strings.xml +++ b/desktopApp/src/main/composeResources/values-no-rNO/strings.xml @@ -325,6 +325,35 @@ Venstrejustert Høyrejustert Strekk + Tilpass handlinger + Velg rekkefølge og synlighet for handlingsknapper på innlegg + Innleggshandlinger + Tilpass handlinger + Velg hvilke handlinger som vises i raden, Mer-menyen eller holdes skjult + Knapperad + Mer-meny + Skjult + Ingen handlinger + Flytt til knapperad + Flytt til Mer-meny + Skjul handling + Flytt opp + Flytt ned + Svar + Kommenter + Videresend + Siter + Lik + Legg til reaksjon + Oversett + Legg til bokmerke + Favoritt + Del + Del via FxEmbed + Slett + Rapporter + Demp + Blokker Absolutt tidsstempel Vis nøyaktige tidsstempler på innlegg Vis ikoner for kildeplattform diff --git a/desktopApp/src/main/composeResources/values-pl-rPL/strings.xml b/desktopApp/src/main/composeResources/values-pl-rPL/strings.xml index 244ffb792..129085777 100644 --- a/desktopApp/src/main/composeResources/values-pl-rPL/strings.xml +++ b/desktopApp/src/main/composeResources/values-pl-rPL/strings.xml @@ -333,6 +333,35 @@ Wyrównany do lewej Wyrównany do prawej Rozciągnięty + Dostosuj akcje + Wybierz kolejność i widoczność przycisków akcji wpisu + Akcje wpisu + Dostosuj akcje + Wybierz, które akcje pojawią się w wierszu, menu Więcej albo pozostaną ukryte + Wiersz przycisków + Menu Więcej + Ukryte + Brak akcji + Przenieś do wiersza przycisków + Przenieś do menu Więcej + Ukryj akcję + Przenieś w górę + Przenieś w dół + Odpowiedz + Komentarz + Podaj dalej + Cytuj + Lubię to + Dodaj reakcję + Przetłumacz + Dodaj zakładkę + Ulubione + Udostępnij + Udostępnij przez FxEmbed + Usuń + Zgłoś + Wycisz + Zablokuj Bezwzględny znacznik czasu Pokazuj dokładny czas przy wpisach Pokaż ikony platform źródłowych diff --git a/desktopApp/src/main/composeResources/values-pt-rBR/strings.xml b/desktopApp/src/main/composeResources/values-pt-rBR/strings.xml index 383c6268d..1a049049a 100644 --- a/desktopApp/src/main/composeResources/values-pt-rBR/strings.xml +++ b/desktopApp/src/main/composeResources/values-pt-rBR/strings.xml @@ -332,6 +332,35 @@ Alinhado à esquerda Alinhado à direita Esticar + Personalizar ações + Escolha a ordem e a visibilidade dos botões de ação da postagem + Ações da postagem + Personalizar ações + Escolha quais ações aparecem na linha, no menu Mais ou ficam ocultas + Linha de botões + Menu Mais + Oculto + Nenhuma ação + Mover para a linha de botões + Mover para o menu Mais + Ocultar ação + Mover para cima + Mover para baixo + Responder + Comentário + Republicar + Citar + Curtir + Adicionar reação + Traduzir + Salvar post + Favoritar + Compartilhar + Compartilhar via FxEmbed + Excluir + Denunciar + Silenciar + Bloquear Data/hora absoluta Mostrar data/hora exata nos posts Mostrar ícones da plataforma de origem diff --git a/desktopApp/src/main/composeResources/values-pt-rPT/strings.xml b/desktopApp/src/main/composeResources/values-pt-rPT/strings.xml index 074b5c354..86786749c 100644 --- a/desktopApp/src/main/composeResources/values-pt-rPT/strings.xml +++ b/desktopApp/src/main/composeResources/values-pt-rPT/strings.xml @@ -332,6 +332,35 @@ Alinhado à esquerda Alinhado à direita Esticar + Personalizar ações + Escolha a ordem e a visibilidade dos botões de ação da publicação + Ações da publicação + Personalizar ações + Escolha que ações aparecem na linha, no menu Mais ou ficam ocultas + Linha de botões + Menu Mais + Oculto + Sem ações + Mover para a linha de botões + Mover para o menu Mais + Ocultar ação + Mover para cima + Mover para baixo + Responder + Comentário + Republicar + Citar + Gostar + Adicionar reação + Traduzir + Guardar publicação + Favorito + Partilhar + Partilhar via FxEmbed + Eliminar + Denunciar + Silenciar + Bloquear Data/hora absoluta Mostrar data/hora exata nas publicações Mostrar ícones da plataforma de origem diff --git a/desktopApp/src/main/composeResources/values-ro-rRO/strings.xml b/desktopApp/src/main/composeResources/values-ro-rRO/strings.xml index 9afdf52cd..f8d0ce398 100644 --- a/desktopApp/src/main/composeResources/values-ro-rRO/strings.xml +++ b/desktopApp/src/main/composeResources/values-ro-rRO/strings.xml @@ -333,6 +333,35 @@ Aliniate la stânga Aliniate la dreapta Întinse (stretch) + Personalizează acțiunile + Alege ordinea și vizibilitatea butoanelor de acțiune ale postării + Acțiuni postare + Personalizează acțiunile + Alege ce acțiuni apar pe rând, în meniul Mai multe sau rămân ascunse + Rând de butoane + Meniu Mai multe + Ascuns + Nicio acțiune + Mută în rândul de butoane + Mută în meniul Mai multe + Ascunde acțiunea + Mută în sus + Mută în jos + Răspunde + Comentariu + Repostează + Citează + Apreciază + Adaugă reacție + Tradu + Adaugă marcaj + Favorit + Partajează + Partajează prin FxEmbed + Șterge + Raportează + Ignoră + Blochează Marcaj temporal absolut Arată marcajele temporale exacte pe postări Arată pictogramele platformei sursă diff --git a/desktopApp/src/main/composeResources/values-ru-rRU/strings.xml b/desktopApp/src/main/composeResources/values-ru-rRU/strings.xml index 160a403ac..9efdbf80f 100644 --- a/desktopApp/src/main/composeResources/values-ru-rRU/strings.xml +++ b/desktopApp/src/main/composeResources/values-ru-rRU/strings.xml @@ -330,6 +330,35 @@ Выровнять слева Выровнять вправо Растянуть + Настроить действия + Выберите порядок и видимость кнопок действий поста + Действия поста + Настроить действия + Выберите, какие действия будут в строке, меню Ещё или останутся скрытыми + Строка кнопок + Меню Ещё + Скрыто + Нет действий + Переместить в строку кнопок + Переместить в меню Ещё + Скрыть действие + Переместить вверх + Переместить вниз + Ответ + Комментарий + Репост + Цитата + Лайкнуть + Добавить реакцию + Перевести + Добавить закладку + В избранное + Поделиться + Поделиться через FxEmbed + Удалить + Пожаловаться + Игнорировать + Заблокировать Абсолютная метка времени Показывать абсолютную метку времени на записях Показать логотип платформы diff --git a/desktopApp/src/main/composeResources/values-sr-rSP/strings.xml b/desktopApp/src/main/composeResources/values-sr-rSP/strings.xml index f8afb06da..f0529003f 100644 --- a/desktopApp/src/main/composeResources/values-sr-rSP/strings.xml +++ b/desktopApp/src/main/composeResources/values-sr-rSP/strings.xml @@ -271,6 +271,35 @@ Poravnato levo Poravnato desno Razvučeno + Прилагоди радње + Изаберите редослед и видљивост дугмади радњи објаве + Радње објаве + Прилагоди радње + Изаберите које радње се приказују у реду, менију Још или остају скривене + Ред дугмади + Мени Још + Скривено + Нема радњи + Премести у ред дугмади + Премести у мени Још + Сакриј радњу + Премести нагоре + Премести надоле + Odgovori + Komentariši + Ponovo objavi + Citiraj + Sviđa mi se + Dodaj reakciju + Prevedi + Dodaj obeleživač + Omiljeno + Podeli + Podeli putem FxEmbed + Obriši + Prijavi + Utišaj + Blokiraj Apsolutna vremenska oznaka Prikaži tačne vremenske oznake na objavama Prikaži ikonice izvornih platformi diff --git a/desktopApp/src/main/composeResources/values-sv-rSE/strings.xml b/desktopApp/src/main/composeResources/values-sv-rSE/strings.xml index 6ce4440ff..2bac07196 100644 --- a/desktopApp/src/main/composeResources/values-sv-rSE/strings.xml +++ b/desktopApp/src/main/composeResources/values-sv-rSE/strings.xml @@ -332,6 +332,35 @@ Vänsterjusterad Högerjusterad Sträck ut + Anpassa åtgärder + Välj ordning och synlighet för inläggets åtgärdsknappar + Inläggsåtgärder + Anpassa åtgärder + Välj vilka åtgärder som visas i raden, menyn Mer eller förblir dolda + Knapprad + Menyn Mer + Dold + Inga åtgärder + Flytta till knapprad + Flytta till menyn Mer + Dölj åtgärd + Flytta upp + Flytta ned + Svara + Kommentera + Reposta + Citera + Gilla + Lägg till reaktion + Översätt + Lägg till bokmärke + Favoritmarkera + Dela + Dela via FxEmbed + Radera + Rapportera + Tysta + Blockera Absolut tidsstämpel Visa exakta tidsstämplar på inlägg Visa källplattformsikoner diff --git a/desktopApp/src/main/composeResources/values-tr-rTR/strings.xml b/desktopApp/src/main/composeResources/values-tr-rTR/strings.xml index 1ce27ee0e..4cdbea33a 100644 --- a/desktopApp/src/main/composeResources/values-tr-rTR/strings.xml +++ b/desktopApp/src/main/composeResources/values-tr-rTR/strings.xml @@ -271,6 +271,35 @@ Sola hizalı Sağa hizalı Uzatılmış + Eylemleri özelleştir + Gönderi eylem düğmelerinin sırasını ve görünürlüğünü seçin + Gönderi eylemleri + Eylemleri özelleştir + Hangi eylemlerin satırda, Daha Fazla menüsünde ya da gizli kalacağını seçin + Düğme satırı + Daha Fazla menüsü + Gizli + Eylem yok + Düğme satırına taşı + Daha Fazla menüsüne taşı + Eylemi gizle + Yukarı taşı + Aşağı taşı + Yanıtla + Yorum yap + Yeniden paylaş + Alıntıla + Beğen + Tepki ekle + Çevir + Yer işareti ekle + Favorilere ekle + Paylaş + FxEmbed ile paylaş + Sil + Bildir + Sessize al + Engelle Mutlak zaman damgası Gönderilerde tam zaman damgalarını göster Kaynak platform simgelerini göster diff --git a/desktopApp/src/main/composeResources/values-uk-rUA/strings.xml b/desktopApp/src/main/composeResources/values-uk-rUA/strings.xml index 138b3cd61..516a8617c 100644 --- a/desktopApp/src/main/composeResources/values-uk-rUA/strings.xml +++ b/desktopApp/src/main/composeResources/values-uk-rUA/strings.xml @@ -324,6 +324,35 @@ За лівим краєм За правим краєм Розтягнути + Налаштувати дії + Виберіть порядок і видимість кнопок дій допису + Дії допису + Налаштувати дії + Виберіть, які дії відображати в рядку, меню Ще або залишити прихованими + Рядок кнопок + Меню Ще + Приховано + Немає дій + Перемістити до рядка кнопок + Перемістити до меню Ще + Приховати дію + Перемістити вгору + Перемістити вниз + Відповісти + Коментувати + Поширити + Цитата + Вподобати + Додати реакцію + Перекласти + Додати до закладок + У вибране + Поділитися + Поділитися через FxEmbed + Видалити + Поскаржитися + Приглушити + Заблокувати Абсолютна мітка часу Показувати точний час у дописах Показувати іконки джерел diff --git a/desktopApp/src/main/composeResources/values-vi-rVN/strings.xml b/desktopApp/src/main/composeResources/values-vi-rVN/strings.xml index f3703e1dc..a761cfd7b 100644 --- a/desktopApp/src/main/composeResources/values-vi-rVN/strings.xml +++ b/desktopApp/src/main/composeResources/values-vi-rVN/strings.xml @@ -269,6 +269,35 @@ Căn lề trái Căn lề phải Giãn đều + Tùy chỉnh hành động + Chọn thứ tự và khả năng hiển thị của các nút hành động bài đăng + Hành động bài đăng + Tùy chỉnh hành động + Chọn hành động xuất hiện trong hàng, menu Thêm hoặc vẫn bị ẩn + Hàng nút + Menu Thêm + Đã ẩn + Không có hành động + Chuyển vào hàng nút + Chuyển vào menu Thêm + Ẩn hành động + Chuyển lên + Chuyển xuống + Phản hồi + Bình luận + Đăng lại + Trích dẫn + Thích + Thêm phản ứng + Dịch + Thêm dấu trang + Yêu thích + Chia sẻ + Chia sẻ qua FxEmbed + Xóa + Báo cáo + Ẩn + Chặn Dấu thời gian tuyệt đối Hiển thị thời gian chính xác trên các bài đăng Hiển thị biểu tượng nền tảng nguồn diff --git a/desktopApp/src/main/composeResources/values-zh-rCN/strings.xml b/desktopApp/src/main/composeResources/values-zh-rCN/strings.xml index 2c9366a36..4613a8fb3 100644 --- a/desktopApp/src/main/composeResources/values-zh-rCN/strings.xml +++ b/desktopApp/src/main/composeResources/values-zh-rCN/strings.xml @@ -326,6 +326,35 @@ 左对齐 右对齐 伸展 + 自定义操作 + 选择动态操作按钮的顺序和可见性 + 动态操作 + 自定义操作 + 选择哪些操作显示在按钮行、更多菜单或保持隐藏 + 按钮行 + 更多菜单 + 隐藏 + 无操作 + 移动到按钮行 + 移动到更多菜单 + 隐藏操作 + 上移 + 下移 + 回复 + 评论 + 转发 + 引用 + + 回应 + 翻译 + 书签 + 收藏 + 分享 + Fx 分享 + 删除 + 举报 + 静音用户 + 屏蔽用户 绝对时间戳 在内容上显示准确的具体时间 显示来源平台图标 diff --git a/desktopApp/src/main/composeResources/values-zh-rTW/strings.xml b/desktopApp/src/main/composeResources/values-zh-rTW/strings.xml index 5e25c3edf..9aa364411 100644 --- a/desktopApp/src/main/composeResources/values-zh-rTW/strings.xml +++ b/desktopApp/src/main/composeResources/values-zh-rTW/strings.xml @@ -273,6 +273,35 @@ 靠左對齊 靠右對齊 延伸 + 自訂操作 + 選擇貼文操作按鈕的順序與可見性 + 貼文操作 + 自訂操作 + 選擇哪些操作顯示在按鈕列、更多選單或保持隱藏 + 按鈕列 + 更多選單 + 隱藏 + 沒有操作 + 移至按鈕列 + 移至更多選單 + 隱藏操作 + 上移 + 下移 + 回覆 + 留言 + 轉發 + 引用 + 喜歡 + 反應 + 翻譯 + 書籤 + 收藏 + 分享 + Fx 分享 + 刪除 + 檢舉 + 靜音使用者 + 封鎖使用者 絕對時間戳記 在貼文上顯示確切的時間 顯示來源平台圖示 diff --git a/desktopApp/src/main/composeResources/values/strings.xml b/desktopApp/src/main/composeResources/values/strings.xml index 94bec4aec..4bb8eb595 100644 --- a/desktopApp/src/main/composeResources/values/strings.xml +++ b/desktopApp/src/main/composeResources/values/strings.xml @@ -358,6 +358,35 @@ Left aligned Right aligned Stretch + Customize actions + Choose the order and visibility of post action buttons + Post actions + Customize actions + Choose which actions appear in the row, More menu, or stay hidden + Button row + More menu + Hidden + No actions + Move to button row + Move to More menu + Hide action + Move up + Move down + Reply + Comment + Repost + Quote + Like + React + Translate + Bookmark + Favorite + Share + Fx share + Delete + Report + Mute user + Block user Absolute timestamp Show exact timestamps on posts diff --git a/desktopApp/src/main/kotlin/dev/dimension/flare/ui/route/Route.kt b/desktopApp/src/main/kotlin/dev/dimension/flare/ui/route/Route.kt index 89d7b722a..d5d64ba36 100644 --- a/desktopApp/src/main/kotlin/dev/dimension/flare/ui/route/Route.kt +++ b/desktopApp/src/main/kotlin/dev/dimension/flare/ui/route/Route.kt @@ -46,6 +46,8 @@ internal sealed interface Route : NavKey { data object Settings : ScreenRoute + data object PostActionLayout : ScreenRoute + data object DraftBox : ScreenRoute data class Profile( diff --git a/desktopApp/src/main/kotlin/dev/dimension/flare/ui/route/Router.kt b/desktopApp/src/main/kotlin/dev/dimension/flare/ui/route/Router.kt index eac4b8205..9b8f836e2 100644 --- a/desktopApp/src/main/kotlin/dev/dimension/flare/ui/route/Router.kt +++ b/desktopApp/src/main/kotlin/dev/dimension/flare/ui/route/Router.kt @@ -94,6 +94,7 @@ import dev.dimension.flare.ui.screen.settings.AppLoggingScreen import dev.dimension.flare.ui.screen.settings.LocalCacheScreen import dev.dimension.flare.ui.screen.settings.LocalHistoryAgentScreen import dev.dimension.flare.ui.screen.settings.NostrRelaysScreen +import dev.dimension.flare.ui.screen.settings.PostActionLayoutScreen import dev.dimension.flare.ui.screen.settings.SettingsScreen import dev.dimension.flare.ui.screen.status.StatusScreen import dev.dimension.flare.ui.screen.status.VVOCommentScreen @@ -688,9 +689,16 @@ internal fun Router( toNostrRelays = { navigate(Route.NostrRelays(it)) }, + toPostActionLayout = { + navigate(Route.PostActionLayout) + }, ) } + entry { + PostActionLayoutScreen() + } + entry { DraftBoxScreen( onEdit = { groupId -> diff --git a/desktopApp/src/main/kotlin/dev/dimension/flare/ui/screen/settings/PostActionLayoutScreen.kt b/desktopApp/src/main/kotlin/dev/dimension/flare/ui/screen/settings/PostActionLayoutScreen.kt new file mode 100644 index 000000000..99aff7824 --- /dev/null +++ b/desktopApp/src/main/kotlin/dev/dimension/flare/ui/screen/settings/PostActionLayoutScreen.kt @@ -0,0 +1,493 @@ +package dev.dimension.flare.ui.screen.settings + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyItemScope +import androidx.compose.foundation.lazy.LazyListScope +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import compose.icons.FontAwesomeIcons +import compose.icons.fontawesomeicons.Solid +import compose.icons.fontawesomeicons.solid.Bars +import compose.icons.fontawesomeicons.solid.EllipsisVertical +import dev.dimension.flare.LocalWindowPadding +import dev.dimension.flare.Res +import dev.dimension.flare.data.datasource.microblog.PostActionFamily +import dev.dimension.flare.data.datasource.microblog.PostActionLayoutConfig +import dev.dimension.flare.data.datasource.microblog.PostActionLayoutHelpers +import dev.dimension.flare.data.datasource.microblog.PostActionPlacement +import dev.dimension.flare.data.model.appearance.AppearanceKeys +import dev.dimension.flare.data.repository.SettingsRepository +import dev.dimension.flare.settings_post_action_family_block_user +import dev.dimension.flare.settings_post_action_family_bookmark +import dev.dimension.flare.settings_post_action_family_comment +import dev.dimension.flare.settings_post_action_family_delete +import dev.dimension.flare.settings_post_action_family_favorite +import dev.dimension.flare.settings_post_action_family_fx_share +import dev.dimension.flare.settings_post_action_family_like +import dev.dimension.flare.settings_post_action_family_mute_user +import dev.dimension.flare.settings_post_action_family_quote +import dev.dimension.flare.settings_post_action_family_react +import dev.dimension.flare.settings_post_action_family_reply +import dev.dimension.flare.settings_post_action_family_report +import dev.dimension.flare.settings_post_action_family_repost +import dev.dimension.flare.settings_post_action_family_share +import dev.dimension.flare.settings_post_action_family_translate +import dev.dimension.flare.settings_post_action_layout_button_row +import dev.dimension.flare.settings_post_action_layout_empty +import dev.dimension.flare.settings_post_action_layout_enable +import dev.dimension.flare.settings_post_action_layout_enable_description +import dev.dimension.flare.settings_post_action_layout_hidden +import dev.dimension.flare.settings_post_action_layout_hide_action +import dev.dimension.flare.settings_post_action_layout_more_menu +import dev.dimension.flare.settings_post_action_layout_move_down +import dev.dimension.flare.settings_post_action_layout_move_to_button_row +import dev.dimension.flare.settings_post_action_layout_move_to_more_menu +import dev.dimension.flare.settings_post_action_layout_move_up +import dev.dimension.flare.tab_settings_drag +import dev.dimension.flare.ui.common.plus +import dev.dimension.flare.ui.component.FAIcon +import dev.dimension.flare.ui.component.FlareScrollBar +import dev.dimension.flare.ui.component.Header +import dev.dimension.flare.ui.component.LocalTimelineAppearance +import dev.dimension.flare.ui.component.status.StatusItem +import dev.dimension.flare.ui.component.toImageVector +import dev.dimension.flare.ui.model.PostActionLayoutPreviewHelper +import dev.dimension.flare.ui.model.UiIcon +import dev.dimension.flare.ui.model.onSuccess +import dev.dimension.flare.ui.presenter.invoke +import dev.dimension.flare.ui.presenter.settings.AppearancePresenter +import dev.dimension.flare.ui.presenter.settings.AppearanceState +import io.github.composefluent.component.CardExpanderItem +import io.github.composefluent.component.FlyoutPlacement +import io.github.composefluent.component.MenuFlyout +import io.github.composefluent.component.MenuFlyoutItem +import io.github.composefluent.component.SubtleButton +import io.github.composefluent.component.Switcher +import io.github.composefluent.component.Text +import io.github.composefluent.surface.Card +import kotlinx.coroutines.launch +import moe.tlaster.precompose.molecule.producePresenter +import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.stringResource +import org.koin.compose.koinInject +import sh.calvin.reorderable.ReorderableItem +import sh.calvin.reorderable.ReorderableLazyListState +import sh.calvin.reorderable.rememberReorderableLazyListState + +@Composable +internal fun PostActionLayoutScreen() { + val state by producePresenter { postActionLayoutPresenter() } + val timelineAppearance = LocalTimelineAppearance.current + val persistedConfig = PostActionLayoutHelpers.normalizedForEdit(timelineAppearance.postActionLayout) + var config by remember { mutableStateOf(persistedConfig) } + var isDraggingAction by remember { mutableStateOf(false) } + val listState = rememberLazyListState() + val reorderableState = + rememberReorderableLazyListState(listState) { from, to -> + val fromFamily = from.key.asPostActionFamilyOrNull() ?: return@rememberReorderableLazyListState + val toFamily = to.key.asPostActionFamilyOrNull() ?: return@rememberReorderableLazyListState + val fromPlacement = PostActionLayoutHelpers.placementOf(config, fromFamily) + val toPlacement = PostActionLayoutHelpers.placementOf(config, toFamily) + if (fromPlacement == toPlacement) { + updateConfig( + value = PostActionLayoutHelpers.moveWithin(config, fromPlacement, fromFamily, toFamily), + persist = false, + updateLocal = { config = it }, + persistConfig = state::updatePostActionLayout, + ) + } + } + + LaunchedEffect(PostActionLayoutHelpers.signature(persistedConfig)) { + if (!isDraggingAction) { + config = persistedConfig + } + } + + fun commitConfig() { + updateConfig( + value = config, + persist = true, + updateLocal = { config = it }, + persistConfig = state::updatePostActionLayout, + ) + } + + FlareScrollBar(listState) { + LazyColumn( + state = listState, + modifier = + Modifier + .fillMaxSize() + .padding(horizontal = 24.dp), + verticalArrangement = Arrangement.spacedBy(2.dp), + contentPadding = LocalWindowPadding.current + PaddingValues(vertical = 12.dp), + ) { + item(key = "preview") { + state.sampleStatus.onSuccess { sample -> + Card( + modifier = Modifier.fillMaxWidth(), + ) { + CompositionLocalProvider( + LocalTimelineAppearance provides timelineAppearance.copy(postActionLayout = config), + ) { + StatusItem( + item = PostActionLayoutPreviewHelper.withPreviewActions(sample), + ) + } + } + } + } + item(key = "enabled") { + CardExpanderItem( + icon = null, + heading = { + Text(stringResource(Res.string.settings_post_action_layout_enable)) + }, + caption = { + Text(stringResource(Res.string.settings_post_action_layout_enable_description)) + }, + trailing = { + Switcher( + checked = config.enabled, + onCheckStateChange = { + updateConfig( + value = PostActionLayoutHelpers.withEnabled(config, it), + updateLocal = { value -> config = value }, + persistConfig = state::updatePostActionLayout, + ) + }, + textBefore = true, + ) + }, + ) + } + + if (config.enabled) { + postActionSection( + title = Res.string.settings_post_action_layout_button_row, + placement = PostActionPlacement.ButtonRow, + config = config, + onConfigChanged = { + updateConfig( + value = it, + updateLocal = { value -> config = value }, + persistConfig = state::updatePostActionLayout, + ) + }, + onDragStarted = { + isDraggingAction = true + }, + onDragStopped = { + isDraggingAction = false + commitConfig() + }, + reorderableState = reorderableState, + ) + postActionSection( + title = Res.string.settings_post_action_layout_more_menu, + placement = PostActionPlacement.MoreMenu, + config = config, + onConfigChanged = { + updateConfig( + value = it, + updateLocal = { value -> config = value }, + persistConfig = state::updatePostActionLayout, + ) + }, + onDragStarted = { + isDraggingAction = true + }, + onDragStopped = { + isDraggingAction = false + commitConfig() + }, + reorderableState = reorderableState, + ) + postActionSection( + title = Res.string.settings_post_action_layout_hidden, + placement = PostActionPlacement.Hidden, + config = config, + onConfigChanged = { + updateConfig( + value = it, + updateLocal = { value -> config = value }, + persistConfig = state::updatePostActionLayout, + ) + }, + onDragStarted = { + isDraggingAction = true + }, + onDragStopped = { + isDraggingAction = false + commitConfig() + }, + reorderableState = reorderableState, + ) + } + } + } +} + +private fun LazyListScope.postActionSection( + title: StringResource, + placement: PostActionPlacement, + config: PostActionLayoutConfig, + onConfigChanged: (PostActionLayoutConfig) -> Unit, + onDragStarted: () -> Unit, + onDragStopped: () -> Unit, + reorderableState: ReorderableLazyListState, +) { + val families = PostActionLayoutHelpers.familiesFor(config, placement) + item(key = "${placement.name}_header") { + Header( + text = stringResource(title), + modifier = Modifier.padding(top = 16.dp), + ) + } + if (families.isEmpty()) { + item(key = "${placement.name}_empty") { + CardExpanderItem( + icon = null, + heading = { + Text(stringResource(Res.string.settings_post_action_layout_empty)) + }, + ) + } + } else { + itemsIndexed( + items = families, + key = { _, family -> family.saveableKey }, + ) { index, family -> + PostActionFamilyRow( + family = family, + placement = placement, + index = index, + totalCount = families.size, + config = config, + onConfigChanged = onConfigChanged, + onDragStarted = onDragStarted, + onDragStopped = onDragStopped, + reorderableState = reorderableState, + ) + } + } +} + +@Composable +private fun LazyItemScope.PostActionFamilyRow( + family: PostActionFamily, + placement: PostActionPlacement, + index: Int, + totalCount: Int, + config: PostActionLayoutConfig, + onConfigChanged: (PostActionLayoutConfig) -> Unit, + onDragStarted: () -> Unit, + onDragStopped: () -> Unit, + reorderableState: ReorderableLazyListState, +) { + ReorderableItem(reorderableState, key = family.saveableKey) { _ -> + var isFlyoutVisible by remember { mutableStateOf(false) } + CardExpanderItem( + icon = { + FAIcon( + imageVector = family.icon.toImageVector(), + contentDescription = null, + ) + }, + heading = { + Text(stringResource(family.label)) + }, + trailing = { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + ) { + SubtleButton( + modifier = + Modifier.draggableHandle( + onDragStarted = { + onDragStarted() + }, + onDragStopped = onDragStopped, + ), + onClick = {}, + iconOnly = true, + ) { + FAIcon( + imageVector = FontAwesomeIcons.Solid.Bars, + contentDescription = stringResource(Res.string.tab_settings_drag), + ) + } + Box { + SubtleButton( + onClick = { + isFlyoutVisible = true + }, + iconOnly = true, + ) { + FAIcon( + imageVector = FontAwesomeIcons.Solid.EllipsisVertical, + contentDescription = null, + ) + } + MenuFlyout( + visible = isFlyoutVisible, + onDismissRequest = { + isFlyoutVisible = false + }, + placement = FlyoutPlacement.BottomAlignedEnd, + ) { + postActionPlacements.forEach { target -> + if (target != placement) { + MenuFlyoutItem( + text = { Text(stringResource(target.moveLabel)) }, + onClick = { + isFlyoutVisible = false + onConfigChanged( + PostActionLayoutHelpers.moveTo(config, family, target), + ) + }, + ) + } + } + if (index > 0) { + MenuFlyoutItem( + text = { Text(stringResource(Res.string.settings_post_action_layout_move_up)) }, + onClick = { + isFlyoutVisible = false + onConfigChanged(PostActionLayoutHelpers.moveBy(config, family, -1)) + }, + ) + } + if (index < totalCount - 1) { + MenuFlyoutItem( + text = { Text(stringResource(Res.string.settings_post_action_layout_move_down)) }, + onClick = { + isFlyoutVisible = false + onConfigChanged(PostActionLayoutHelpers.moveBy(config, family, 1)) + }, + ) + } + } + } + } + }, + ) + } +} + +@Composable +private fun postActionLayoutPresenter() = + run { + val scope = rememberCoroutineScope() + val settingsRepository = koinInject() + val appearanceState = remember { AppearancePresenter() }.invoke() + object : AppearanceState by appearanceState { + fun updatePostActionLayout(value: PostActionLayoutConfig) { + scope.launch { + settingsRepository.updateAppearance(AppearanceKeys.PostActionLayout, value) + } + } + } + } + +private fun updateConfig( + value: PostActionLayoutConfig, + persist: Boolean = true, + updateLocal: (PostActionLayoutConfig) -> Unit, + persistConfig: (PostActionLayoutConfig) -> Unit, +) { + val normalized = PostActionLayoutHelpers.normalizedForEdit(value) + updateLocal(normalized) + if (persist) { + persistConfig(normalized) + } +} + +private val postActionPlacements: List = + listOf( + PostActionPlacement.ButtonRow, + PostActionPlacement.MoreMenu, + PostActionPlacement.Hidden, + ) + +private const val POST_ACTION_FAMILY_KEY_PREFIX = "post_action_family:" + +private val PostActionFamily.saveableKey: String + get() = "$POST_ACTION_FAMILY_KEY_PREFIX$name" + +private fun Any?.asPostActionFamilyOrNull(): PostActionFamily? { + val key = this as? String ?: return null + if (!key.startsWith(POST_ACTION_FAMILY_KEY_PREFIX)) return null + return runCatching { + enumValueOf(key.removePrefix(POST_ACTION_FAMILY_KEY_PREFIX)) + }.getOrNull() +} + +private val PostActionPlacement.moveLabel: StringResource + get() = + when (this) { + PostActionPlacement.ButtonRow -> Res.string.settings_post_action_layout_move_to_button_row + PostActionPlacement.MoreMenu -> Res.string.settings_post_action_layout_move_to_more_menu + PostActionPlacement.Hidden -> Res.string.settings_post_action_layout_hide_action + } + +private val PostActionFamily.icon: UiIcon + get() = + when (this) { + PostActionFamily.Reply -> UiIcon.Reply + PostActionFamily.Comment -> UiIcon.Comment + PostActionFamily.Repost -> UiIcon.Retweet + PostActionFamily.Quote -> UiIcon.Quote + PostActionFamily.Like -> UiIcon.Like + PostActionFamily.React -> UiIcon.React + PostActionFamily.Translate -> UiIcon.Translate + PostActionFamily.Bookmark -> UiIcon.Bookmark + PostActionFamily.Favorite -> UiIcon.Favourite + PostActionFamily.Share -> UiIcon.Share + PostActionFamily.FxShare -> UiIcon.Share + PostActionFamily.Delete -> UiIcon.Delete + PostActionFamily.Report -> UiIcon.Report + PostActionFamily.MuteUser -> UiIcon.Mute + PostActionFamily.BlockUser -> UiIcon.Block + } + +private val PostActionFamily.label: StringResource + get() = + when (this) { + PostActionFamily.Reply -> Res.string.settings_post_action_family_reply + PostActionFamily.Comment -> Res.string.settings_post_action_family_comment + PostActionFamily.Repost -> Res.string.settings_post_action_family_repost + PostActionFamily.Quote -> Res.string.settings_post_action_family_quote + PostActionFamily.Like -> Res.string.settings_post_action_family_like + PostActionFamily.React -> Res.string.settings_post_action_family_react + PostActionFamily.Translate -> Res.string.settings_post_action_family_translate + PostActionFamily.Bookmark -> Res.string.settings_post_action_family_bookmark + PostActionFamily.Favorite -> Res.string.settings_post_action_family_favorite + PostActionFamily.Share -> Res.string.settings_post_action_family_share + PostActionFamily.FxShare -> Res.string.settings_post_action_family_fx_share + PostActionFamily.Delete -> Res.string.settings_post_action_family_delete + PostActionFamily.Report -> Res.string.settings_post_action_family_report + PostActionFamily.MuteUser -> Res.string.settings_post_action_family_mute_user + PostActionFamily.BlockUser -> Res.string.settings_post_action_family_block_user + } diff --git a/desktopApp/src/main/kotlin/dev/dimension/flare/ui/screen/settings/SettingsScreen.kt b/desktopApp/src/main/kotlin/dev/dimension/flare/ui/screen/settings/SettingsScreen.kt index a9f5b68d5..e70c8d676 100644 --- a/desktopApp/src/main/kotlin/dev/dimension/flare/ui/screen/settings/SettingsScreen.kt +++ b/desktopApp/src/main/kotlin/dev/dimension/flare/ui/screen/settings/SettingsScreen.kt @@ -158,6 +158,8 @@ import dev.dimension.flare.settings_appearance_layout_group_subtitle import dev.dimension.flare.settings_appearance_layout_group_title import dev.dimension.flare.settings_appearance_media_group_subtitle import dev.dimension.flare.settings_appearance_media_group_title +import dev.dimension.flare.settings_appearance_post_action_layout +import dev.dimension.flare.settings_appearance_post_action_layout_description import dev.dimension.flare.settings_appearance_post_action_style import dev.dimension.flare.settings_appearance_post_action_style_description import dev.dimension.flare.settings_appearance_post_action_style_hidden @@ -304,6 +306,7 @@ internal fun SettingsScreen( toAppLog: () -> Unit, toRSSManagement: () -> Unit, toNostrRelays: (MicroBlogKey) -> Unit, + toPostActionLayout: () -> Unit, ) { val window = LocalComposeWindow.current val state by producePresenter { @@ -884,6 +887,22 @@ internal fun SettingsScreen( ) }, ) + ExpanderItemSeparator() + ExpanderItem( + heading = { + Text(stringResource(Res.string.settings_appearance_post_action_layout)) + }, + caption = { + Text(stringResource(Res.string.settings_appearance_post_action_layout_description)) + }, + trailing = { + FAIcon( + FontAwesomeIcons.Solid.AngleRight, + contentDescription = null, + ) + }, + modifier = Modifier.clickable(onClick = toPostActionLayout), + ) AnimatedVisibility(LocalTimelineAppearance.current.postActionStyle != PostActionStyle.Hidden) { Column { ExpanderItemSeparator() diff --git a/iosApp/flare/Localizable.xcstrings b/iosApp/flare/Localizable.xcstrings index 0354fc30e..3c2ac9101 100644 --- a/iosApp/flare/Localizable.xcstrings +++ b/iosApp/flare/Localizable.xcstrings @@ -29662,6 +29662,390 @@ } } }, + "Choose the order and visibility of post action buttons" : { + "comment" : "A description of the post action buttons.", + "isCommentAutoGenerated" : true, + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kies die volgorde en sigbaarheid van plasingaksieknoppies" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اختر ترتيب أزرار إجراءات المنشور وإمكانية ظهورها" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изберете реда и видимостта на бутоните за действия с публикация" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tria l’ordre i la visibilitat dels botons d’acció de la publicació" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zvolte pořadí a viditelnost tlačítek akcí příspěvku" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vælg rækkefølge og synlighed for handlingsknapper til indlæg" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reihenfolge und Sichtbarkeit der Beitragsaktions-Schaltflächen wählen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επιλέξτε τη σειρά και την ορατότητα των κουμπιών ενεργειών δημοσίευσης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choose the order and visibility of post action buttons" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elige el orden y la visibilidad de los botones de acción de la publicación" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse julkaisun toimintopainikkeiden järjestys ja näkyvyys" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choisir l’ordre et la visibilité des boutons d’action du post" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Válaszd ki a bejegyzésművelet-gombok sorrendjét és láthatóságát" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scegli ordine e visibilità dei pulsanti di azione del post" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בחר את הסדר והנראות של כפתורי הפעולה בפוסט" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "投稿アクションボタンの順序と表示を選択" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "게시물 동작 버튼의 순서와 표시 여부 선택" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kies de volgorde en zichtbaarheid van actieknoppen voor berichten" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg rekkefølge og synlighet for handlingsknapper på innlegg" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wybierz kolejność i widoczność przycisków akcji wpisu" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Escolha a ordem e a visibilidade dos botões de ação da postagem" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Escolha a ordem e a visibilidade dos botões de ação da publicação" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alege ordinea și vizibilitatea butoanelor de acțiune ale postării" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выберите порядок и видимость кнопок действий поста" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изаберите редослед и видљивост дугмади радњи објаве" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Välj ordning och synlighet för inläggets åtgärdsknappar" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gönderi eylem düğmelerinin sırasını ve görünürlüğünü seçin" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Виберіть порядок і видимість кнопок дій допису" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chọn thứ tự và khả năng hiển thị của các nút hành động bài đăng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "选择动态操作按钮的顺序和可见性" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "選擇貼文操作按鈕的順序與可見性" + } + } + } + }, + "Choose which actions appear in the row, More menu, or stay hidden" : { + "comment" : "A description of the toggle that lets you choose which actions appear in the row, More menu, or stay hidden.", + "isCommentAutoGenerated" : true, + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kies watter aksies in die ry, Meer-kieslys of versteek bly" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اختر الإجراءات التي تظهر في صف الأزرار أو قائمة المزيد أو تبقى مخفية" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изберете кои действия да се показват в реда с бутони, менюто „Още“ или да останат скрити" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tria quines accions apareixen a la fila, al menú Més o romanen amagades" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zvolte, které akce se zobrazí v řádku, v nabídce Další nebo zůstanou skryté" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vælg hvilke handlinger der vises i rækken, menuen Mere eller forbliver skjult" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wähle, welche Aktionen in der Zeile, im Mehr-Menü oder ausgeblendet bleiben" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Επιλέξτε ποιες ενέργειες εμφανίζονται στη σειρά, στο μενού Περισσότερα ή παραμένουν κρυφές" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choose which actions appear in the row, More menu, or stay hidden" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elige qué acciones aparecen en la fila, en el menú Más o permanecen ocultas" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Valitse mitkä toiminnot näkyvät rivillä, Lisää-valikossa tai pysyvät piilossa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Choisir les actions affichées dans la rangée, le menu Plus ou masquées" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Válaszd ki, mely műveletek jelenjenek meg a sorban, a Továbbiak menüben vagy maradjanak rejtve" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Scegli quali azioni appaiono nella riga, nel menu Altro o restano nascoste" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "בחר אילו פעולות יופיעו בשורה, בתפריט עוד או יישארו מוסתרות" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボタン行、その他メニュー、非表示にするアクションを選択" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "버튼 행, 더 보기 메뉴 또는 숨김에 표시할 동작 선택" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kies welke acties in de rij, het Meer-menu of verborgen blijven" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Velg hvilke handlinger som vises i raden, Mer-menyen eller holdes skjult" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wybierz, które akcje pojawią się w wierszu, menu Więcej albo pozostaną ukryte" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Escolha quais ações aparecem na linha, no menu Mais ou ficam ocultas" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Escolha que ações aparecem na linha, no menu Mais ou ficam ocultas" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alege ce acțiuni apar pe rând, în meniul Mai multe sau rămân ascunse" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Выберите, какие действия будут в строке, меню Ещё или останутся скрытыми" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изаберите које радње се приказују у реду, менију Још или остају скривене" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Välj vilka åtgärder som visas i raden, menyn Mer eller förblir dolda" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hangi eylemlerin satırda, Daha Fazla menüsünde ya da gizli kalacağını seçin" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Виберіть, які дії відображати в рядку, меню Ще або залишити прихованими" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chọn hành động xuất hiện trong hàng, menu Thêm hoặc vẫn bị ẩn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "选择哪些操作显示在按钮行、更多菜单或保持隐藏" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "選擇哪些操作顯示在按鈕列、更多選單或保持隱藏" + } + } + } + }, "Choose which service handles translation" : { "localizations" : { "af" : { @@ -32896,6 +33280,198 @@ } } }, + "Customize actions" : { + "comment" : "A label for a screen that allows the user to customize the order and visibility of post action buttons.", + "isCommentAutoGenerated" : true, + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pas aksies aan" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تخصيص الإجراءات" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Персонализиране на действията" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personalitza les accions" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přizpůsobit akce" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilpas handlinger" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktionen anpassen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προσαρμογή ενεργειών" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Customize actions" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personalizar acciones" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mukauta toimintoja" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personnaliser les actions" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Műveletek testreszabása" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personalizza azioni" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "התאמה אישית של פעולות" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アクションをカスタマイズ" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "동작 사용자화" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acties aanpassen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilpass handlinger" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dostosuj akcje" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personalizar ações" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personalizar ações" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Personalizează acțiunile" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Настроить действия" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Прилагоди радње" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anpassa åtgärder" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eylemleri özelleştir" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Налаштувати дії" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tùy chỉnh hành động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "自定义操作" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "自訂操作" + } + } + } + }, "Dark" : { "localizations" : { "af" : { @@ -34417,196 +34993,196 @@ } }, "Delete" : { - "localizations" : { - "af" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verwyder" - } - }, - "ar" : { - "stringUnit" : { - "state" : "translated", - "value" : "حذف" - } - }, - "bg" : { - "stringUnit" : { - "state" : "translated", - "value" : "Изтриване" - } - }, - "ca" : { - "stringUnit" : { - "state" : "translated", - "value" : "Esborrar" - } - }, - "cs" : { - "stringUnit" : { - "state" : "translated", - "value" : "Vymazat" - } - }, - "da" : { - "stringUnit" : { - "state" : "translated", - "value" : "Slet" - } - }, - "de" : { - "stringUnit" : { - "state" : "translated", - "value" : "Löschen" - } - }, - "el" : { - "stringUnit" : { - "state" : "translated", - "value" : "Διαγραφή" - } - }, - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Delete" - } - }, - "es" : { - "stringUnit" : { - "state" : "translated", - "value" : "Eliminar" - } - }, - "fi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Poista" - } - }, - "fr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Supprimer" - } - }, - "he" : { - "stringUnit" : { - "state" : "translated", - "value" : "מחק" - } - }, - "hu" : { - "stringUnit" : { - "state" : "translated", - "value" : "Törlés" - } - }, - "it" : { - "stringUnit" : { - "state" : "translated", - "value" : "Elimina" - } - }, - "ja" : { - "stringUnit" : { - "state" : "translated", - "value" : "削除" - } - }, - "ko" : { - "stringUnit" : { - "state" : "translated", - "value" : "삭제" - } - }, - "nb" : { - "stringUnit" : { - "state" : "translated", - "value" : "Slett" - } - }, - "nl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Verwijderen" - } - }, - "pl" : { - "stringUnit" : { - "state" : "translated", - "value" : "Usuń" - } - }, - "pt" : { - "stringUnit" : { - "state" : "translated", - "value" : "Excluir" - } - }, - "pt-BR" : { - "stringUnit" : { - "state" : "translated", - "value" : "Excluir" - } - }, - "ro" : { - "stringUnit" : { - "state" : "translated", - "value" : "Șterge" - } - }, - "ru" : { - "stringUnit" : { - "state" : "translated", - "value" : "Удалить" - } - }, - "sr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Обриши" - } - }, - "sv" : { - "stringUnit" : { - "state" : "translated", - "value" : "Ta bort" - } - }, - "tr" : { - "stringUnit" : { - "state" : "translated", - "value" : "Sil" - } - }, - "uk" : { - "stringUnit" : { - "state" : "translated", - "value" : "Видалити" - } - }, - "vi" : { - "stringUnit" : { - "state" : "translated", - "value" : "Xóa" - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "删除" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "刪除" - } - } - } - }, - "delete_alert_title" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vee uit" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حذف" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Изтриване" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elimina" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Smazat" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slet" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Löschen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Διαγραφή" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delete" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Poista" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Supprimer" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מחק" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Törlés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Elimina" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "削除" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "삭제" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Slett" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verwijderen" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Usuń" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eliminar" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Excluir" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Șterge" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Удалить" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Обриши" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ta bort" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sil" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Видалити" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Xóa" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "删除" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "刪除" + } + } + } + }, + "delete_alert_title" : { "localizations" : { "af" : { "stringUnit" : { @@ -99022,6 +99598,390 @@ } } }, + "Move down" : { + "comment" : "A button that moves a post action family down in the list.", + "isCommentAutoGenerated" : true, + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skuif af" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نقل لأسفل" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Преместване надолу" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mou avall" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přesunout dolů" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flyt ned" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach unten verschieben" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μετακίνηση κάτω" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Move down" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover abajo" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siirrä alas" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Descendre" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mozgatás le" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sposta giù" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "העבר למטה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "下へ移動" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "아래로 이동" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omlaag verplaatsen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flytt ned" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Przenieś w dół" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover para baixo" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover para baixo" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mută în jos" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переместить вниз" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Премести надоле" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flytta ned" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aşağı taşı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемістити вниз" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chuyển xuống" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "下移" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "下移" + } + } + } + }, + "Move up" : { + "comment" : "A button that moves a post action up in the list.", + "isCommentAutoGenerated" : true, + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skuif op" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نقل لأعلى" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Преместване нагоре" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mou amunt" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přesunout nahoru" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flyt op" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nach oben verschieben" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μετακίνηση πάνω" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Move up" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover arriba" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siirrä ylös" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Monter" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mozgatás fel" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sposta su" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "העבר למעלה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "上へ移動" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "위로 이동" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omhoog verplaatsen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flytt opp" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Przenieś w górę" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover para cima" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover para cima" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mută în sus" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переместить вверх" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Премести нагоре" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flytta upp" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yukarı taşı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемістити вгору" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chuyển lên" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "上移" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "上移" + } + } + } + }, "mute" : { "localizations" : { "af" : { @@ -99973,6 +100933,198 @@ } } }, + "No actions" : { + "comment" : "A message displayed when a placement has no actions.", + "isCommentAutoGenerated" : true, + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen aksies" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "لا توجد إجراءات" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Няма действия" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cap acció" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Žádné akce" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen handlinger" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Keine Aktionen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Καμία ενέργεια" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "No actions" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sin acciones" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ei toimintoja" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aucune action" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nincsenek műveletek" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nessuna azione" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אין פעולות" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アクションなし" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "동작 없음" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Geen acties" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ingen handlinger" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Brak akcji" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nenhuma ação" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sem ações" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nicio acțiune" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нет действий" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Нема радњи" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inga åtgärder" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eylem yok" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Немає дій" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Không có hành động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "无操作" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "沒有操作" + } + } + } + }, "No Drafts" : { "localizations" : { "af" : { @@ -102825,6 +103977,50 @@ } } }, + "notification_download_started" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cache is not ready. Download started." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "缓存尚未准备好,已开始下载。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "快取尚未準備好,已開始下載。" + } + } + } + }, + "notification_download_video_started" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Video cache is not ready. Download started." + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "视频缓存尚未准备好,已开始下载。" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "影片快取尚未準備好,已開始下載。" + } + } + } + }, "notification_login_expired" : { "localizations" : { "af" : { @@ -103397,52 +104593,6 @@ } } }, - "notification_download_started" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Cache is not ready. Download started." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "缓存尚未准备好,已开始下载。" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "快取尚未準備好,已開始下載。" - } - } - } - }, - "notification_download_video_started" : { - "extractionState" : "stale", - "localizations" : { - "en" : { - "stringUnit" : { - "state" : "translated", - "value" : "Video cache is not ready. Download started." - } - }, - "zh-Hans" : { - "stringUnit" : { - "state" : "translated", - "value" : "视频缓存尚未准备好,已开始下载。" - } - }, - "zh-Hant" : { - "stringUnit" : { - "state" : "translated", - "value" : "影片快取尚未準備好,已開始下載。" - } - } - } - }, "notification_save_video_error" : { "extractionState" : "stale", "localizations" : { @@ -108254,6 +109404,198 @@ } } }, + "Post actions" : { + "comment" : "A screen that lets the user configure the layout of post actions.", + "isCommentAutoGenerated" : true, + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Plasingaksies" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إجراءات المنشور" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Действия с публикация" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Accions de la publicació" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akce příspěvku" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Indlægshandlinger" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beitragsaktionen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ενέργειες δημοσίευσης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Post actions" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acciones de la publicación" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Julkaisun toiminnot" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actions du post" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bejegyzésműveletek" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Azioni del post" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "פעולות פוסט" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "投稿アクション" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "게시물 동작" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Berichtacties" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Innleggshandlinger" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Akcje wpisu" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ações da postagem" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ações da publicação" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Acțiuni postare" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Действия поста" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Радње објаве" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Inläggsåtgärder" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gönderi eylemleri" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Дії допису" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hành động bài đăng" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "动态操作" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "貼文操作" + } + } + } + }, "posts_title" : { "localizations" : { "af" : { @@ -146374,7 +147716,7 @@ "ca" : { "stringUnit" : { "state" : "translated", - "value" : "Tradueix" + "value" : "Traduir" } }, "cs" : { @@ -146428,7 +147770,7 @@ "he" : { "stringUnit" : { "state" : "translated", - "value" : "תרגם" + "value" : "תרגום" } }, "hu" : { @@ -146500,7 +147842,7 @@ "sr" : { "stringUnit" : { "state" : "translated", - "value" : "Преведи" + "value" : "Prevedi" } }, "sv" : { @@ -150159,7 +151501,3617 @@ } } } + }, + "Button row" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Knoppiery" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "صف الأزرار" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ред с бутони" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fila de botons" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Řádek tlačítek" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Knaprække" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Schaltflächenzeile" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σειρά κουμπιών" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Button row" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fila de botones" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Painikerivi" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rangée de boutons" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gombsor" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Riga pulsanti" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שורת כפתורים" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボタン行" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "버튼 행" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Knoppenrij" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Knapperad" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wiersz przycisków" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Linha de botões" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Linha de botões" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rând de butoane" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Строка кнопок" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ред дугмади" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Knapprad" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Düğme satırı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Рядок кнопок" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hàng nút" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "按钮行" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "按鈕列" + } + } + } + }, + "More menu" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meer-kieslys" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "قائمة المزيد" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Меню „Още“" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menú Més" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nabídka Další" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menuen Mere" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mehr-Menü" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μενού Περισσότερα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "More menu" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menú Más" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lisää-valikko" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu Plus" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Továbbiak menü" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu Altro" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "תפריט עוד" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "その他メニュー" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "더 보기 메뉴" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meer-menu" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mer-meny" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu Więcej" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu Mais" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu Mais" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Meniu Mai multe" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Меню Ещё" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Мени Још" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menyn Mer" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daha Fazla menüsü" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Меню Ще" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Menu Thêm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "更多菜单" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "更多選單" + } + } + } + }, + "Hidden" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versteek" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مخفي" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скрити" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Amagat" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skryté" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjult" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ausgeblendet" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κρυφό" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hidden" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oculto" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Piilotettu" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masqué" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rejtett" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nascosto" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מוסתר" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "非表示" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "숨김" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Verborgen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjult" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukryte" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oculto" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oculto" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ascuns" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скрыто" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скривено" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dold" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gizli" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Приховано" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đã ẩn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "隐藏" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "隱藏" + } + } + } + }, + "Move to button row" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skuif na knoppiery" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نقل إلى صف الأزرار" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Преместване в реда с бутони" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mou a la fila de botons" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přesunout do řádku tlačítek" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flyt til knaprække" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "In die Schaltflächenzeile verschieben" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μετακίνηση στη σειρά κουμπιών" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Move to button row" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover a la fila de botones" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siirrä painikeriville" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Déplacer vers la rangée de boutons" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Áthelyezés a gombsorba" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sposta nella riga pulsanti" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "העבר לשורת הכפתורים" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ボタン行へ移動" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "버튼 행으로 이동" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naar knoppenrij verplaatsen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flytt til knapperad" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Przenieś do wiersza przycisków" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover para a linha de botões" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover para a linha de botões" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mută în rândul de butoane" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переместить в строку кнопок" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Премести у ред дугмади" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flytta till knapprad" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Düğme satırına taşı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемістити до рядка кнопок" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chuyển vào hàng nút" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "移动到按钮行" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "移至按鈕列" + } + } + } + }, + "Move to More menu" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skuif na Meer-kieslys" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "نقل إلى قائمة المزيد" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Преместване в менюто „Още“" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mou al menú Més" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přesunout do nabídky Další" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flyt til menuen Mere" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ins Mehr-Menü verschieben" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μετακίνηση στο μενού Περισσότερα" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Move to More menu" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover al menú Más" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siirrä Lisää-valikkoon" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Déplacer vers le menu Plus" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Áthelyezés a Továbbiak menübe" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sposta nel menu Altro" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "העבר לתפריט עוד" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "その他メニューへ移動" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "더 보기 메뉴로 이동" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Naar Meer-menu verplaatsen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flytt til Mer-meny" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Przenieś do menu Więcej" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover para o menu Mais" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mover para o menu Mais" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mută în meniul Mai multe" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Переместить в меню Ещё" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Премести у мени Још" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Flytta till menyn Mer" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Daha Fazla menüsüne taşı" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Перемістити до меню Ще" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chuyển vào menu Thêm" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "移动到更多菜单" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "移至更多選單" + } + } + } + }, + "Hide action" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Versteek aksie" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إخفاء الإجراء" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скриване на действието" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Amaga l’acció" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skrýt akci" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjul handling" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aktion ausblenden" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Απόκρυψη ενέργειας" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hide action" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocultar acción" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Piilota toiminto" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masquer l’action" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Művelet elrejtése" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nascondi azione" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הסתר פעולה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "アクションを非表示" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "동작 숨기기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Actie verbergen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skjul handling" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ukryj akcję" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocultar ação" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ocultar ação" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ascunde acțiunea" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скрыть действие" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Сакриј радњу" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dölj åtgärd" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Eylemi gizle" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Приховати дію" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ẩn hành động" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "隐藏操作" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "隱藏操作" + } + } + } + }, + "Reply" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Antwoord" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "رد" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Отговор" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Respondre" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odpovědět" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svar" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Antworten" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Απάντηση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reply" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Responder" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vastaa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Répondre" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Válasz" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rispondi" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "תגובה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "返信" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "답글" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beantwoorden" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svar" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odpowiedz" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Responder" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Responder" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Răspunde" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ответить" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Odgovori" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Svara" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yanıtla" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Відповісти" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trả lời" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "回复" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "回覆" + } + } + } + }, + "Comment" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommentaar" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تعليق" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Коментар" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comenta" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komentovat" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommentar" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommentieren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σχόλιο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comment" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comentario" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommentti" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commenter" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hozzászólás" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Commenta" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "תגובה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "コメント" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "댓글" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reactie" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommentar" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Komentarz" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comentar" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comentar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Comentariu" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Комментировать" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Коментариши" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kommentera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yorum yap" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Коментувати" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bình luận" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "评论" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "留言" + } + } + } + }, + "Repost" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Herplaas" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إعادة نشر" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Препубликуване" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Republicar" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přesdílet" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Post igen" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teilen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αναδημοσίευση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repost" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jaa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repartager" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Újraközlés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ripubblica" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "פרסום מחדש" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "リポスト" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "리포스트" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reposter" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podaj dalej" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repostar" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Republicar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Repostare" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Репост" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ponovo objavi" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reposta" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yeniden paylaş" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поширити" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Đăng lại" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "转发" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "轉發" + } + } + } + }, + "Quote" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Haal aan" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "اقتباس" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цитат" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citar" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citovat" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citér" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zitieren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Παράθεση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Quote" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lainaa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citer" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Idézés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cita" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "ציטוט" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "引用" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "인용" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citeren" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Siter" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cytat" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citar" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citat" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цитировать" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citiraj" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Citera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Alıntıla" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Цитата" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Trích dẫn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "引用" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "引用" + } + } + } + }, + "Like" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Hou van" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إعجاب" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Харесване" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "M'agrada" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "To se mi líbí" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Synes godt om" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gefällt mir" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Μου αρέσει" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Like" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Me gusta" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tykkää" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "J'aime" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kedvelés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mi piace" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "אהבתי" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "いいね" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "좋아요" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Vind ik leuk" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lik" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lubię to!" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Curtir" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gostar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Apreciază" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Лайк" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sviđa mi se" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gilla" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Beğen" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Подобається" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thích" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "赞" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "喜歡" + } + } + } + }, + "React" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voeg reaksie by" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إضافة تفاعل" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добави реакция" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afegir reacció" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přidat reakci" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilføj reaktion" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reaktion hinzufügen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προσθήκη αντίδρασης" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "React" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Añadir reacción" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lisää reaktio" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter une réaction" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reakció hozzáadása" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiungi reazione" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הוסף תגובה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "リアクション" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "반응" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reactie toevoegen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legg til reaksjon" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj reakcję" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar reação" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar reação" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adaugă reacție" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавить реакцию" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj reakciju" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lägg till reaktion" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tepki ekle" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Додати реакцію" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thêm cảm xúc" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "回应" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "反應" + } + } + } + }, + "Bookmark" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Voeg boekmerk by" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إضافة إشارة مرجعية" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добави отметка" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Afegir marcador" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Přidat do záložek" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tilføj bogmærke" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lesezeichen hinzufügen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Προσθήκη σελιδοδείκτη" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bookmark" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Añadir marcador" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lisää kirjanmerkki" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ajouter un signet" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Könyvjelző hozzáadása" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Aggiungi segnalibro" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "הוסף סימנייה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ブックマーク" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "북마크" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bladwijzer toevoegen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Legg til bokmerke" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj zakładkę" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Salvar favorito" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adicionar marcador" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Adaugă semn de carte" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Добавить в закладки" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dodaj obeleživač" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lägg till bokmärke" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yer işareti ekle" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "У закладки" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thêm dấu trang" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "书签" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "書籤" + } + } + } + }, + "Favorite" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gunsteling" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "تفضيل" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Любими" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferit" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oblíbené" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorit" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorisieren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αγαπημένο" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorite" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorito" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Suosikki" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favori" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Kedvenc" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Preferito" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "מועדף" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "お気に入り" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "즐겨찾기" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favoriet" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favoritt" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ulubione" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favoritar" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorito" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorit" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "В избранное" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Omiljeno" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favoritmarkera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Favorilere ekle" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "У вибране" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Yêu thích" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "收藏" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "收藏" + } + } + } + }, + "Share" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deel" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مشاركة" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Споделяне" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sdílet" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Del" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Teilen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κοινοποίηση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Share" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jaa" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Megosztás" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Condividi" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שיתוף" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "共有" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "공유" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Del" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Udostępnij" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartilhar" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partilhar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partajează" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поделиться" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podeli" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dela" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Paylaş" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поділитися" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chia sẻ" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "分享" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "分享" + } + } + } + }, + "Fx share" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Deel via FxEmbed" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "مشاركة عبر FxEmbed" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Споделяне чрез FxEmbed" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir via FxEmbed" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sdílet přes FxEmbed" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Del via FxEmbed" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Über FxEmbed teilen" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Κοινοποίηση μέσω FxEmbed" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fx share" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartir vía FxEmbed" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jaa FxEmbedin kautta" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partager via FxEmbed" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Megosztás FxEmbed-en keresztül" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Condividi tramite FxEmbed" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "שתף דרך FxEmbed" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fx共有" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fx 공유" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Delen via FxEmbed" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Del via FxEmbed" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Udostępnij przez FxEmbed" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Compartilhar via FxEmbed" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partilhar via FxEmbed" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Partajează prin FxEmbed" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поделиться через FxEmbed" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Podeli putem FxEmbed" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dela via FxEmbed" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "FxEmbed ile paylaş" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Поділитися через FxEmbed" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chia sẻ qua FxEmbed" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fx 分享" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "Fx 分享" + } + } + } + }, + "Report" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapporteer" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "إبلاغ" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Докладване" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Informar" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Nahlásit" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Anmeld" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Melden" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αναφορά" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Report" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Reportar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Raportoi" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Signaler" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Jelentés" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Segnala" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "דיווח" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "報告" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "신고" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapporteren" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapporter" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zgłoś" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denunciar" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Denunciar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Raportează" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Пожаловаться" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Prijavi" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Rapportera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bildir" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Скаржитися" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Báo cáo" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "举报" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "檢舉" + } + } + } + }, + "Mute user" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demp" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "كتم" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заглушаване" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Silenciar" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Skrýt" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Lydløs" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Stummschalten" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Σίγαση" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mute user" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Silenciar" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Mykistä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Masquer" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Némítás" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Silenzia" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "השתקה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ユーザーをミュート" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "사용자 뮤트" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Dempen" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Demp" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Wycisz" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Silenciar" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Silenciar" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ignoră" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Игнорировать" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Utišaj" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tysta" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sessize al" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Приглушити" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Ẩn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "静音用户" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "靜音使用者" + } + } + } + }, + "Block user" : { + "localizations" : { + "af" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokkeer" + } + }, + "ar" : { + "stringUnit" : { + "state" : "translated", + "value" : "حظر" + } + }, + "bg" : { + "stringUnit" : { + "state" : "translated", + "value" : "Блокиране" + } + }, + "ca" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquejar" + } + }, + "cs" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokovat" + } + }, + "da" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloker" + } + }, + "de" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockieren" + } + }, + "el" : { + "stringUnit" : { + "state" : "translated", + "value" : "Αποκλεισμός" + } + }, + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Block user" + } + }, + "es" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear" + } + }, + "fi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Estä" + } + }, + "fr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquer" + } + }, + "hu" : { + "stringUnit" : { + "state" : "translated", + "value" : "Tiltás" + } + }, + "it" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blocca" + } + }, + "he" : { + "stringUnit" : { + "state" : "translated", + "value" : "חסימה" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ユーザーをブロック" + } + }, + "ko" : { + "stringUnit" : { + "state" : "translated", + "value" : "사용자 차단" + } + }, + "nl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokkeren" + } + }, + "nb" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokker" + } + }, + "pl" : { + "stringUnit" : { + "state" : "translated", + "value" : "Zablokuj" + } + }, + "pt-BR" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear" + } + }, + "pt" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bloquear" + } + }, + "ro" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blochează" + } + }, + "ru" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заблокировать" + } + }, + "sr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blokiraj" + } + }, + "sv" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blockera" + } + }, + "tr" : { + "stringUnit" : { + "state" : "translated", + "value" : "Engelle" + } + }, + "uk" : { + "stringUnit" : { + "state" : "translated", + "value" : "Заблокувати" + } + }, + "vi" : { + "stringUnit" : { + "state" : "translated", + "value" : "Chặn" + } + }, + "zh-Hans" : { + "stringUnit" : { + "state" : "translated", + "value" : "屏蔽用户" + } + }, + "zh-Hant" : { + "stringUnit" : { + "state" : "translated", + "value" : "封鎖使用者" + } + } + } } }, "version" : "1.1" -} +} \ No newline at end of file diff --git a/iosApp/flare/UI/Component/Status/StatusActionView.swift b/iosApp/flare/UI/Component/Status/StatusActionView.swift index 2c9dda0f7..263a9b46f 100644 --- a/iosApp/flare/UI/Component/Status/StatusActionView.swift +++ b/iosApp/flare/UI/Component/Status/StatusActionView.swift @@ -10,17 +10,20 @@ import UIKit struct StatusActionsView: View { @Environment(\.timelineAppearance.postActionStyle) private var postActionStyle @Environment(\.timelineAppearance.showNumbers) private var showNumbers + @Environment(\.timelineAppearance.postActionLayout) private var postActionLayout @Environment(\.openURL) private var openURL @ScaledMetric(relativeTo: .footnote) var fontSize = 13 let data: [ActionMenu] let useText: Bool var allowSpacer: Bool = true + var applyPostActionLayout: Bool = true var body: some View { + let actions = resolvedData if useText { - ForEach(0.. [ActionMenu] { + if let actions = value as? [ActionMenu] { + return actions + } + if let actions = value as? NSArray { + return actions.cast(ActionMenu.self) + } + return [] +} + extension UiIcon { var image: Image { Image(imageName) diff --git a/iosApp/flare/UI/Component/Status/StatusShareSheet.swift b/iosApp/flare/UI/Component/Status/StatusShareSheet.swift index 0cbbda81c..4687b07d3 100644 --- a/iosApp/flare/UI/Component/Status/StatusShareSheet.swift +++ b/iosApp/flare/UI/Component/Status/StatusShareSheet.swift @@ -158,6 +158,7 @@ private extension TimelineAppearance { compatLinkPreview: compatLinkPreview, showNumbers: showNumbers, postActionStyle: postActionStyle, + postActionLayout: postActionLayout, fullWidthPost: fullWidthPost, absoluteTimestamp: absoluteTimestamp, showPlatformLogo: showPlatformLogo, diff --git a/iosApp/flare/UI/Component/Status/StatusUIKitActions.swift b/iosApp/flare/UI/Component/Status/StatusUIKitActions.swift index 249147ea4..3e9adad71 100644 --- a/iosApp/flare/UI/Component/Status/StatusUIKitActions.swift +++ b/iosApp/flare/UI/Component/Status/StatusUIKitActions.swift @@ -16,6 +16,7 @@ final class StatusActionsUIView: UIView, ManualLayoutMeasurable, TimelineHeightP private var useText: Bool = false private var allowSpacer: Bool = true private var postActionStyle: PostActionStyle = .leftAligned + private var postActionLayout: PostActionLayoutConfig = PostActionLayoutConfig.companion.Default private var showNumbers: Bool = true private var fontSize: CGFloat = 13 private var textStyle: UIFont.TextStyle = .footnote @@ -40,13 +41,18 @@ final class StatusActionsUIView: UIView, ManualLayoutMeasurable, TimelineHeightP useText: Bool, allowSpacer: Bool = true, postActionStyle: PostActionStyle, + postActionLayout: PostActionLayoutConfig = PostActionLayoutConfig.companion.Default, + applyPostActionLayout: Bool = true, showNumbers: Bool, isDetail: Bool ) { - self.data = data + self.data = applyPostActionLayout + ? castActionMenus(PostActionLayoutHelpers.shared.apply(actions: data, config: postActionLayout)) + : data self.useText = useText self.allowSpacer = allowSpacer self.postActionStyle = postActionStyle + self.postActionLayout = postActionLayout self.showNumbers = showNumbers self.fontSize = UIFontMetrics(forTextStyle: .footnote).scaledValue(for: 13) self.textStyle = isDetail ? .body : .footnote @@ -450,6 +456,7 @@ private final class ActionGroupColumnView: UIView, ManualLayoutMeasurable, Timel useText: true, allowSpacer: false, postActionStyle: postActionStyle, + applyPostActionLayout: false, showNumbers: showNumbers, isDetail: false ) @@ -673,6 +680,16 @@ private final class ActionItemControl: UIButton, ManualLayoutMeasurable, Timelin import SwiftUI // for OpenURLAction +private func castActionMenus(_ value: Any) -> [ActionMenu] { + if let actions = value as? [ActionMenu] { + return actions + } + if let actions = value as? NSArray { + return actions.cast(ActionMenu.self) + } + return [] +} + // MARK: - Color mapping extension ActionMenu.ItemColor { var uiColor: UIColor? { diff --git a/iosApp/flare/UI/Component/Status/StatusUIKitView.swift b/iosApp/flare/UI/Component/Status/StatusUIKitView.swift index 1672bb9c1..1fd766093 100644 --- a/iosApp/flare/UI/Component/Status/StatusUIKitView.swift +++ b/iosApp/flare/UI/Component/Status/StatusUIKitView.swift @@ -866,6 +866,7 @@ final class StatusUIKitView: UIView, UIGestureRecognizerDelegate, ManualLayoutMe useText: false, allowSpacer: true, postActionStyle: appearance.postActionStyle, + postActionLayout: appearance.postActionLayout, showNumbers: appearance.showNumbers, isDetail: isDetail ) diff --git a/iosApp/flare/UI/Component/UIKitAppearance.swift b/iosApp/flare/UI/Component/UIKitAppearance.swift index 4569d17a7..efafc1672 100644 --- a/iosApp/flare/UI/Component/UIKitAppearance.swift +++ b/iosApp/flare/UI/Component/UIKitAppearance.swift @@ -30,6 +30,8 @@ struct StatusUIKitAppearance: Equatable { let absoluteTimestamp: Bool let postActionStyle: PostActionStyle let postActionStyleID: String + let postActionLayout: PostActionLayoutConfig + let postActionLayoutID: String let showNumbers: Bool let showMedia: Bool let showSensitiveContent: Bool @@ -49,6 +51,8 @@ struct StatusUIKitAppearance: Equatable { absoluteTimestamp = timeline.absoluteTimestamp postActionStyle = timeline.postActionStyle postActionStyleID = timeline.postActionStyle.name + postActionLayout = timeline.postActionLayout + postActionLayoutID = PostActionLayoutHelpers.shared.signature(config: timeline.postActionLayout) showNumbers = timeline.showNumbers showMedia = timeline.showMedia showSensitiveContent = timeline.showSensitiveContent @@ -66,6 +70,7 @@ struct StatusUIKitAppearance: Equatable { lhs.showPlatformLogo == rhs.showPlatformLogo && lhs.absoluteTimestamp == rhs.absoluteTimestamp && lhs.postActionStyleID == rhs.postActionStyleID && + lhs.postActionLayoutID == rhs.postActionLayoutID && lhs.showNumbers == rhs.showNumbers && lhs.showMedia == rhs.showMedia && lhs.showSensitiveContent == rhs.showSensitiveContent && diff --git a/iosApp/flare/UI/FlareTheme.swift b/iosApp/flare/UI/FlareTheme.swift index 514c8060a..dab2195a4 100644 --- a/iosApp/flare/UI/FlareTheme.swift +++ b/iosApp/flare/UI/FlareTheme.swift @@ -68,6 +68,7 @@ private extension TimelineAppearance { compatLinkPreview: compatLinkPreview, showNumbers: showNumbers, postActionStyle: postActionStyle, + postActionLayout: postActionLayout, fullWidthPost: fullWidthPost, absoluteTimestamp: absoluteTimestamp, showPlatformLogo: showPlatformLogo, diff --git a/iosApp/flare/UI/Route/Route.swift b/iosApp/flare/UI/Route/Route.swift index fdafa8915..b393e11cb 100644 --- a/iosApp/flare/UI/Route/Route.swift +++ b/iosApp/flare/UI/Route/Route.swift @@ -97,6 +97,8 @@ enum Route: Hashable, Identifiable { AppearanceThemeScreen() case .appearanceLayout: AppearanceLayoutScreen() + case .postActionLayout: + PostActionLayoutScreen() case .appearanceDisplay: AppearanceDisplayScreen() case .appearanceMedia: @@ -230,6 +232,7 @@ enum Route: Hashable, Identifiable { case storage case appearanceTheme case appearanceLayout + case postActionLayout case appearanceDisplay case appearanceMedia case appIconSettings diff --git a/iosApp/flare/UI/Screen/AppearanceLayoutScreen.swift b/iosApp/flare/UI/Screen/AppearanceLayoutScreen.swift index 111a21709..f1a208ced 100644 --- a/iosApp/flare/UI/Screen/AppearanceLayoutScreen.swift +++ b/iosApp/flare/UI/Screen/AppearanceLayoutScreen.swift @@ -73,6 +73,12 @@ struct AppearanceLayoutScreen: View { Text("appearance_show_numbers_description") } } + NavigationLink(value: Route.postActionLayout) { + VStack(alignment: .leading) { + Text("Customize actions") + Text("Choose the order and visibility of post action buttons") + } + } } } .navigationTitle("appearance_layout_group_title") diff --git a/iosApp/flare/UI/Screen/GalleryDetailScreen.swift b/iosApp/flare/UI/Screen/GalleryDetailScreen.swift index c9badb686..8f69c6628 100644 --- a/iosApp/flare/UI/Screen/GalleryDetailScreen.swift +++ b/iosApp/flare/UI/Screen/GalleryDetailScreen.swift @@ -861,6 +861,7 @@ private extension TimelineAppearance { compatLinkPreview: compatLinkPreview, showNumbers: showNumbers, postActionStyle: postActionStyle, + postActionLayout: postActionLayout, fullWidthPost: fullWidthPost, absoluteTimestamp: absoluteTimestamp, showPlatformLogo: showPlatformLogo, diff --git a/iosApp/flare/UI/Screen/PostActionLayoutScreen.swift b/iosApp/flare/UI/Screen/PostActionLayoutScreen.swift new file mode 100644 index 000000000..f23324362 --- /dev/null +++ b/iosApp/flare/UI/Screen/PostActionLayoutScreen.swift @@ -0,0 +1,254 @@ +import SwiftUI +import KotlinSharedUI + +struct PostActionLayoutScreen: View { + @StateObject private var statusPresenter = KotlinPresenter(presenter: AppearancePresenter()) + @StateObject private var presenter = KotlinPresenter(presenter: SettingsPresenter()) + @Environment(\.timelineAppearance) private var timelineAppearance + @State private var config = PostActionLayoutConfig.companion.Default + + private let placements: [PostActionPlacement] = [.buttonRow, .moreMenu, .hidden] + + var body: some View { + List { + Section { + StateView(state: statusPresenter.state.sampleStatus) { status in + TimelineView(data: PostActionLayoutPreviewHelper.shared.withPreviewActions(post: status)) + .environment(\.timelineAppearance, timelineAppearance.withPostActionLayout(config)) + } + Toggle(isOn: Binding(get: { + config.enabled + }, set: { enabled in + updateConfig( + PostActionLayoutHelpers.shared.withEnabled( + config: config, + enabled: enabled + ) + ) + })) { + Text("Customize actions") + Text("Choose which actions appear in the row, More menu, or stay hidden") + } + } + + if config.enabled { + ForEach(placements, id: \.name) { placement in + actionSection(placement) + } + } + } + .navigationTitle("Post actions") + .onAppear { + config = normalizedTimelineConfig + } + .onChange(of: persistedSignature) { _, _ in + config = normalizedTimelineConfig + } + } + + @ViewBuilder + private func actionSection(_ placement: PostActionPlacement) -> some View { + let families = families(for: placement) + Section(placement.title) { + if families.isEmpty { + Text("No actions") + .foregroundStyle(.secondary) + } else { + ForEach(Array(families.enumerated()), id: \.element.name) { index, family in + actionRow(family: family, placement: placement, index: index, totalCount: families.count) + } + .onMove { source, destination in + move(placement: placement, from: source, to: destination) + } + } + } + } + + private func actionRow( + family: PostActionFamily, + placement: PostActionPlacement, + index: Int, + totalCount: Int + ) -> some View { + HStack(spacing: 12) { + Image(family.imageName) + .frame(width: 24, height: 24) + .foregroundStyle(.primary) + Text(family.title) + Spacer() + Image(systemName: "line.3.horizontal") + .foregroundStyle(.secondary) + Menu { + ForEach(placements, id: \.name) { target in + if target.name != placement.name { + Button(target.moveTitle) { + updateConfig( + PostActionLayoutHelpers.shared.moveTo( + config: config, + family: family, + placement: target + ) + ) + } + } + } + Divider() + Button("Move up") { + updateConfig( + PostActionLayoutHelpers.shared.moveBy( + config: config, + family: family, + offset: Int32(-1) + ) + ) + } + .disabled(index == 0) + Button("Move down") { + updateConfig( + PostActionLayoutHelpers.shared.moveBy( + config: config, + family: family, + offset: Int32(1) + ) + ) + } + .disabled(index >= totalCount - 1) + } label: { + Image("fa-ellipsis-vertical") + .frame(width: 32, height: 32) + } + .buttonStyle(.borderless) + .foregroundStyle(.secondary) + } + } + + private var persistedSignature: String { + PostActionLayoutHelpers.shared.signature(config: timelineAppearance.postActionLayout) + } + + private var normalizedTimelineConfig: PostActionLayoutConfig { + PostActionLayoutHelpers.shared.normalizedForEdit(config: timelineAppearance.postActionLayout) + } + + private func families(for placement: PostActionPlacement) -> [PostActionFamily] { + castPostActionFamilies( + PostActionLayoutHelpers.shared.familiesFor( + config: config, + placement: placement + ) + ) + } + + private func updateConfig(_ value: PostActionLayoutConfig) { + let normalized = PostActionLayoutHelpers.shared.normalizedForEdit(config: value) + config = normalized + presenter.state.updatePostActionLayout(value: normalized) + } + + private func move(placement: PostActionPlacement, from source: IndexSet, to destination: Int) { + guard let fromIndex = source.first else { return } + updateConfig( + PostActionLayoutHelpers.shared.moveAt( + config: config, + placement: placement, + fromIndex: Int32(fromIndex), + toOffset: Int32(destination) + ) + ) + } +} + +private extension PostActionPlacement { + var title: String { + switch self { + case .buttonRow: return String(localized: "Button row") + case .moreMenu: return String(localized: "More menu") + case .hidden: return String(localized: "Hidden") + } + } + + var moveTitle: String { + switch self { + case .buttonRow: return String(localized: "Move to button row") + case .moreMenu: return String(localized: "Move to More menu") + case .hidden: return String(localized: "Hide action") + } + } +} + +private extension PostActionFamily { + var title: String { + switch self { + case .reply: return String(localized: "Reply") + case .comment: return String(localized: "Comment") + case .repost: return String(localized: "Repost") + case .quote: return String(localized: "Quote") + case .like: return String(localized: "Like") + case .react: return String(localized: "React") + case .translate: return String(localized: "Translate") + case .bookmark: return String(localized: "Bookmark") + case .favorite: return String(localized: "Favorite") + case .share: return String(localized: "Share") + case .fxShare: return String(localized: "Fx share") + case .delete: return String(localized: "Delete") + case .report: return String(localized: "Report") + case .muteUser: return String(localized: "Mute user") + case .blockUser: return String(localized: "Block user") + } + } + + var imageName: String { + switch self { + case .reply: return "fa-reply" + case .comment: return "fa-comment-dots" + case .repost: return "fa-retweet" + case .quote: return "fa-reply" + case .like: return "fa-heart" + case .react: return "fa-plus" + case .translate: return "fa-language" + case .bookmark: return "fa-bookmark" + case .favorite: return "fa-star" + case .share: return "fa-share-nodes" + case .fxShare: return "fa-share-nodes" + case .delete: return "fa-trash" + case .report: return "fa-circle-info" + case .muteUser: return "fa-volume-xmark" + case .blockUser: return "fa-user-slash" + } + } +} + +private extension TimelineAppearance { + func withPostActionLayout(_ config: PostActionLayoutConfig) -> TimelineAppearance { + doCopy( + avatarShape: avatarShape, + showMedia: showMedia, + showSensitiveContent: showSensitiveContent, + expandContentWarning: expandContentWarning, + expandMediaSize: expandMediaSize, + videoAutoplay: videoAutoplay, + showLinkPreview: showLinkPreview, + compatLinkPreview: compatLinkPreview, + showNumbers: showNumbers, + postActionStyle: postActionStyle, + postActionLayout: config, + fullWidthPost: fullWidthPost, + absoluteTimestamp: absoluteTimestamp, + showPlatformLogo: showPlatformLogo, + timelineDisplayMode: timelineDisplayMode, + aiConfig: aiConfig, + lineLimit: lineLimit, + showTranslateButton: showTranslateButton + ) + } +} + +private func castPostActionFamilies(_ value: Any) -> [PostActionFamily] { + if let families = value as? [PostActionFamily] { + return families + } + if let families = value as? NSArray { + return families.cast(PostActionFamily.self) + } + return [] +} diff --git a/iosApp/flare/UI/Screen/StatusInsightSheet.swift b/iosApp/flare/UI/Screen/StatusInsightSheet.swift index df36e8803..bb8370077 100644 --- a/iosApp/flare/UI/Screen/StatusInsightSheet.swift +++ b/iosApp/flare/UI/Screen/StatusInsightSheet.swift @@ -188,6 +188,7 @@ private extension TimelineAppearance { compatLinkPreview: compatLinkPreview, showNumbers: showNumbers, postActionStyle: .hidden, + postActionLayout: postActionLayout, fullWidthPost: fullWidthPost, absoluteTimestamp: absoluteTimestamp, showPlatformLogo: showPlatformLogo, diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/model/TranslationDisplay.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/model/TranslationDisplay.kt index e3a102979..470b0e2ce 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/model/TranslationDisplay.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/data/database/cache/model/TranslationDisplay.kt @@ -3,6 +3,7 @@ package dev.dimension.flare.data.database.cache.model import dev.dimension.flare.common.Locale import dev.dimension.flare.common.encodeJson import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.translation.PreTranslationStoreSupport import dev.dimension.flare.model.AccountType import dev.dimension.flare.ui.model.ClickEvent @@ -227,6 +228,7 @@ private fun ActionMenu.prependTranslationAction( ), ), icon = UiIcon.Translate, + actionFamily = PostActionFamily.Translate, ) copy( actions = diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/microblog/ActionMenu.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/microblog/ActionMenu.kt index 615f59304..7c3636003 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/microblog/ActionMenu.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/data/datasource/microblog/ActionMenu.kt @@ -9,11 +9,73 @@ import dev.dimension.flare.ui.model.UiIcon import dev.dimension.flare.ui.model.UiNumber import dev.dimension.flare.ui.model.onClicked import dev.dimension.flare.ui.route.DeeplinkRoute +import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.persistentListOf +import kotlinx.collections.immutable.toPersistentList import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlin.native.HiddenFromObjC +@Serializable +@Immutable +public enum class PostActionFamily { + Reply, + Comment, + Repost, + Quote, + Like, + React, + Translate, + Bookmark, + Favorite, + Share, + FxShare, + Delete, + Report, + MuteUser, + BlockUser, +} + +public enum class PostActionPlacement { + ButtonRow, + MoreMenu, + Hidden, +} + +@Serializable +@Immutable +public data class PostActionLayoutConfig( + val enabled: Boolean = false, + val primary: SerializableImmutableList = DefaultPrimary, + val overflow: SerializableImmutableList = DefaultOverflow, + val hidden: SerializableImmutableList = persistentListOf(), +) { + public companion object { + public val DefaultPrimary: SerializableImmutableList = + persistentListOf( + PostActionFamily.Reply, + PostActionFamily.Comment, + PostActionFamily.Repost, + PostActionFamily.Like, + PostActionFamily.React, + ) + + public val DefaultOverflow: SerializableImmutableList = + persistentListOf( + PostActionFamily.Translate, + PostActionFamily.Bookmark, + PostActionFamily.Favorite, + PostActionFamily.Share, + PostActionFamily.Delete, + PostActionFamily.Report, + PostActionFamily.MuteUser, + PostActionFamily.BlockUser, + ) + + public val Default: PostActionLayoutConfig = PostActionLayoutConfig() + } +} + @Serializable @Immutable public sealed class ActionMenu { @@ -44,6 +106,7 @@ public sealed class ActionMenu { val count: UiNumber? = null, val color: Color? = null, public val clickEvent: ClickEvent = ClickEvent.Noop, + val actionFamily: PostActionFamily? = null, ) : ActionMenu() { init { require(icon != null || text != null) { @@ -117,6 +180,330 @@ public sealed class ActionMenu { } } +@HiddenFromObjC +public fun ImmutableList.applyPostActionLayout(config: PostActionLayoutConfig): ImmutableList { + if (!config.enabled) return this + + var nextIndex = 0 + val entries = mutableListOf() + + fun collect(action: ActionMenu) { + when (action) { + is ActionMenu.Item -> { + if (!action.isDisplayOnlyPostActionContainer()) { + entries += PostActionEntry(nextIndex++, action, action.actionFamily) + } + } + + is ActionMenu.Group -> { + if (action.displayItem.isDisplayOnlyPostActionContainer()) { + action.actions.forEach(::collect) + } else { + entries += PostActionEntry(nextIndex++, action, action.actionFamilyForLayout()) + } + } + + ActionMenu.Divider -> { + Unit + } + } + } + + forEach(::collect) + + val consumedIndexes = mutableSetOf() + val hiddenFamilies = config.hidden.toSet() + + fun pick(family: PostActionFamily): ActionMenu? = + entries + .firstOrNull { + it.family == family && + it.index !in consumedIndexes && + it.family !in hiddenFamilies + }?.also { + consumedIndexes += it.index + }?.action + + val primaryFamilies = config.primary.distinct().filterNot { it in hiddenFamilies } + val overflowFamilies = + config.overflow + .distinct() + .filterNot { it in hiddenFamilies } + .filterNot { it in primaryFamilies } + + val primaryActions = primaryFamilies.mapNotNull(::pick).toMutableList() + val overflowActions = overflowFamilies.mapNotNull(::pick).toMutableList() + overflowActions += + entries + .filterNot { it.index in consumedIndexes } + .filterNot { it.family?.let { family -> family in hiddenFamilies } == true } + .map { it.action } + + val normalizedOverflow = overflowActions.normalizePostActionDividers() + val displayActions = + if (normalizedOverflow.isEmpty()) { + primaryActions + } else { + primaryActions + + ActionMenu.Group( + displayItem = + ActionMenu.Item( + icon = UiIcon.More, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.More), + ), + actions = normalizedOverflow.toPersistentList(), + ) + } + + return displayActions.toPersistentList() +} + +public object PostActionLayoutHelpers { + public val allEditableFamilies: List = + listOf( + PostActionFamily.Reply, + PostActionFamily.Comment, + PostActionFamily.Repost, + PostActionFamily.Like, + PostActionFamily.React, + PostActionFamily.Translate, + PostActionFamily.Bookmark, + PostActionFamily.Favorite, + PostActionFamily.Share, + PostActionFamily.Delete, + PostActionFamily.Report, + PostActionFamily.MuteUser, + PostActionFamily.BlockUser, + ) + + public fun normalizedForEdit(config: PostActionLayoutConfig): PostActionLayoutConfig { + val primary = config.primary.cleanFamilies() + val hidden = config.hidden.cleanFamilies().filterNot { it in primary } + val overflow = + ( + config.overflow.cleanFamilies().filterNot { it in primary || it in hidden } + + allEditableFamilies.filterNot { it in primary || it in hidden || it in config.overflow } + ).distinct() + return config.copy( + primary = primary.toPersistentList(), + overflow = overflow.toPersistentList(), + hidden = hidden.toPersistentList(), + ) + } + + public fun withEnabled( + config: PostActionLayoutConfig, + enabled: Boolean, + ): PostActionLayoutConfig = + normalizedForEdit( + config.copy(enabled = enabled), + ) + + public fun familiesFor( + config: PostActionLayoutConfig, + placement: PostActionPlacement, + ): List = + when (placement) { + PostActionPlacement.ButtonRow -> config.primary.toList() + PostActionPlacement.MoreMenu -> config.overflow.toList() + PostActionPlacement.Hidden -> config.hidden.toList() + } + + public fun placementOf( + config: PostActionLayoutConfig, + family: PostActionFamily, + ): PostActionPlacement = + when { + family in config.primary -> PostActionPlacement.ButtonRow + family in config.hidden -> PostActionPlacement.Hidden + else -> PostActionPlacement.MoreMenu + } + + public fun moveTo( + config: PostActionLayoutConfig, + family: PostActionFamily, + placement: PostActionPlacement, + ): PostActionLayoutConfig { + val primary = config.primary.filterNot { it == family }.toMutableList() + val overflow = config.overflow.filterNot { it == family }.toMutableList() + val hidden = config.hidden.filterNot { it == family }.toMutableList() + when (placement) { + PostActionPlacement.ButtonRow -> primary += family + PostActionPlacement.MoreMenu -> overflow += family + PostActionPlacement.Hidden -> hidden += family + } + return normalizedForEdit( + config.copy( + primary = primary.toPersistentList(), + overflow = overflow.toPersistentList(), + hidden = hidden.toPersistentList(), + ), + ) + } + + public fun moveWithin( + config: PostActionLayoutConfig, + placement: PostActionPlacement, + from: PostActionFamily, + to: PostActionFamily, + ): PostActionLayoutConfig { + if (from == to) return config + return when (placement) { + PostActionPlacement.ButtonRow -> { + config.copy( + primary = + config.primary + .toMutableList() + .move(from, to) + .toPersistentList(), + ) + } + + PostActionPlacement.MoreMenu -> { + config.copy( + overflow = + config.overflow + .toMutableList() + .move(from, to) + .toPersistentList(), + ) + } + + PostActionPlacement.Hidden -> { + config.copy( + hidden = + config.hidden + .toMutableList() + .move(from, to) + .toPersistentList(), + ) + } + }.let(::normalizedForEdit) + } + + public fun moveAt( + config: PostActionLayoutConfig, + placement: PostActionPlacement, + fromIndex: Int, + toOffset: Int, + ): PostActionLayoutConfig { + val families = familiesFor(config, placement).toMutableList() + if (fromIndex !in families.indices) return config + val item = families.removeAt(fromIndex) + val destination = if (toOffset > fromIndex) toOffset - 1 else toOffset + families.add(destination.coerceIn(0, families.size), item) + return replaceFamilies(config, placement, families) + } + + public fun moveBy( + config: PostActionLayoutConfig, + family: PostActionFamily, + offset: Int, + ): PostActionLayoutConfig { + val placement = placementOf(config, family) + val families = familiesFor(config, placement) + val fromIndex = families.indexOf(family) + if (fromIndex == -1) return config + val toIndex = (fromIndex + offset).coerceIn(families.indices) + if (fromIndex == toIndex) return config + return moveWithin(config, placement, family, families[toIndex]) + } + + public fun apply( + actions: List, + config: PostActionLayoutConfig, + ): List = + actions + .toPersistentList() + .applyPostActionLayout(config) + .toList() + + public fun signature(config: PostActionLayoutConfig): String = + buildString { + append(config.enabled) + append('|') + append(config.primary.joinToString(",") { it.name }) + append('|') + append(config.overflow.joinToString(",") { it.name }) + append('|') + append(config.hidden.joinToString(",") { it.name }) + } + + private fun replaceFamilies( + config: PostActionLayoutConfig, + placement: PostActionPlacement, + families: List, + ): PostActionLayoutConfig = + when (placement) { + PostActionPlacement.ButtonRow -> config.copy(primary = families.toPersistentList()) + PostActionPlacement.MoreMenu -> config.copy(overflow = families.toPersistentList()) + PostActionPlacement.Hidden -> config.copy(hidden = families.toPersistentList()) + }.let(::normalizedForEdit) +} + +private data class PostActionEntry( + val index: Int, + val action: ActionMenu, + val family: PostActionFamily?, +) + +private fun Iterable.cleanFamilies(): List = + filter { it in PostActionLayoutHelpers.allEditableFamilies }.distinct() + +private fun MutableList.move( + from: PostActionFamily, + to: PostActionFamily, +): MutableList { + val fromIndex = indexOf(from) + val toIndex = indexOf(to) + if (fromIndex == -1 || toIndex == -1) return this + add(toIndex, removeAt(fromIndex)) + return this +} + +private fun ActionMenu.Group.actionFamilyForLayout(): PostActionFamily? = + displayItem.actionFamily + ?: actions + .asSequence() + .filterIsInstance() + .mapNotNull { it.actionFamily } + .firstOrNull { it == PostActionFamily.Repost } + ?: actions + .asSequence() + .filterIsInstance() + .mapNotNull { it.actionFamily } + .firstOrNull() + +private fun List.normalizePostActionDividers(): List { + val result = mutableListOf() + var lastWasDivider = true + for (action in this) { + when (action) { + ActionMenu.Divider -> { + if (!lastWasDivider) { + result += action + lastWasDivider = true + } + } + + else -> { + result += action + lastWasDivider = false + } + } + } + while (result.lastOrNull() == ActionMenu.Divider) { + result.removeAt(result.lastIndex) + } + return result +} + +private fun ActionMenu.Item.isDisplayOnlyPostActionContainer(): Boolean = + actionFamily == null && + clickEvent == ClickEvent.Noop && + icon == UiIcon.More && + text == ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.More) + @HiddenFromObjC public fun userActionsMenu( accountKey: MicroBlogKey?, @@ -135,6 +522,7 @@ public fun userActionsMenu( ClickEvent.Deeplink( DeeplinkRoute.MuteUser(accountKey, userKey), ), + actionFamily = PostActionFamily.MuteUser, ), ActionMenu.Item( icon = UiIcon.Block, @@ -147,5 +535,6 @@ public fun userActionsMenu( ClickEvent.Deeplink( DeeplinkRoute.BlockUser(accountKey, userKey), ), + actionFamily = PostActionFamily.BlockUser, ), ) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/data/model/appearance/AppearanceKeys.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/data/model/appearance/AppearanceKeys.kt index 28bce8909..a3da0a724 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/data/model/appearance/AppearanceKeys.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/data/model/appearance/AppearanceKeys.kt @@ -3,6 +3,7 @@ package dev.dimension.flare.data.model.appearance import kotlinx.serialization.KSerializer import kotlinx.serialization.builtins.serializer import kotlin.native.HiddenFromObjC +import dev.dimension.flare.data.datasource.microblog.PostActionLayoutConfig as AppearancePostActionLayoutConfig import dev.dimension.flare.data.model.AvatarShape as AppearanceAvatarShape import dev.dimension.flare.data.model.BottomBarBehavior as AppearanceBottomBarBehavior import dev.dimension.flare.data.model.BottomBarStyle as AppearanceBottomBarStyle @@ -77,6 +78,12 @@ public object AppearanceKeys { AppearancePostActionStyle.serializer(), ) + public object PostActionLayout : PerTimeline( + "timeline.post_action_layout", + AppearancePostActionLayoutConfig.Default, + AppearancePostActionLayoutConfig.serializer(), + ) + public object FullWidthPost : PerTimeline("timeline.full_width_post", false, Boolean.serializer()) public object AbsoluteTimestamp : PerTimeline("timeline.absolute_timestamp", false, Boolean.serializer()) @@ -113,6 +120,7 @@ public object AppearanceKeys { CompatLinkPreview, ShowNumbers, PostActionStyle, + PostActionLayout, FullWidthPost, AbsoluteTimestamp, ShowPlatformLogo, diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/data/model/appearance/AppearanceModels.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/data/model/appearance/AppearanceModels.kt index 2f5affe6c..31e6f86be 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/data/model/appearance/AppearanceModels.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/data/model/appearance/AppearanceModels.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.data.model.appearance import androidx.compose.runtime.Immutable +import dev.dimension.flare.data.datasource.microblog.PostActionLayoutConfig import dev.dimension.flare.data.model.AvatarShape import dev.dimension.flare.data.model.BottomBarBehavior import dev.dimension.flare.data.model.BottomBarStyle @@ -41,6 +42,7 @@ public data class TimelineAppearance( val compatLinkPreview: Boolean = AppearanceKeys.CompatLinkPreview.default, val showNumbers: Boolean = AppearanceKeys.ShowNumbers.default, val postActionStyle: PostActionStyle = AppearanceKeys.PostActionStyle.default, + val postActionLayout: PostActionLayoutConfig = AppearanceKeys.PostActionLayout.default, val fullWidthPost: Boolean = AppearanceKeys.FullWidthPost.default, val absoluteTimestamp: Boolean = AppearanceKeys.AbsoluteTimestamp.default, val showPlatformLogo: Boolean = AppearanceKeys.ShowPlatformLogo.default, @@ -90,6 +92,7 @@ public fun AppearancePatch.toTimelineAppearance(override: AppearancePatch?): Tim compatLinkPreview = getTimelineValue(AppearanceKeys.CompatLinkPreview, override), showNumbers = getTimelineValue(AppearanceKeys.ShowNumbers, override), postActionStyle = getTimelineValue(AppearanceKeys.PostActionStyle, override), + postActionLayout = getTimelineValue(AppearanceKeys.PostActionLayout, override), fullWidthPost = getTimelineValue(AppearanceKeys.FullWidthPost, override), absoluteTimestamp = getTimelineValue(AppearanceKeys.AbsoluteTimestamp, override), showPlatformLogo = getTimelineValue(AppearanceKeys.ShowPlatformLogo, override), @@ -109,6 +112,7 @@ public fun TimelineAppearance.withPatch(patch: AppearancePatch?): TimelineAppear compatLinkPreview = patch.getTimelineValue(AppearanceKeys.CompatLinkPreview, compatLinkPreview), showNumbers = patch.getTimelineValue(AppearanceKeys.ShowNumbers, showNumbers), postActionStyle = patch.getTimelineValue(AppearanceKeys.PostActionStyle, postActionStyle), + postActionLayout = patch.getTimelineValue(AppearanceKeys.PostActionLayout, postActionLayout), fullWidthPost = patch.getTimelineValue(AppearanceKeys.FullWidthPost, fullWidthPost), absoluteTimestamp = patch.getTimelineValue(AppearanceKeys.AbsoluteTimestamp, absoluteTimestamp), showPlatformLogo = patch.getTimelineValue(AppearanceKeys.ShowPlatformLogo, showPlatformLogo), diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/PostActionLayoutPreviewHelper.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/PostActionLayoutPreviewHelper.kt new file mode 100644 index 000000000..5e2fcbe84 --- /dev/null +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/PostActionLayoutPreviewHelper.kt @@ -0,0 +1,77 @@ +package dev.dimension.flare.ui.model + +import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily +import kotlinx.collections.immutable.toPersistentList + +public object PostActionLayoutPreviewHelper { + public fun withPreviewActions(post: UiTimelineV2.Post): UiTimelineV2.Post { + var replacedMore = false + val previewActions = + post.actions + .map { action -> + val item = action as? ActionMenu.Item + if (item?.isMoreMenuDisplayItem() == true) { + replacedMore = true + previewMoreGroup(item) + } else if (item?.actionFamily == PostActionFamily.Repost) { + previewRepostGroup(item) + } else { + action + } + }.let { actions -> + if (replacedMore) actions else actions + previewMoreGroup() + } + return post.copy(actions = previewActions.toPersistentList()) + } + + private fun ActionMenu.Item.isMoreMenuDisplayItem(): Boolean = + actionFamily == null && + icon == UiIcon.More && + text == ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.More) + + private fun previewMoreGroup( + displayItem: ActionMenu.Item = + ActionMenu.Item( + icon = UiIcon.More, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.More), + ), + ): ActionMenu.Group = + ActionMenu.Group( + displayItem = displayItem, + actions = + listOf( + ActionMenu.Item( + icon = UiIcon.Translate, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Translate), + actionFamily = PostActionFamily.Translate, + ), + ActionMenu.Item( + icon = UiIcon.Bookmark, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Bookmark), + count = UiNumber(4), + actionFamily = PostActionFamily.Bookmark, + ), + ActionMenu.Item( + icon = UiIcon.Share, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Share), + actionFamily = PostActionFamily.Share, + ), + ).toPersistentList(), + ) + + private fun previewRepostGroup(displayItem: ActionMenu.Item): ActionMenu.Group = + ActionMenu.Group( + displayItem = displayItem, + actions = + listOf( + displayItem, + ActionMenu.Item( + icon = UiIcon.Quote, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Quote), + count = UiNumber(2), + actionFamily = PostActionFamily.Quote, + ), + ).toPersistentList(), + ) +} diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiStatus.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiStatus.kt index 1cf6216f7..ce1a3e887 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiStatus.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/UiStatus.kt @@ -3,6 +3,7 @@ package dev.dimension.flare.ui.model import com.fleeksoft.ksoup.nodes.Element import com.fleeksoft.ksoup.nodes.TextNode import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.model.AccountType import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.ui.render.toUi @@ -45,16 +46,19 @@ public fun createSampleStatus(user: UiProfile): UiTimelineV2.Post = icon = UiIcon.Reply, text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Reply), count = UiNumber(10), + actionFamily = PostActionFamily.Reply, ), ActionMenu.Item( icon = UiIcon.Retweet, text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Retweet), count = UiNumber(20), + actionFamily = PostActionFamily.Repost, ), ActionMenu.Item( icon = UiIcon.Like, text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Like), count = UiNumber(30), + actionFamily = PostActionFamily.Like, ), ActionMenu.Item( icon = UiIcon.More, diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/BlueskyActionMenu.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/BlueskyActionMenu.kt index 08b186442..65e13dd3c 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/BlueskyActionMenu.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/BlueskyActionMenu.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.ui.model.mapper import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.ui.model.ClickEvent @@ -41,6 +42,7 @@ public fun ActionMenu.Companion.blueskyReblog( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Repost, ) public fun ActionMenu.Companion.blueskyLike( @@ -77,6 +79,7 @@ public fun ActionMenu.Companion.blueskyLike( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Like, ) public fun ActionMenu.Companion.blueskyBookmark( @@ -112,4 +115,5 @@ public fun ActionMenu.Companion.blueskyBookmark( count = count, ) }, + actionFamily = PostActionFamily.Bookmark, ) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/MastodonActionMenu.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/MastodonActionMenu.kt index e5cd44001..7e69e5497 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/MastodonActionMenu.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/MastodonActionMenu.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.ui.model.mapper import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.ui.model.ClickEvent @@ -37,6 +38,7 @@ public fun ActionMenu.Companion.mastodonLike( count = favouritesCount, ) }, + actionFamily = PostActionFamily.Like, ) public fun ActionMenu.Companion.mastodonRepost( @@ -69,6 +71,7 @@ public fun ActionMenu.Companion.mastodonRepost( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Repost, ) public fun ActionMenu.Companion.mastodonBookmark( @@ -103,4 +106,5 @@ public fun ActionMenu.Companion.mastodonBookmark( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Bookmark, ) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/MisskeyActionMenu.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/MisskeyActionMenu.kt index 02a3ffdb6..6750a3ad6 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/MisskeyActionMenu.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/MisskeyActionMenu.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.ui.model.mapper import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.model.AccountType import dev.dimension.flare.model.MicroBlogKey @@ -29,6 +30,7 @@ public fun ActionMenu.Companion.misskeyRenote( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Repost, ) public fun ActionMenu.Companion.misskeyReact( @@ -75,6 +77,7 @@ public fun ActionMenu.Companion.misskeyReact( ) } }, + actionFamily = PostActionFamily.React, ) public fun ActionMenu.Companion.misskeyFavourite( @@ -105,4 +108,5 @@ public fun ActionMenu.Companion.misskeyFavourite( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Favorite, ) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Nostr.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Nostr.kt index 0c2aebbf4..2e15cc29c 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Nostr.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Nostr.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.ui.model.mapper import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.ui.model.ClickEvent @@ -35,6 +36,7 @@ public fun ActionMenu.Companion.nostrRepost( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Repost, ) public fun ActionMenu.Companion.nostrLike( @@ -65,4 +67,5 @@ public fun ActionMenu.Companion.nostrLike( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Like, ) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/PixivActionMenu.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/PixivActionMenu.kt index a6e096d70..d8835e8ae 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/PixivActionMenu.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/PixivActionMenu.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.ui.model.mapper import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.ui.model.ClickEvent @@ -32,4 +33,5 @@ public fun ActionMenu.Companion.pixivBookmark( accountKey = accountKey, ), ), + actionFamily = PostActionFamily.Bookmark, ) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/VVOActionMenu.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/VVOActionMenu.kt index a3122c8de..4546320c7 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/VVOActionMenu.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/VVOActionMenu.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.ui.model.mapper import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.ui.model.ClickEvent @@ -32,6 +33,7 @@ public fun ActionMenu.Companion.vvoLike( accountKey = accountKey, ), ), + actionFamily = PostActionFamily.Like, ) public fun ActionMenu.Companion.vvoLikeComment( @@ -59,6 +61,7 @@ public fun ActionMenu.Companion.vvoLikeComment( accountKey = accountKey, ), ), + actionFamily = PostActionFamily.Like, ) public fun ActionMenu.Companion.vvoFavorite( @@ -83,4 +86,5 @@ public fun ActionMenu.Companion.vvoFavorite( accountKey = accountKey, ), ), + actionFamily = PostActionFamily.Favorite, ) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQTActionMenu.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQTActionMenu.kt index f59d7ac7a..794ddb2d2 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQTActionMenu.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQTActionMenu.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.ui.model.mapper import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.model.MicroBlogKey import dev.dimension.flare.ui.model.ClickEvent @@ -32,6 +33,7 @@ public fun ActionMenu.Companion.xqtRetweet( accountKey = accountKey, ), ), + actionFamily = PostActionFamily.Repost, ) public fun ActionMenu.Companion.xqtLike( @@ -59,6 +61,7 @@ public fun ActionMenu.Companion.xqtLike( accountKey = accountKey, ), ), + actionFamily = PostActionFamily.Like, ) public fun ActionMenu.Companion.xqtBookmark( @@ -85,4 +88,5 @@ public fun ActionMenu.Companion.xqtBookmark( accountKey = accountKey, ), ), + actionFamily = PostActionFamily.Bookmark, ) diff --git a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/presenter/SettingsPresenter.kt b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/presenter/SettingsPresenter.kt index 35e921975..e2888b1a3 100644 --- a/shared/src/commonMain/kotlin/dev/dimension/flare/ui/presenter/SettingsPresenter.kt +++ b/shared/src/commonMain/kotlin/dev/dimension/flare/ui/presenter/SettingsPresenter.kt @@ -3,6 +3,7 @@ package dev.dimension.flare.ui.presenter import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.rememberCoroutineScope +import dev.dimension.flare.data.datasource.microblog.PostActionLayoutConfig import dev.dimension.flare.data.datastore.model.AppSettings import dev.dimension.flare.data.model.AvatarShape import dev.dimension.flare.data.model.PostActionStyle @@ -102,6 +103,8 @@ public class SettingsPresenter : override fun updatePostActionStyle(value: PostActionStyle) = update(AppearanceKeys.PostActionStyle, value) + override fun updatePostActionLayout(value: PostActionLayoutConfig) = update(AppearanceKeys.PostActionLayout, value) + override fun updateShowNumbers(value: Boolean) = update(AppearanceKeys.ShowNumbers, value) override fun updateAppSettings(block: AppSettings.() -> AppSettings) { @@ -166,6 +169,8 @@ public class SettingsPresenter : public fun updatePostActionStyle(value: PostActionStyle) + public fun updatePostActionLayout(value: PostActionLayoutConfig) + public fun updateShowNumbers(value: Boolean) @WebIgnore diff --git a/shared/src/commonTest/kotlin/dev/dimension/flare/data/datasource/microblog/PostActionLayoutConfigTest.kt b/shared/src/commonTest/kotlin/dev/dimension/flare/data/datasource/microblog/PostActionLayoutConfigTest.kt new file mode 100644 index 000000000..3fab7b5ac --- /dev/null +++ b/shared/src/commonTest/kotlin/dev/dimension/flare/data/datasource/microblog/PostActionLayoutConfigTest.kt @@ -0,0 +1,126 @@ +package dev.dimension.flare.data.datasource.microblog + +import dev.dimension.flare.ui.model.UiIcon +import kotlinx.collections.immutable.persistentListOf +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertIs + +class PostActionLayoutConfigTest { + @Test + fun disabledConfigKeepsOriginalActions() { + val actions = + persistentListOf( + action(PostActionFamily.Reply, UiIcon.Reply), + ActionMenu.Group( + displayItem = moreItem(), + actions = persistentListOf(action(PostActionFamily.Share, UiIcon.Share)), + ), + ) + + assertEquals(actions, actions.applyPostActionLayout(PostActionLayoutConfig.Default)) + } + + @Test + fun enabledConfigMovesGroupsAndKeepsUnknownActionsInOverflow() { + val unknown = ActionMenu.Item(icon = UiIcon.Info) + val repostGroup = + ActionMenu.Group( + displayItem = action(PostActionFamily.Repost, UiIcon.Retweet), + actions = + persistentListOf( + action(PostActionFamily.Repost, UiIcon.Retweet), + action(PostActionFamily.Quote, UiIcon.Quote), + ), + ) + val actions = + persistentListOf( + action(PostActionFamily.Reply, UiIcon.Reply), + repostGroup, + action(PostActionFamily.Like, UiIcon.Like), + ActionMenu.Group( + displayItem = moreItem(), + actions = + persistentListOf( + action(PostActionFamily.Bookmark, UiIcon.Bookmark), + ActionMenu.Divider, + action(PostActionFamily.Share, UiIcon.Share), + ActionMenu.Divider, + action(PostActionFamily.Delete, UiIcon.Delete), + unknown, + ), + ), + ) + val config = + PostActionLayoutConfig( + enabled = true, + primary = + persistentListOf( + PostActionFamily.Like, + PostActionFamily.Bookmark, + PostActionFamily.Reply, + ), + overflow = + persistentListOf( + PostActionFamily.Share, + ), + hidden = persistentListOf(PostActionFamily.Delete), + ) + + val result = actions.applyPostActionLayout(config) + + assertEquals( + listOf(PostActionFamily.Like, PostActionFamily.Bookmark, PostActionFamily.Reply), + result.take(3).map { (it as ActionMenu.Item).actionFamily }, + ) + val more = assertIs(result.last()) + assertEquals( + listOf(PostActionFamily.Share, PostActionFamily.Repost, null), + more.actions.map { + when (it) { + is ActionMenu.Item -> it.actionFamily + is ActionMenu.Group -> it.displayItem.actionFamily + ActionMenu.Divider -> null + } + }, + ) + assertEquals(repostGroup, more.actions[1]) + assertFalse(more.actions.any { (it as? ActionMenu.Item)?.actionFamily == PostActionFamily.Delete }) + } + + @Test + fun emptyOverflowDoesNotGenerateMoreGroup() { + val actions = + persistentListOf( + action(PostActionFamily.Reply, UiIcon.Reply), + action(PostActionFamily.Like, UiIcon.Like), + ) + val config = + PostActionLayoutConfig( + enabled = true, + primary = persistentListOf(PostActionFamily.Reply, PostActionFamily.Like), + overflow = persistentListOf(), + ) + + val result = actions.applyPostActionLayout(config) + + assertEquals(2, result.size) + assertFalse(result.any { it is ActionMenu.Group }) + } + + private fun action( + family: PostActionFamily, + icon: UiIcon, + ): ActionMenu.Item = + ActionMenu.Item( + icon = icon, + actionFamily = family, + ) + + private fun moreItem(): ActionMenu.Item = + ActionMenu.Item( + icon = UiIcon.More, + text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.More), + ) +} diff --git a/shared/src/commonTest/kotlin/dev/dimension/flare/data/model/appearance/AppearancePatchTest.kt b/shared/src/commonTest/kotlin/dev/dimension/flare/data/model/appearance/AppearancePatchTest.kt index 773ccb42d..7829133c4 100644 --- a/shared/src/commonTest/kotlin/dev/dimension/flare/data/model/appearance/AppearancePatchTest.kt +++ b/shared/src/commonTest/kotlin/dev/dimension/flare/data/model/appearance/AppearancePatchTest.kt @@ -1,5 +1,7 @@ package dev.dimension.flare.data.model.appearance +import dev.dimension.flare.data.datasource.microblog.PostActionFamily +import dev.dimension.flare.data.datasource.microblog.PostActionLayoutConfig import dev.dimension.flare.data.model.AppearanceSettings import dev.dimension.flare.data.model.AvatarShape import dev.dimension.flare.data.model.BottomBarBehavior @@ -169,6 +171,7 @@ class AppearancePatchTest { AppearanceKeys.ShowBottomBarLabels, AppearanceKeys.DeckMode, AppearanceKeys.ExpandContentWarning, + AppearanceKeys.PostActionLayout, ) val activeFields = AppearanceSettings @@ -226,6 +229,15 @@ class AppearancePatchTest { .set(AppearanceKeys.ExpandContentWarning, true) .set(AppearanceKeys.VideoAutoplay, VideoAutoplay.ALWAYS) .set(AppearanceKeys.PostActionStyle, PostActionStyle.Stretch) + .set( + AppearanceKeys.PostActionLayout, + PostActionLayoutConfig( + enabled = true, + primary = kotlinx.collections.immutable.persistentListOf(PostActionFamily.Like), + overflow = kotlinx.collections.immutable.persistentListOf(PostActionFamily.Share), + hidden = kotlinx.collections.immutable.persistentListOf(PostActionFamily.Report), + ), + ) assertEquals(patch, patch.toBag().toPatch()) } diff --git a/social/bluesky/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Bluesky.kt b/social/bluesky/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Bluesky.kt index e78acd6d7..e1786ce7e 100644 --- a/social/bluesky/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Bluesky.kt +++ b/social/bluesky/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Bluesky.kt @@ -32,6 +32,7 @@ import chat.bsky.convo.DeletedMessageView import chat.bsky.convo.MessageView import dev.dimension.flare.common.SerializableImmutableList import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.userActionsMenu import dev.dimension.flare.model.AccountType import dev.dimension.flare.model.MicroBlogKey @@ -846,6 +847,7 @@ internal fun PostView.render(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Reply, ), ActionMenu.Group( displayItem = @@ -886,6 +888,7 @@ internal fun PostView.render(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Quote, ), ).toImmutableList(), ), @@ -929,6 +932,7 @@ internal fun PostView.render(accountKey: MicroBlogKey): UiTimelineV2.Post { fxShareUrl = fxUrl, ), ), + actionFamily = PostActionFamily.Share, ), ) @@ -949,6 +953,7 @@ internal fun PostView.render(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Delete, ), ) } else { @@ -977,6 +982,7 @@ internal fun PostView.render(accountKey: MicroBlogKey): UiTimelineV2.Post { ), ), ), + actionFamily = PostActionFamily.Report, ), ) } @@ -1501,6 +1507,7 @@ private fun render( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Reply, ), ActionMenu.Group( displayItem = @@ -1531,6 +1538,7 @@ private fun render( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Quote, ), ).toImmutableList(), ), @@ -1563,6 +1571,7 @@ private fun render( fxShareUrl = fxUrl, ), ), + actionFamily = PostActionFamily.Share, ), if (isFromMe) { ActionMenu.Item( @@ -1579,6 +1588,7 @@ private fun render( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Delete, ) } else { ActionMenu.Item( @@ -1595,6 +1605,7 @@ private fun render( ), ), ), + actionFamily = PostActionFamily.Report, ) }, ).toImmutableList(), diff --git a/social/mastodon/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Mastodon.kt b/social/mastodon/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Mastodon.kt index 0de5e109b..d52e422ae 100644 --- a/social/mastodon/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Mastodon.kt +++ b/social/mastodon/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Mastodon.kt @@ -3,6 +3,7 @@ package dev.dimension.flare.ui.model.mapper import com.fleeksoft.ksoup.nodes.Element import com.fleeksoft.ksoup.nodes.Node import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.data.datasource.microblog.userActionsMenu import dev.dimension.flare.data.network.mastodon.api.model.Account @@ -468,6 +469,7 @@ private fun Status.renderStatus( ), ) }, + actionFamily = PostActionFamily.Reply, ), ) if (canReblog && canQuote && accountKey != null) { @@ -496,6 +498,7 @@ private fun Status.renderStatus( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Quote, ), ) } @@ -528,6 +531,7 @@ private fun Status.renderStatus( ), ) }, + actionFamily = PostActionFamily.Quote, ), ) } else if (canReblog) { @@ -574,6 +578,7 @@ private fun Status.renderStatus( ), ) }, + actionFamily = PostActionFamily.React, ), ) } @@ -628,6 +633,7 @@ private fun Status.renderStatus( shareUrl = postUrl, ), ), + actionFamily = PostActionFamily.Share, ), ) @@ -648,6 +654,7 @@ private fun Status.renderStatus( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Delete, ), ) } else { @@ -679,6 +686,7 @@ private fun Status.renderStatus( }, ), ), + actionFamily = PostActionFamily.Report, ), ) } diff --git a/social/misskey/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Misskey.kt b/social/misskey/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Misskey.kt index 3314f1c60..9fe330c63 100644 --- a/social/misskey/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Misskey.kt +++ b/social/misskey/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/Misskey.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.ui.model.mapper import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.PostEvent import dev.dimension.flare.data.datasource.microblog.userActionsMenu import dev.dimension.flare.data.network.misskey.api.model.Antenna @@ -549,6 +550,7 @@ private fun Note.renderStatus(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Reply, ), if (canReblog) { ActionMenu.Group( @@ -577,6 +579,7 @@ private fun Note.renderStatus(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Quote, ), ).toImmutableList(), ) @@ -618,6 +621,7 @@ private fun Note.renderStatus(accountKey: MicroBlogKey): UiTimelineV2.Post { shareUrl = postUrl, ), ), + actionFamily = PostActionFamily.Share, ), ) if (isFromMe) { @@ -637,6 +641,7 @@ private fun Note.renderStatus(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Delete, ), ) } else { @@ -666,6 +671,7 @@ private fun Note.renderStatus(accountKey: MicroBlogKey): UiTimelineV2.Post { ), ), ), + actionFamily = PostActionFamily.Report, ), ) } diff --git a/social/nostr/src/commonMain/kotlin/dev/dimension/flare/data/network/nostr/NostrService.kt b/social/nostr/src/commonMain/kotlin/dev/dimension/flare/data/network/nostr/NostrService.kt index 92957519f..41b4d3d01 100644 --- a/social/nostr/src/commonMain/kotlin/dev/dimension/flare/data/network/nostr/NostrService.kt +++ b/social/nostr/src/commonMain/kotlin/dev/dimension/flare/data/network/nostr/NostrService.kt @@ -4,6 +4,7 @@ import dev.dimension.flare.common.FileType import dev.dimension.flare.common.JSON import dev.dimension.flare.common.jsonObjectOrNull import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.userActionsMenu import dev.dimension.flare.data.datasource.nostr.NostrCache import dev.dimension.flare.data.platform.NostrCredential @@ -1792,6 +1793,7 @@ internal class NostrService( ), ), count = UiNumber(0L), + actionFamily = PostActionFamily.Reply, ), ) add( @@ -1821,6 +1823,7 @@ internal class NostrService( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Quote, ), ).toImmutableList(), ), @@ -1855,6 +1858,7 @@ internal class NostrService( shareUrl = statusShareUrl(statusKey.id), ), ), + actionFamily = PostActionFamily.Share, ), ) if (canSign && pubKey == accountKey.id) { @@ -1873,6 +1877,7 @@ internal class NostrService( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Delete, ), ) } else { @@ -1897,6 +1902,7 @@ internal class NostrService( accountKey = accountKey, ) }, + actionFamily = PostActionFamily.Report, ), ) } diff --git a/social/pixiv/src/commonMain/kotlin/dev/dimension/flare/data/datasource/pixiv/PixivMapper.kt b/social/pixiv/src/commonMain/kotlin/dev/dimension/flare/data/datasource/pixiv/PixivMapper.kt index d2e8f7e86..cc0ae7c6a 100644 --- a/social/pixiv/src/commonMain/kotlin/dev/dimension/flare/data/datasource/pixiv/PixivMapper.kt +++ b/social/pixiv/src/commonMain/kotlin/dev/dimension/flare/data/datasource/pixiv/PixivMapper.kt @@ -1,6 +1,7 @@ package dev.dimension.flare.data.datasource.pixiv import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.datasource.GalleryDetail import dev.dimension.flare.data.datasource.microblog.datasource.GalleryOrientation import dev.dimension.flare.data.network.pixiv.PIXIV_IMAGE_REFERER @@ -84,6 +85,7 @@ internal fun PixivIllust.toUiTimeline(accountKey: MicroBlogKey): UiTimelineV2.Po shareUrl = "https://www.pixiv.net/artworks/$id", ), ), + actionFamily = PostActionFamily.Share, ), ), poll = null, diff --git a/social/vvo/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/VVO.kt b/social/vvo/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/VVO.kt index 7cf1b18d7..d6d09fc4c 100644 --- a/social/vvo/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/VVO.kt +++ b/social/vvo/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/VVO.kt @@ -3,6 +3,7 @@ package dev.dimension.flare.ui.model.mapper import com.fleeksoft.ksoup.nodes.Element import com.fleeksoft.ksoup.nodes.Node import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.network.vvo.model.Attitude import dev.dimension.flare.data.network.vvo.model.Comment import dev.dimension.flare.data.network.vvo.model.Status @@ -207,6 +208,7 @@ private fun Status.renderStatusV2(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Quote, ) } else { null @@ -222,6 +224,7 @@ private fun Status.renderStatusV2(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Comment, ), ActionMenu.vvoLike( statusKey = statusKey, @@ -253,6 +256,7 @@ private fun Status.renderStatusV2(accountKey: MicroBlogKey): UiTimelineV2.Post { shareUrl = url, ), ), + actionFamily = PostActionFamily.Share, ), if (isFromMe) { ActionMenu.Item( @@ -266,6 +270,7 @@ private fun Status.renderStatusV2(accountKey: MicroBlogKey): UiTimelineV2.Post { statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Delete, ) } else { ActionMenu.Item( @@ -273,6 +278,7 @@ private fun Status.renderStatusV2(accountKey: MicroBlogKey): UiTimelineV2.Post { text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Report), color = ActionMenu.Item.Color.Red, clickEvent = ClickEvent.Noop, + actionFamily = PostActionFamily.Report, ) }, ).toImmutableList(), @@ -372,6 +378,7 @@ private fun Comment.renderStatusV2(accountKey: MicroBlogKey): UiTimelineV2.Post rootId = statusMid, ), ), + actionFamily = PostActionFamily.Comment, ) }, ActionMenu.vvoLikeComment( @@ -399,6 +406,7 @@ private fun Comment.renderStatusV2(accountKey: MicroBlogKey): UiTimelineV2.Post shareUrl = url, ), ), + actionFamily = PostActionFamily.Share, ), if (isFromMe) { ActionMenu.Item( @@ -412,6 +420,7 @@ private fun Comment.renderStatusV2(accountKey: MicroBlogKey): UiTimelineV2.Post statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Delete, ) } else { null diff --git a/social/xqt/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQT.kt b/social/xqt/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQT.kt index d5b0fcfba..ad9573331 100644 --- a/social/xqt/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQT.kt +++ b/social/xqt/src/commonMain/kotlin/dev/dimension/flare/ui/model/mapper/XQT.kt @@ -6,6 +6,7 @@ import dev.dimension.flare.common.decodeJson import dev.dimension.flare.common.encodeJson import dev.dimension.flare.data.database.cache.mapper.XQTTimeline import dev.dimension.flare.data.datasource.microblog.ActionMenu +import dev.dimension.flare.data.datasource.microblog.PostActionFamily import dev.dimension.flare.data.datasource.microblog.userActionsMenu import dev.dimension.flare.data.network.xqt.model.Admin import dev.dimension.flare.data.network.xqt.model.AudioSpace @@ -669,6 +670,7 @@ internal fun Tweet.renderStatus( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Reply, ), ActionMenu.Group( displayItem = @@ -698,6 +700,7 @@ internal fun Tweet.renderStatus( statusKey = statusKey, ), ), + actionFamily = PostActionFamily.Quote, ), ).toImmutableList(), ), @@ -738,6 +741,7 @@ internal fun Tweet.renderStatus( fixvxShareUrl = fixvxUrl, ), ), + actionFamily = PostActionFamily.Share, ), ) @@ -757,6 +761,7 @@ internal fun Tweet.renderStatus( .Specific(accountKey), ), ), + actionFamily = PostActionFamily.Delete, ), ) } else { @@ -777,6 +782,7 @@ internal fun Tweet.renderStatus( text = ActionMenu.Item.Text.Localized(ActionMenu.Item.Text.Localized.Type.Report), color = ActionMenu.Item.Color.Red, clickEvent = ClickEvent.Noop, + actionFamily = PostActionFamily.Report, ), ) }