mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 16:10:20 +00:00
Add a warning to the media caption composer. (#3574)
We can now remove the feature flag.
This commit is contained in:
@@ -387,6 +387,9 @@
|
|||||||
"screen_knock_requests_list_empty_state_description" = "When somebody will ask to join the room, you’ll be able to see their request here.";
|
"screen_knock_requests_list_empty_state_description" = "When somebody will ask to join the room, you’ll be able to see their request here.";
|
||||||
"screen_knock_requests_list_empty_state_title" = "No pending request to join";
|
"screen_knock_requests_list_empty_state_title" = "No pending request to join";
|
||||||
"screen_knock_requests_list_title" = "Requests to join";
|
"screen_knock_requests_list_title" = "Requests to join";
|
||||||
|
"screen_media_upload_preview_caption_warning" = "Captions might not be visible to people using older apps.";
|
||||||
|
"screen_media_upload_preview_error_failed_processing" = "Failed processing media to upload, please try again.";
|
||||||
|
"screen_media_upload_preview_error_failed_sending" = "Failed uploading media, please try again.";
|
||||||
"screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here.";
|
"screen_pinned_timeline_empty_state_description" = "Press on a message and choose “%1$@” to include here.";
|
||||||
"screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered";
|
"screen_pinned_timeline_empty_state_headline" = "Pin important messages so that they can be easily discovered";
|
||||||
"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again.";
|
"screen_reset_encryption_password_error" = "An unknown error happened. Please check your account password is correct and try again.";
|
||||||
@@ -584,8 +587,6 @@
|
|||||||
"screen_login_title" = "Welcome back!";
|
"screen_login_title" = "Welcome back!";
|
||||||
"screen_login_title_with_homeserver" = "Sign in to %1$@";
|
"screen_login_title_with_homeserver" = "Sign in to %1$@";
|
||||||
"screen_media_picker_error_failed_selection" = "Failed selecting media, please try again.";
|
"screen_media_picker_error_failed_selection" = "Failed selecting media, please try again.";
|
||||||
"screen_media_upload_preview_error_failed_processing" = "Failed processing media to upload, please try again.";
|
|
||||||
"screen_media_upload_preview_error_failed_sending" = "Failed uploading media, please try again.";
|
|
||||||
"screen_migration_message" = "This is a one time process, thanks for waiting.";
|
"screen_migration_message" = "This is a one time process, thanks for waiting.";
|
||||||
"screen_migration_title" = "Setting up your account.";
|
"screen_migration_title" = "Setting up your account.";
|
||||||
"screen_notification_optin_subtitle" = "You can change your settings later.";
|
"screen_notification_optin_subtitle" = "You can change your settings later.";
|
||||||
|
|||||||
@@ -240,6 +240,10 @@ final class AppSettings {
|
|||||||
|
|
||||||
@UserPreference(key: UserDefaultsKeys.optimizeMediaUploads, defaultValue: true, storageType: .userDefaults(store))
|
@UserPreference(key: UserDefaultsKeys.optimizeMediaUploads, defaultValue: true, storageType: .userDefaults(store))
|
||||||
var optimizeMediaUploads
|
var optimizeMediaUploads
|
||||||
|
|
||||||
|
/// Whether or not to show a warning on the media caption composer so the user knows
|
||||||
|
/// that captions might not be visible to users who are using other Matrix clients.
|
||||||
|
let shouldShowMediaCaptionWarning = true
|
||||||
|
|
||||||
// MARK: - Element Call
|
// MARK: - Element Call
|
||||||
|
|
||||||
|
|||||||
@@ -997,7 +997,7 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
|
|||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: appSettings),
|
||||||
title: url.lastPathComponent,
|
title: url.lastPathComponent,
|
||||||
url: url,
|
url: url,
|
||||||
createMediaCaptionsEnabled: appSettings.createMediaCaptionsEnabled)
|
shouldShowCaptionWarning: appSettings.shouldShowMediaCaptionWarning)
|
||||||
|
|
||||||
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters)
|
let mediaUploadPreviewScreenCoordinator = MediaUploadPreviewScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
|
|||||||
@@ -1358,6 +1358,8 @@ internal enum L10n {
|
|||||||
}
|
}
|
||||||
/// Failed selecting media, please try again.
|
/// Failed selecting media, please try again.
|
||||||
internal static var screenMediaPickerErrorFailedSelection: String { return L10n.tr("Localizable", "screen_media_picker_error_failed_selection") }
|
internal static var screenMediaPickerErrorFailedSelection: String { return L10n.tr("Localizable", "screen_media_picker_error_failed_selection") }
|
||||||
|
/// Captions might not be visible to people using older apps.
|
||||||
|
internal static var screenMediaUploadPreviewCaptionWarning: String { return L10n.tr("Localizable", "screen_media_upload_preview_caption_warning") }
|
||||||
/// Failed processing media to upload, please try again.
|
/// Failed processing media to upload, please try again.
|
||||||
internal static var screenMediaUploadPreviewErrorFailedProcessing: String { return L10n.tr("Localizable", "screen_media_upload_preview_error_failed_processing") }
|
internal static var screenMediaUploadPreviewErrorFailedProcessing: String { return L10n.tr("Localizable", "screen_media_upload_preview_error_failed_processing") }
|
||||||
/// Failed uploading media, please try again.
|
/// Failed uploading media, please try again.
|
||||||
|
|||||||
+2
-2
@@ -14,7 +14,7 @@ struct MediaUploadPreviewScreenCoordinatorParameters {
|
|||||||
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
let mediaUploadingPreprocessor: MediaUploadingPreprocessor
|
||||||
let title: String?
|
let title: String?
|
||||||
let url: URL
|
let url: URL
|
||||||
let createMediaCaptionsEnabled: Bool
|
let shouldShowCaptionWarning: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaUploadPreviewScreenCoordinatorAction {
|
enum MediaUploadPreviewScreenCoordinatorAction {
|
||||||
@@ -36,7 +36,7 @@ final class MediaUploadPreviewScreenCoordinator: CoordinatorProtocol {
|
|||||||
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
mediaUploadingPreprocessor: parameters.mediaUploadingPreprocessor,
|
||||||
title: parameters.title,
|
title: parameters.title,
|
||||||
url: parameters.url,
|
url: parameters.url,
|
||||||
createMediaCaptionsEnabled: parameters.createMediaCaptionsEnabled)
|
shouldShowCaptionWarning: parameters.shouldShowCaptionWarning)
|
||||||
}
|
}
|
||||||
|
|
||||||
func start() {
|
func start() {
|
||||||
|
|||||||
+3
-1
@@ -14,7 +14,7 @@ enum MediaUploadPreviewScreenViewModelAction {
|
|||||||
struct MediaUploadPreviewScreenViewState: BindableState {
|
struct MediaUploadPreviewScreenViewState: BindableState {
|
||||||
let url: URL
|
let url: URL
|
||||||
let title: String?
|
let title: String?
|
||||||
let showMediaCaptionComposer: Bool
|
let shouldShowCaptionWarning: Bool
|
||||||
var shouldDisableInteraction = false
|
var shouldDisableInteraction = false
|
||||||
|
|
||||||
var bindings = MediaUploadPreviewScreenBindings()
|
var bindings = MediaUploadPreviewScreenBindings()
|
||||||
@@ -23,6 +23,8 @@ struct MediaUploadPreviewScreenViewState: BindableState {
|
|||||||
struct MediaUploadPreviewScreenBindings: BindableState {
|
struct MediaUploadPreviewScreenBindings: BindableState {
|
||||||
var caption = NSAttributedString()
|
var caption = NSAttributedString()
|
||||||
var presendCallback: (() -> Void)?
|
var presendCallback: (() -> Void)?
|
||||||
|
|
||||||
|
var isPresentingMediaCaptionWarning = false
|
||||||
}
|
}
|
||||||
|
|
||||||
enum MediaUploadPreviewScreenViewAction {
|
enum MediaUploadPreviewScreenViewAction {
|
||||||
|
|||||||
+2
-2
@@ -33,13 +33,13 @@ class MediaUploadPreviewScreenViewModel: MediaUploadPreviewScreenViewModelType,
|
|||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor,
|
||||||
title: String?,
|
title: String?,
|
||||||
url: URL,
|
url: URL,
|
||||||
createMediaCaptionsEnabled: Bool) {
|
shouldShowCaptionWarning: Bool) {
|
||||||
self.userIndicatorController = userIndicatorController
|
self.userIndicatorController = userIndicatorController
|
||||||
self.roomProxy = roomProxy
|
self.roomProxy = roomProxy
|
||||||
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
self.mediaUploadingPreprocessor = mediaUploadingPreprocessor
|
||||||
self.url = url
|
self.url = url
|
||||||
|
|
||||||
super.init(initialViewState: MediaUploadPreviewScreenViewState(url: url, title: title, showMediaCaptionComposer: createMediaCaptionsEnabled))
|
super.init(initialViewState: MediaUploadPreviewScreenViewState(url: url, title: title, shouldShowCaptionWarning: shouldShowCaptionWarning))
|
||||||
}
|
}
|
||||||
|
|
||||||
override func process(viewAction: MediaUploadPreviewScreenViewAction) {
|
override func process(viewAction: MediaUploadPreviewScreenViewAction) {
|
||||||
|
|||||||
+68
-26
@@ -14,26 +14,27 @@ struct MediaUploadPreviewScreen: View {
|
|||||||
|
|
||||||
@ObservedObject var context: MediaUploadPreviewScreenViewModel.Context
|
@ObservedObject var context: MediaUploadPreviewScreenViewModel.Context
|
||||||
|
|
||||||
var title: String { ProcessInfo.processInfo.isiOSAppOnMac ? context.viewState.title ?? "" : "" }
|
@State private var captionWarningFrame: CGRect = .zero
|
||||||
var colorSchemeOverride: ColorScheme { ProcessInfo.processInfo.isiOSAppOnMac ? colorScheme : .dark }
|
|
||||||
|
private var title: String { ProcessInfo.processInfo.isiOSAppOnMac ? context.viewState.title ?? "" : "" }
|
||||||
|
private var colorSchemeOverride: ColorScheme { ProcessInfo.processInfo.isiOSAppOnMac ? colorScheme : .dark }
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
mainContent
|
mainContent
|
||||||
.id(context.viewState.url)
|
.id(context.viewState.url)
|
||||||
.ignoresSafeArea(edges: [.horizontal])
|
.ignoresSafeArea(edges: [.horizontal])
|
||||||
.safeAreaInset(edge: .bottom, spacing: 0) {
|
.safeAreaInset(edge: .bottom, spacing: 0) {
|
||||||
if context.viewState.showMediaCaptionComposer {
|
composer
|
||||||
composer
|
.padding(.horizontal, 12)
|
||||||
.padding(.horizontal, 12)
|
.padding(.vertical, 16)
|
||||||
.padding(.vertical, 16)
|
.background() // Don't use compound so we match the QLPreviewController.
|
||||||
.background() // Don't use compound so we match the QLPreviewController.
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.navigationTitle(title)
|
.navigationTitle(title)
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
.toolbar { toolbar }
|
.toolbar { toolbar }
|
||||||
.disabled(context.viewState.shouldDisableInteraction)
|
.disabled(context.viewState.shouldDisableInteraction)
|
||||||
.interactiveDismissDisabled()
|
.interactiveDismissDisabled()
|
||||||
|
.presentationBackground(.background) // Fix a bug introduced by the caption warning.
|
||||||
.preferredColorScheme(colorSchemeOverride)
|
.preferredColorScheme(colorSchemeOverride)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,13 +53,19 @@ struct MediaUploadPreviewScreen: View {
|
|||||||
|
|
||||||
private var composer: some View {
|
private var composer: some View {
|
||||||
HStack(spacing: 12) {
|
HStack(spacing: 12) {
|
||||||
MessageComposerTextField(placeholder: L10n.richTextEditorComposerCaptionPlaceholder,
|
HStack(spacing: 6) {
|
||||||
text: $context.caption,
|
MessageComposerTextField(placeholder: L10n.richTextEditorComposerCaptionPlaceholder,
|
||||||
presendCallback: $context.presendCallback,
|
text: $context.caption,
|
||||||
maxHeight: ComposerConstant.maxHeight,
|
presendCallback: $context.presendCallback,
|
||||||
keyHandler: { _ in },
|
maxHeight: ComposerConstant.maxHeight,
|
||||||
pasteHandler: { _ in })
|
keyHandler: { _ in },
|
||||||
.messageComposerStyle()
|
pasteHandler: { _ in })
|
||||||
|
|
||||||
|
if context.viewState.shouldShowCaptionWarning {
|
||||||
|
captionWarningButton
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.messageComposerStyle()
|
||||||
|
|
||||||
SendButton {
|
SendButton {
|
||||||
context.send(viewAction: .send)
|
context.send(viewAction: .send)
|
||||||
@@ -66,6 +73,47 @@ struct MediaUploadPreviewScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var captionWarningButton: some View {
|
||||||
|
Button {
|
||||||
|
context.isPresentingMediaCaptionWarning = true
|
||||||
|
} label: {
|
||||||
|
CompoundIcon(\.infoSolid, size: .xSmall, relativeTo: .compound.bodyLG)
|
||||||
|
}
|
||||||
|
.tint(.compound.iconCriticalPrimary)
|
||||||
|
.popover(isPresented: $context.isPresentingMediaCaptionWarning, arrowEdge: .bottom) {
|
||||||
|
captionWarningContent
|
||||||
|
.presentationDetents([.height(captionWarningFrame.height)])
|
||||||
|
.presentationDragIndicator(.visible)
|
||||||
|
.padding(.top, 19) // For the drag indicator
|
||||||
|
.presentationBackground(.compound.bgCanvasDefault)
|
||||||
|
.preferredColorScheme(colorSchemeOverride)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var captionWarningContent: some View {
|
||||||
|
VStack(spacing: 0) {
|
||||||
|
VStack(spacing: 16) {
|
||||||
|
BigIcon(icon: \.infoSolid, style: .alertSolid)
|
||||||
|
|
||||||
|
Text(L10n.screenMediaUploadPreviewCaptionWarning)
|
||||||
|
.font(.compound.bodyMD)
|
||||||
|
.foregroundStyle(.compound.textPrimary)
|
||||||
|
.multilineTextAlignment(.center)
|
||||||
|
.fixedSize(horizontal: false, vertical: true)
|
||||||
|
}
|
||||||
|
.padding(24)
|
||||||
|
.padding(.bottom, 8)
|
||||||
|
|
||||||
|
Button(L10n.actionOk) {
|
||||||
|
context.isPresentingMediaCaptionWarning = false
|
||||||
|
}
|
||||||
|
.buttonStyle(.compound(.secondary))
|
||||||
|
.padding(.horizontal, 16)
|
||||||
|
.padding(.bottom, 16)
|
||||||
|
}
|
||||||
|
.readFrame($captionWarningFrame)
|
||||||
|
}
|
||||||
|
|
||||||
@ToolbarContentBuilder
|
@ToolbarContentBuilder
|
||||||
private var toolbar: some ToolbarContent {
|
private var toolbar: some ToolbarContent {
|
||||||
ToolbarItem(placement: .cancellationAction) {
|
ToolbarItem(placement: .cancellationAction) {
|
||||||
@@ -76,16 +124,6 @@ struct MediaUploadPreviewScreen: View {
|
|||||||
// follow the dark colour scheme on devices running with dark mode disabled.
|
// follow the dark colour scheme on devices running with dark mode disabled.
|
||||||
.tint(.compound.textActionPrimary)
|
.tint(.compound.textActionPrimary)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !context.viewState.showMediaCaptionComposer {
|
|
||||||
ToolbarItem(placement: .confirmationAction) {
|
|
||||||
Button { context.send(viewAction: .send) } label: {
|
|
||||||
Text(L10n.actionSend)
|
|
||||||
}
|
|
||||||
// Same fix as above (this button is temporary anyway).
|
|
||||||
.tint(.compound.textActionPrimary)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,10 +207,14 @@ struct MediaUploadPreviewScreen_Previews: PreviewProvider, TestablePreview {
|
|||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
title: "App Icon.png",
|
title: "App Icon.png",
|
||||||
url: snapshotURL,
|
url: snapshotURL,
|
||||||
createMediaCaptionsEnabled: true)
|
shouldShowCaptionWarning: true)
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
NavigationStack {
|
NavigationStack {
|
||||||
MediaUploadPreviewScreen(context: viewModel.context)
|
MediaUploadPreviewScreen(context: viewModel.context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MediaUploadPreviewScreen(context: viewModel.context)
|
||||||
|
.captionWarningContent
|
||||||
|
.previewDisplayName("Caption warning")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:885fb08cfe301dd996fa6f1b276afa7a74f6534d692ab313ec7d64bfcb970981
|
oid sha256:79c2656c58dde4df7af57a9ce1561d33c83772a8f15e88ea57fe80f8c14b61b5
|
||||||
size 91980
|
size 92652
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:4443b88f50798db3ddf789a3a1daf69b45158e799801e51e0a6604840af9b811
|
||||||
|
size 84527
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:ab99e9ac3233f55705f96cc0c8f1480351f0765b7b36d1e90e3262758b2ede9a
|
oid sha256:f53cf59f991c8958e8ae06759f05cf41b39ec6d1b402fcc6123edbd54e863409
|
||||||
size 93223
|
size 93837
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:babfab486bcb62122404c32f1268e3f6ce486c7ac1942de40e3fd754260c2e9a
|
||||||
|
size 89371
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:3cd02932f70d0c1336b295d94ca31694f569fea56b36b8cc29c448284406e8dd
|
oid sha256:1212f7afafd62561d7a90852e077667a042d4e86bd9b572904e0c89146e8d8b9
|
||||||
size 50622
|
size 51188
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:74e2863ec160d47570f9d8c512d5dc4c6afa50ce0f2fb6cd7520a4bfb287f4dc
|
||||||
|
size 43826
|
||||||
+2
-2
@@ -1,3 +1,3 @@
|
|||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:26109f9334be8c7452fd98c15158bea787df6b87f560f8ee6b6aea46dc6a1647
|
oid sha256:f6ac82c8457a7771ae7c30049ea4250f434285e4ae15ad52eefc935bd38e37eb
|
||||||
size 51627
|
size 51942
|
||||||
|
|||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:d1ea4d6009691a8079ae6d788dda3d9ea3c28d45451ad31278dbe8dfc399e2f7
|
||||||
|
size 54743
|
||||||
@@ -126,7 +126,7 @@ class MediaUploadPreviewScreenViewModelTests: XCTestCase {
|
|||||||
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
mediaUploadingPreprocessor: MediaUploadingPreprocessor(appSettings: ServiceLocator.shared.settings),
|
||||||
title: "Some File",
|
title: "Some File",
|
||||||
url: url,
|
url: url,
|
||||||
createMediaCaptionsEnabled: true)
|
shouldShowCaptionWarning: true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func verifyCaption(_ caption: String?, expectedCaption: String?) -> Result<Void, TimelineProxyError> {
|
private func verifyCaption(_ caption: String?, expectedCaption: String?) -> Result<Void, TimelineProxyError> {
|
||||||
|
|||||||
Reference in New Issue
Block a user