mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:55:41 +00:00
Merge branch 'develop' into feature/bma/scroll-to-unread-messages
This commit is contained in:
+36
@@ -190,6 +190,9 @@ class MessagesFlowNode(
|
||||
|
||||
@Parcelize
|
||||
data object ThreadsList : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class AvatarPreview(val name: String, val avatarUrl: String) : NavTarget
|
||||
}
|
||||
|
||||
private val callback: MessagesEntryPoint.Callback = callback()
|
||||
@@ -327,6 +330,10 @@ class MessagesFlowNode(
|
||||
override fun navigateToDeveloperSettings() {
|
||||
callback.navigateToDeveloperSettings()
|
||||
}
|
||||
|
||||
override fun navigateToAvatarPreview(username: String, avatarUrl: String) {
|
||||
overlay.show(NavTarget.AvatarPreview(username, avatarUrl))
|
||||
}
|
||||
}
|
||||
val inputs = MessagesNode.Inputs(focusedEventId = navTarget.focusedEventId)
|
||||
createNode<MessagesNode>(buildContext, listOf(callback, inputs))
|
||||
@@ -562,6 +569,10 @@ class MessagesFlowNode(
|
||||
override fun navigateToDeveloperSettings() {
|
||||
callback.navigateToDeveloperSettings()
|
||||
}
|
||||
|
||||
override fun navigateToAvatarPreview(username: String, avatarUrl: String) {
|
||||
overlay.show(NavTarget.AvatarPreview(username, avatarUrl))
|
||||
}
|
||||
}
|
||||
createNode<ThreadedMessagesNode>(buildContext, listOf(inputs, callback))
|
||||
}
|
||||
@@ -573,6 +584,31 @@ class MessagesFlowNode(
|
||||
}
|
||||
createNode<ThreadsListNode>(buildContext, listOf(callback))
|
||||
}
|
||||
is NavTarget.AvatarPreview -> {
|
||||
val callback = object : MediaViewerEntryPoint.Callback {
|
||||
override fun onDone() {
|
||||
overlay.hide()
|
||||
}
|
||||
|
||||
override fun viewInTimeline(eventId: EventId) {
|
||||
// Cannot happen
|
||||
}
|
||||
|
||||
override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) {
|
||||
// Cannot happen
|
||||
}
|
||||
}
|
||||
val params = mediaViewerEntryPoint.createParamsForAvatar(
|
||||
filename = navTarget.name,
|
||||
avatarUrl = navTarget.avatarUrl,
|
||||
)
|
||||
mediaViewerEntryPoint.createNode(
|
||||
parentNode = this,
|
||||
buildContext = buildContext,
|
||||
params = params,
|
||||
callback = callback,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-1
@@ -66,6 +66,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.alias.matches
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.matrix.ui.model.getBestName
|
||||
import io.element.android.libraries.mediaplayer.api.MediaPlayer
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard
|
||||
@@ -107,7 +108,7 @@ class MessagesNode(
|
||||
private val timelineController = TimelineController(room, room.liveTimeline)
|
||||
private val presenter = presenterFactory.create(
|
||||
navigator = this,
|
||||
composerPresenter = messageComposerPresenterFactory.create(timelineController, this, isInThread = false),
|
||||
composerPresenter = messageComposerPresenterFactory.create(timelineController, this, threadRoot = null),
|
||||
timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this),
|
||||
actionListPresenter = actionListPresenterFactory.create(
|
||||
postProcessor = TimelineItemActionPostProcessor.Default,
|
||||
@@ -136,6 +137,8 @@ class MessagesNode(
|
||||
fun navigateToDeveloperSettings()
|
||||
|
||||
fun navigateToThreadsList()
|
||||
|
||||
fun navigateToAvatarPreview(username: String, avatarUrl: String)
|
||||
}
|
||||
|
||||
override fun onBuilt() {
|
||||
@@ -319,6 +322,11 @@ class MessagesNode(
|
||||
else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target))
|
||||
}
|
||||
},
|
||||
onAvatarClick = { user ->
|
||||
user.avatarUrl?.let { url ->
|
||||
callback.navigateToAvatarPreview(user.getBestName(), url)
|
||||
}
|
||||
},
|
||||
modifier = Modifier,
|
||||
)
|
||||
|
||||
|
||||
+7
-8
@@ -49,6 +49,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatch
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
|
||||
import io.element.android.libraries.di.annotations.SessionCoroutineScope
|
||||
import io.element.android.libraries.matrix.api.core.EventId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||
@@ -109,7 +110,7 @@ import io.element.android.libraries.core.mimetype.MimeTypes.Any as AnyMimeTypes
|
||||
class MessageComposerPresenter(
|
||||
@Assisted private val navigator: MessagesNavigator,
|
||||
@Assisted private val timelineController: TimelineController,
|
||||
@Assisted private val isInThread: Boolean,
|
||||
@Assisted private val threadRoot: ThreadId?,
|
||||
@SessionCoroutineScope private val sessionCoroutineScope: CoroutineScope,
|
||||
private val room: JoinedRoom,
|
||||
private val mediaPickerProvider: PickerProvider,
|
||||
@@ -138,10 +139,11 @@ class MessageComposerPresenter(
|
||||
fun create(
|
||||
timelineController: TimelineController,
|
||||
navigator: MessagesNavigator,
|
||||
isInThread: Boolean,
|
||||
threadRoot: ThreadId?,
|
||||
): MessageComposerPresenter
|
||||
}
|
||||
|
||||
private val isInThread = threadRoot != null
|
||||
private val mediaSender = mediaSenderFactory.create(timelineMode = timelineController.mainTimelineMode())
|
||||
|
||||
private val cameraPermissionPresenter = permissionsPresenterFactory.create(Manifest.permission.CAMERA)
|
||||
@@ -234,8 +236,7 @@ class MessageComposerPresenter(
|
||||
LaunchedEffect(Unit) {
|
||||
val draft = draftService.loadDraft(
|
||||
roomId = room.roomId,
|
||||
// TODO support threads in composer
|
||||
threadRoot = null,
|
||||
threadRoot = threadRoot,
|
||||
isVolatile = false
|
||||
)
|
||||
if (draft != null) {
|
||||
@@ -652,8 +653,7 @@ class MessageComposerPresenter(
|
||||
roomId = room.roomId,
|
||||
draft = draft,
|
||||
isVolatile = isVolatile,
|
||||
// TODO support threads in composer
|
||||
threadRoot = null,
|
||||
threadRoot = threadRoot,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -816,8 +816,7 @@ class MessageComposerPresenter(
|
||||
// Use the volatile draft only when coming from edit mode otherwise.
|
||||
val draft = draftService.loadDraft(
|
||||
roomId = room.roomId,
|
||||
// TODO support threads in composer
|
||||
threadRoot = null,
|
||||
threadRoot = threadRoot,
|
||||
isVolatile = true
|
||||
).takeIf { fromEdit }
|
||||
if (draft != null) {
|
||||
|
||||
+9
-1
@@ -67,6 +67,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.alias.matches
|
||||
import io.element.android.libraries.matrix.api.timeline.Timeline
|
||||
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
|
||||
import io.element.android.libraries.matrix.ui.model.getBestName
|
||||
import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard
|
||||
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
@@ -112,7 +113,7 @@ class ThreadedMessagesNode(
|
||||
this.timelineController = timelineController
|
||||
return presenterFactory.create(
|
||||
navigator = this,
|
||||
composerPresenter = messageComposerPresenterFactory.create(timelineController, this, isInThread = true),
|
||||
composerPresenter = messageComposerPresenterFactory.create(timelineController, this, threadRoot = inputs.threadRootEventId),
|
||||
timelinePresenter = timelinePresenterFactory.create(timelineController = timelineController, this),
|
||||
// TODO add special processor for threaded timeline
|
||||
actionListPresenter = actionListPresenterFactory.create(
|
||||
@@ -138,6 +139,8 @@ class ThreadedMessagesNode(
|
||||
fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean)
|
||||
fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?)
|
||||
fun navigateToDeveloperSettings()
|
||||
|
||||
fun navigateToAvatarPreview(username: String, avatarUrl: String)
|
||||
}
|
||||
|
||||
override fun onBuilt() {
|
||||
@@ -315,6 +318,11 @@ class ThreadedMessagesNode(
|
||||
else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target))
|
||||
}
|
||||
},
|
||||
onAvatarClick = { user ->
|
||||
user.avatarUrl?.let { url ->
|
||||
callback.navigateToAvatarPreview(user.getBestName(), url)
|
||||
}
|
||||
},
|
||||
modifier = Modifier,
|
||||
)
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
<string name="emoji_picker_category_symbols">"Сімвалы"</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Не атрымалася апрацаваць медыяфайл для загрузкі, паспрабуйце яшчэ раз."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Не атрымалася загрузіць медыяфайлы, паспрабуйце яшчэ раз."</string>
|
||||
<string name="screen_media_upload_preview_processing">"Апрацоўка…"</string>
|
||||
<string name="screen_media_upload_preview_title">"Дадаць медыя"</string>
|
||||
<string name="screen_report_content_block_user">"Заблакіраваць карыстальніка"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Адзначце, ці хочаце вы схаваць усе бягучыя і будучыя паведамленні ад гэтага карыстальніка"</string>
|
||||
<string name="screen_report_content_explanation">"Гэтае паведамленне будзе перададзена адміністратару вашага хатняга сервера. Яны не змогуць прачытаць зашыфраваныя паведамленні."</string>
|
||||
@@ -25,7 +27,7 @@
|
||||
<string name="screen_room_attachment_source_camera_video">"Запісаць відэа"</string>
|
||||
<string name="screen_room_attachment_source_files">"Далучэнне"</string>
|
||||
<string name="screen_room_attachment_source_gallery">"Бібліятэка фота & відэа"</string>
|
||||
<string name="screen_room_attachment_source_location">"Месцазнаходжанне"</string>
|
||||
<string name="screen_room_attachment_source_location">"Абагуліць месцазнаходжанне"</string>
|
||||
<string name="screen_room_attachment_source_poll">"Апытанне"</string>
|
||||
<string name="screen_room_attachment_text_formatting">"Фармаціраванне тэксту"</string>
|
||||
<string name="screen_room_encrypted_history_banner">"Гісторыя паведамленняў зараз недаступна."</string>
|
||||
|
||||
@@ -16,6 +16,12 @@
|
||||
<string name="emoji_picker_category_places">"Путешествия и места"</string>
|
||||
<string name="emoji_picker_category_recent">"Недавние эмодзи"</string>
|
||||
<string name="emoji_picker_category_symbols">"Символы"</string>
|
||||
<string name="screen_image_edition_a11y_flip_image_horizontally">"Перевернуть изображение по горизонтали"</string>
|
||||
<string name="screen_image_edition_a11y_flip_image_horizontally_state_flipped">"Перевернуто по горизонтали"</string>
|
||||
<string name="screen_image_edition_a11y_flip_image_horizontally_state_original">"Оригинал"</string>
|
||||
<string name="screen_image_edition_a11y_flip_image_vertically">"Перевернуть изображение по вертикали"</string>
|
||||
<string name="screen_image_edition_a11y_flip_image_vertically_state_flipped">"Перевернуто по вертикали"</string>
|
||||
<string name="screen_image_edition_a11y_flip_image_vertically_state_original">"Оригинал"</string>
|
||||
<string name="screen_image_edition_a11y_rotate_to_the_left">"Повернуть изображение влево."</string>
|
||||
<plurals name="screen_image_edition_a11y_rotation_state">
|
||||
<item quantity="one">"%1$d градус"</item>
|
||||
@@ -26,8 +32,8 @@
|
||||
<string name="screen_media_upload_preview_caption_warning">"Подпись может быть не видна пользователям старых приложений."</string>
|
||||
<string name="screen_media_upload_preview_change_video_quality_prompt">"Нажмите, чтобы изменить качество загружаемого видео."</string>
|
||||
<string name="screen_media_upload_preview_error_could_not_be_uploaded">"Файл не может быть загружен."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Не удалось обработать медиафайл для загрузки, попробуйте еще раз."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Не удалось загрузить медиафайлы, попробуйте еще раз."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_processing">"Не удалось обработать медиафайл для загрузки, попробуйте ещё раз."</string>
|
||||
<string name="screen_media_upload_preview_error_failed_sending">"Не удалось загрузить медиафайлы, попробуйте ещё раз."</string>
|
||||
<string name="screen_media_upload_preview_error_too_large_message">"Максимальный размер файла: %1$s."</string>
|
||||
<string name="screen_media_upload_preview_error_too_large_title">"Файл слишком большой для загрузки."</string>
|
||||
<string name="screen_media_upload_preview_item_count">"%1$d из %2$d"</string>
|
||||
|
||||
@@ -16,6 +16,13 @@
|
||||
<string name="emoji_picker_category_places">"Подорожі та місця"</string>
|
||||
<string name="emoji_picker_category_recent">"Нещодавні емодзі"</string>
|
||||
<string name="emoji_picker_category_symbols">"Символи"</string>
|
||||
<string name="screen_image_edition_a11y_rotate_to_the_left">"Повернути зображення вліво"</string>
|
||||
<plurals name="screen_image_edition_a11y_rotation_state">
|
||||
<item quantity="one">"%1$d градус"</item>
|
||||
<item quantity="few">"%1$d градуси"</item>
|
||||
<item quantity="many">"%1$d градусів"</item>
|
||||
</plurals>
|
||||
<string name="screen_image_edition_title">"Редагувати фото"</string>
|
||||
<string name="screen_media_upload_preview_caption_warning">"Користувачі старих застосунків можуть не бачити підписи."</string>
|
||||
<string name="screen_media_upload_preview_change_video_quality_prompt">"Натисніть, щоб змінити якість вивантажуваного відео"</string>
|
||||
<string name="screen_media_upload_preview_error_could_not_be_uploaded">"Файл не може бути вивантажено."</string>
|
||||
@@ -26,6 +33,7 @@
|
||||
<string name="screen_media_upload_preview_item_count">"Елемент %1$d з %2$d"</string>
|
||||
<string name="screen_media_upload_preview_optimize_image_quality_title">"Оптимізувати якість зображення"</string>
|
||||
<string name="screen_media_upload_preview_processing">"Обробка…"</string>
|
||||
<string name="screen_media_upload_preview_title">"Додати медіа"</string>
|
||||
<string name="screen_report_content_block_user">"Заблокувати користувача"</string>
|
||||
<string name="screen_report_content_block_user_hint">"Перевірте, чи хочете ви приховати всі поточні та майбутні повідомлення від цього користувача"</string>
|
||||
<string name="screen_report_content_explanation">"Це повідомлення буде надіслано адміністраторам вашого домашнього сервера. Вони не зможуть прочитати зашифровані повідомлення."</string>
|
||||
|
||||
+3
-2
@@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.utils.FakeMentionSpanFormatter
|
||||
import io.element.android.features.messages.impl.utils.FakeTextPillificationHelper
|
||||
import io.element.android.features.messages.impl.utils.TextPillificationHelper
|
||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||
@@ -271,12 +272,12 @@ class MessageComposerPresenterSlashCommandTest {
|
||||
isRichTextEditorEnabled: Boolean = true,
|
||||
draftService: ComposerDraftService = FakeComposerDraftService(),
|
||||
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
|
||||
isInThread: Boolean = false,
|
||||
threadRoot: ThreadId? = null,
|
||||
slashCommandService: SlashCommandService = FakeSlashCommandService(),
|
||||
) = MessageComposerPresenter(
|
||||
navigator = navigator,
|
||||
sessionCoroutineScope = this,
|
||||
isInThread = isInThread,
|
||||
threadRoot = threadRoot,
|
||||
room = room,
|
||||
mediaPickerProvider = pickerProvider,
|
||||
sessionPreferencesStore = sessionPreferencesStore,
|
||||
|
||||
+2
-2
@@ -1534,12 +1534,12 @@ class MessageComposerPresenterTest {
|
||||
isRichTextEditorEnabled: Boolean = true,
|
||||
draftService: ComposerDraftService = FakeComposerDraftService(),
|
||||
mediaOptimizationConfigProvider: FakeMediaOptimizationConfigProvider = FakeMediaOptimizationConfigProvider(),
|
||||
isInThread: Boolean = false,
|
||||
threadRoot: ThreadId? = null,
|
||||
slashCommandService: SlashCommandService = FakeSlashCommandService(),
|
||||
) = MessageComposerPresenter(
|
||||
navigator = navigator,
|
||||
sessionCoroutineScope = this,
|
||||
isInThread = isInThread,
|
||||
threadRoot = threadRoot,
|
||||
room = room,
|
||||
mediaPickerProvider = pickerProvider,
|
||||
sessionPreferencesStore = sessionPreferencesStore,
|
||||
|
||||
Reference in New Issue
Block a user