Polls refinements (#1608)

* Refine focus behavior

* Begin draft mode

* Add alert on poll form

* Add poll ended asset

* Add fallback text for ended poll event

* Cleanup

* Fix assets

* Remove poll feature flags

* Fix UI tests

* Fix ui tests

* Refine discard poll alert

* Remove unused import

* Rename hasDraftContent -> hasContent

* Restore createPoll-2 ref screenshots
This commit is contained in:
Alfonso Grillo
2023-09-01 15:55:11 +02:00
committed by GitHub
parent 721ad446ed
commit 0381fe3ce5
36 changed files with 458 additions and 89 deletions

View File

@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "ended-poll.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}

View File

@@ -0,0 +1,137 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
0.105882 0.113725 0.133333 scn
15.000000 8.617193 m
15.000000 1.666666 l
15.000000 1.208333 14.836806 0.815971 14.510417 0.489583 c
14.184029 0.163194 13.791667 0.000000 13.333334 0.000000 c
1.666667 0.000000 l
1.208333 0.000000 0.815972 0.163194 0.489583 0.489583 c
0.163194 0.815971 0.000000 1.208333 0.000000 1.666666 c
0.000000 13.333333 l
0.000000 13.791667 0.163194 14.184028 0.489583 14.510417 c
0.815972 14.836805 1.208333 15.000000 1.666667 15.000000 c
8.618215 15.000000 l
8.433891 14.478710 8.333564 13.917734 8.333496 13.333333 c
1.666667 13.333333 l
1.666667 1.666666 l
13.333334 1.666666 l
13.333334 8.332741 l
13.333388 8.332741 13.333442 8.332741 13.333496 8.332741 c
13.917828 8.332741 14.478747 8.432978 15.000000 8.617193 c
h
4.760417 3.572916 m
4.600695 3.413194 4.402778 3.333333 4.166667 3.333333 c
3.930556 3.333333 3.732639 3.413194 3.572917 3.572916 c
3.413195 3.732638 3.333333 3.930555 3.333333 4.166666 c
3.333333 8.333333 l
3.333333 8.569445 3.413195 8.767361 3.572917 8.927083 c
3.732639 9.086805 3.930556 9.166666 4.166667 9.166666 c
4.402778 9.166666 4.600695 9.086805 4.760417 8.927083 c
4.920139 8.767361 5.000000 8.569445 5.000000 8.333333 c
5.000000 4.166666 l
5.000000 3.930555 4.920139 3.732638 4.760417 3.572916 c
h
8.093750 3.572916 m
7.934028 3.413194 7.736111 3.333333 7.500000 3.333333 c
7.263889 3.333333 7.065972 3.413194 6.906250 3.572916 c
6.746528 3.732638 6.666667 3.930555 6.666667 4.166666 c
6.666667 10.833333 l
6.666667 11.069445 6.746528 11.267361 6.906250 11.427083 c
7.065972 11.586805 7.263889 11.666666 7.500000 11.666666 c
7.736111 11.666666 7.934028 11.586805 8.093750 11.427083 c
8.253472 11.267361 8.333334 11.069445 8.333334 10.833333 c
8.333334 4.166666 l
8.333334 3.930555 8.253472 3.732638 8.093750 3.572916 c
h
11.427084 3.572916 m
11.267362 3.413194 11.069445 3.333333 10.833334 3.333333 c
10.597222 3.333333 10.399306 3.413194 10.239584 3.572916 c
10.079862 3.732638 10.000000 3.930555 10.000000 4.166666 c
10.000000 5.833333 l
10.000000 6.069444 10.079862 6.267361 10.239584 6.427083 c
10.399306 6.586805 10.597222 6.666666 10.833334 6.666666 c
11.069445 6.666666 11.267362 6.586805 11.427084 6.427083 c
11.586806 6.267361 11.666667 6.069444 11.666667 5.833333 c
11.666667 4.166666 l
11.666667 3.930555 11.586806 3.732638 11.427084 3.572916 c
h
f*
n
Q
q
1.000000 0.000000 -0.000000 1.000000 10.000000 10.667969 cm
0.105882 0.113725 0.133333 scn
6.422589 4.919985 m
6.748026 4.594548 6.748026 4.066910 6.422589 3.741474 c
3.089256 0.408141 l
2.763819 0.082704 2.236181 0.082704 1.910744 0.408141 c
0.244078 2.074807 l
-0.081359 2.400244 -0.081359 2.927881 0.244078 3.253318 c
0.569515 3.578755 1.097152 3.578755 1.422589 3.253318 c
2.500000 2.175907 l
5.244078 4.919985 l
5.569515 5.245422 6.097153 5.245422 6.422589 4.919985 c
h
f*
n
Q
endstream
endobj
3 0 obj
2894
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 16.666504 15.832031 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000002984 00000 n
0000003007 00000 n
0000003180 00000 n
0000003254 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
3313
%%EOF

View File

@@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "timeline-ended-poll.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@@ -0,0 +1,154 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 2.500000 13.167969 cm
0.105882 0.113725 0.133333 scn
13.089256 0.408141 m
16.422588 3.741474 l
16.748028 4.066910 16.748028 4.594548 16.422588 4.919985 c
16.097153 5.245422 15.569514 5.245422 15.244078 4.919985 c
12.500001 2.175907 l
11.422589 3.253318 l
11.097153 3.578755 10.569515 3.578755 10.244079 3.253318 c
9.918641 2.927881 9.918641 2.400244 10.244079 2.074807 c
11.910745 0.408141 l
12.236181 0.082704 12.763820 0.082704 13.089256 0.408141 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 2.500000 3.332031 cm
0.105882 0.113725 0.133333 scn
15.000001 0.833334 m
15.000001 7.784510 l
14.478703 7.600257 13.917727 7.500000 13.333334 7.500000 c
13.333334 0.833334 l
1.666667 0.833334 l
1.666667 12.500000 l
8.333334 12.500000 l
8.333334 13.084393 8.433591 13.645369 8.617844 14.166667 c
1.666667 14.166667 l
1.208333 14.166667 0.815972 14.003472 0.489583 13.677084 c
0.163194 13.350695 0.000000 12.958334 0.000000 12.500000 c
0.000000 0.833334 l
0.000000 0.375001 0.163194 -0.017362 0.489583 -0.343750 c
0.815972 -0.670138 1.208333 -0.833333 1.666667 -0.833333 c
13.333334 -0.833333 l
13.791667 -0.833333 14.184029 -0.670138 14.510418 -0.343750 c
14.836806 -0.017362 15.000001 0.375001 15.000001 0.833334 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 2.500000 12.498047 cm
0.105882 0.113725 0.133333 scn
4.166667 -6.666016 m
4.402778 -6.666016 4.600694 -6.586154 4.760417 -6.426432 c
4.920139 -6.266709 5.000000 -6.068792 5.000000 -5.832682 c
5.000000 -1.666016 l
5.000000 -1.429905 4.920139 -1.231987 4.760417 -1.072266 c
4.600694 -0.912543 4.402778 -0.832682 4.166667 -0.832682 c
3.930556 -0.832682 3.732639 -0.912543 3.572917 -1.072266 c
3.413195 -1.231987 3.333333 -1.429905 3.333333 -1.666016 c
3.333333 -5.832682 l
3.333333 -6.068792 3.413195 -6.266709 3.572917 -6.426432 c
3.732639 -6.586154 3.930556 -6.666016 4.166667 -6.666016 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 2.500000 9.998047 cm
0.105882 0.113725 0.133333 scn
7.500000 -4.166016 m
7.736111 -4.166016 7.934028 -4.086154 8.093750 -3.926432 c
8.253472 -3.766709 8.333334 -3.568792 8.333334 -3.332682 c
8.333334 3.333984 l
8.333334 3.570096 8.253472 3.768013 8.093750 3.927734 c
7.934028 4.087457 7.736111 4.167318 7.500000 4.167318 c
7.263889 4.167318 7.065973 4.087457 6.906250 3.927734 c
6.746528 3.768013 6.666667 3.570096 6.666667 3.333984 c
6.666667 -3.332682 l
6.666667 -3.568792 6.746528 -3.766709 6.906250 -3.926432 c
7.065973 -4.086154 7.263889 -4.166016 7.500000 -4.166016 c
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 2.500000 14.998047 cm
0.105882 0.113725 0.133333 scn
10.833334 -9.166016 m
11.069445 -9.166016 11.267362 -9.086154 11.427084 -8.926432 c
11.586805 -8.766709 11.666667 -8.568792 11.666667 -8.332682 c
11.666667 -6.666016 l
11.666667 -6.429904 11.586805 -6.231988 11.427084 -6.072266 c
11.267362 -5.912542 11.069445 -5.832682 10.833334 -5.832682 c
10.597222 -5.832682 10.399306 -5.912542 10.239584 -6.072266 c
10.079862 -6.231988 10.000000 -6.429904 10.000000 -6.666016 c
10.000000 -8.332682 l
10.000000 -8.568792 10.079862 -8.766709 10.239584 -8.926432 c
10.399306 -9.086154 10.597222 -9.166016 10.833334 -9.166016 c
h
f
n
Q
endstream
endobj
3 0 obj
3206
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 20.000000 20.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000003296 00000 n
0000003319 00000 n
0000003492 00000 n
0000003566 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
3625
%%EOF

View File

@@ -10,6 +10,7 @@
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@@ -10,6 +10,7 @@
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}

View File

@@ -121,6 +121,7 @@
"common_success" = "Success";
"common_suggestions" = "Suggestions";
"common_syncing" = "Syncing";
"common_text" = "Text";
"common_third_party_notices" = "Third-party notices";
"common_topic" = "Topic";
"common_topic_placeholder" = "What is this room about?";
@@ -182,10 +183,12 @@
"rageshake_dialog_content" = "You seem to be shaking the phone in frustration. Would you like to open the bug report screen?";
"report_content_explanation" = "This message will be reported to your homeservers administrator. They will not be able to read any encrypted messages.";
"report_content_hint" = "Reason for reporting this content";
"rich_text_editor_a11y_add_attachment" = "Add attachment";
"rich_text_editor_bullet_list" = "Toggle bullet list";
"rich_text_editor_close_formatting_options" = "Close formatting options";
"rich_text_editor_code_block" = "Toggle code block";
"rich_text_editor_composer_placeholder" = "Message…";
"rich_text_editor_create_link" = "Create a link";
"rich_text_editor_edit_link" = "Edit link";
"rich_text_editor_format_bold" = "Apply bold format";
"rich_text_editor_format_italic" = "Apply italic format";
"rich_text_editor_format_strikethrough" = "Apply strikethrough format";
@@ -195,8 +198,11 @@
"rich_text_editor_inline_code" = "Apply inline code format";
"rich_text_editor_link" = "Set link";
"rich_text_editor_numbered_list" = "Toggle numbered list";
"rich_text_editor_open_compose_options" = "Open compose options";
"rich_text_editor_quote" = "Toggle quote";
"rich_text_editor_unindent" = "Unindent";
"rich_text_editor_url_placeholder" = "Link";
"rich_text_editor_a11y_add_attachment" = "Add attachment";
"room_timeline_beginning_of_room" = "This is the beginning of %1$@.";
"room_timeline_beginning_of_room_no_name" = "This is the beginning of this conversation.";
"room_timeline_read_marker_title" = "New";
@@ -241,7 +247,8 @@
"screen_create_poll_anonymous_desc" = "Show results only after poll ends";
"screen_create_poll_anonymous_headline" = "Anonymous Poll";
"screen_create_poll_answer_hint" = "Option %1$d";
"screen_create_poll_confirmation" = "Are you sure you would like to go back?";
"screen_create_poll_discard_confirmation" = "Are you sure you want to discard this poll?";
"screen_create_poll_discard_confirmation_title" = "Discard Poll";
"screen_create_poll_question_desc" = "Question or topic";
"screen_create_poll_question_hint" = "What is the poll about?";
"screen_create_poll_title" = "Create Poll";

View File

@@ -39,9 +39,7 @@ final class AppSettings {
case hasShownWelcomeScreen
case notificationSettingsEnabled
case swiftUITimelineEnabled
case pollsInTimeline
case richTextEditorEnabled
case pollsCreationEnabled
}
private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
@@ -242,12 +240,6 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.swiftUITimelineEnabled, defaultValue: false, storageType: .volatile)
var swiftUITimelineEnabled
@UserPreference(key: UserDefaultsKeys.pollsInTimeline, defaultValue: false, storageType: .userDefaults(store))
var pollsInTimelineEnabled
@UserPreference(key: UserDefaultsKeys.richTextEditorEnabled, defaultValue: false, storageType: .userDefaults(store))
var richTextEditorEnabled
@UserPreference(key: UserDefaultsKeys.pollsCreationEnabled, defaultValue: false, storageType: .userDefaults(store))
var pollsCreationEnabled
}

View File

@@ -35,6 +35,7 @@ internal enum Asset {
internal static let encryptionNormal = ImageAsset(name: "images/encryption-normal")
internal static let encryptionTrusted = ImageAsset(name: "images/encryption-trusted")
internal static let encryptionWarning = ImageAsset(name: "images/encryption-warning")
internal static let endedPoll = ImageAsset(name: "images/ended-poll")
internal static let launchBackground = ImageAsset(name: "images/launch-background")
internal static let launchLogo = ImageAsset(name: "images/launch-logo")
internal static let locationMarker = ImageAsset(name: "images/location-marker")
@@ -42,6 +43,7 @@ internal enum Asset {
internal static let locationPointerFull = ImageAsset(name: "images/location-pointer-full")
internal static let locationPointer = ImageAsset(name: "images/location-pointer")
internal static let timelineComposerSendMessage = ImageAsset(name: "images/timeline-composer-send-message")
internal static let timelineEndedPoll = ImageAsset(name: "images/timeline-ended-poll")
internal static let timelinePollAttachment = ImageAsset(name: "images/timeline-poll-attachment")
internal static let timelinePoll = ImageAsset(name: "images/timeline-poll")
internal static let timelineReactionAddMore = ImageAsset(name: "images/timeline-reaction-add-more")

View File

@@ -274,6 +274,8 @@ public enum L10n {
public static var commonSuggestions: String { return L10n.tr("Localizable", "common_suggestions") }
/// Syncing
public static var commonSyncing: String { return L10n.tr("Localizable", "common_syncing") }
/// Text
public static var commonText: String { return L10n.tr("Localizable", "common_text") }
/// Third-party notices
public static var commonThirdPartyNotices: String { return L10n.tr("Localizable", "common_third_party_notices") }
/// Topic
@@ -462,10 +464,16 @@ public enum L10n {
public static var richTextEditorA11yAddAttachment: String { return L10n.tr("Localizable", "rich_text_editor_a11y_add_attachment") }
/// Toggle bullet list
public static var richTextEditorBulletList: String { return L10n.tr("Localizable", "rich_text_editor_bullet_list") }
/// Close formatting options
public static var richTextEditorCloseFormattingOptions: String { return L10n.tr("Localizable", "rich_text_editor_close_formatting_options") }
/// Toggle code block
public static var richTextEditorCodeBlock: String { return L10n.tr("Localizable", "rich_text_editor_code_block") }
/// Message
public static var richTextEditorComposerPlaceholder: String { return L10n.tr("Localizable", "rich_text_editor_composer_placeholder") }
/// Create a link
public static var richTextEditorCreateLink: String { return L10n.tr("Localizable", "rich_text_editor_create_link") }
/// Edit link
public static var richTextEditorEditLink: String { return L10n.tr("Localizable", "rich_text_editor_edit_link") }
/// Apply bold format
public static var richTextEditorFormatBold: String { return L10n.tr("Localizable", "rich_text_editor_format_bold") }
/// Apply italic format
@@ -484,10 +492,14 @@ public enum L10n {
public static var richTextEditorLink: String { return L10n.tr("Localizable", "rich_text_editor_link") }
/// Toggle numbered list
public static var richTextEditorNumberedList: String { return L10n.tr("Localizable", "rich_text_editor_numbered_list") }
/// Open compose options
public static var richTextEditorOpenComposeOptions: String { return L10n.tr("Localizable", "rich_text_editor_open_compose_options") }
/// Toggle quote
public static var richTextEditorQuote: String { return L10n.tr("Localizable", "rich_text_editor_quote") }
/// Unindent
public static var richTextEditorUnindent: String { return L10n.tr("Localizable", "rich_text_editor_unindent") }
/// Link
public static var richTextEditorUrlPlaceholder: String { return L10n.tr("Localizable", "rich_text_editor_url_placeholder") }
/// This is the beginning of %1$@.
public static func roomTimelineBeginningOfRoom(_ p1: Any) -> String {
return L10n.tr("Localizable", "room_timeline_beginning_of_room", String(describing: p1))
@@ -612,8 +624,10 @@ public enum L10n {
public static func screenCreatePollAnswerHint(_ p1: Int) -> String {
return L10n.tr("Localizable", "screen_create_poll_answer_hint", p1)
}
/// Are you sure you would like to go back?
public static var screenCreatePollConfirmation: String { return L10n.tr("Localizable", "screen_create_poll_confirmation") }
/// Are you sure you want to discard this poll?
public static var screenCreatePollDiscardConfirmation: String { return L10n.tr("Localizable", "screen_create_poll_discard_confirmation") }
/// Discard Poll
public static var screenCreatePollDiscardConfirmationTitle: String { return L10n.tr("Localizable", "screen_create_poll_discard_confirmation_title") }
/// Question or topic
public static var screenCreatePollQuestionDesc: String { return L10n.tr("Localizable", "screen_create_poll_question_desc") }
/// What is the poll about?

View File

@@ -1508,23 +1508,23 @@ class RoomProxyMock: RoomProxyProtocol {
}
//MARK: - endPoll
var endPollPollStartIDCallsCount = 0
var endPollPollStartIDCalled: Bool {
return endPollPollStartIDCallsCount > 0
var endPollPollStartIDTextCallsCount = 0
var endPollPollStartIDTextCalled: Bool {
return endPollPollStartIDTextCallsCount > 0
}
var endPollPollStartIDReceivedPollStartID: String?
var endPollPollStartIDReceivedInvocations: [String] = []
var endPollPollStartIDReturnValue: Result<Void, RoomProxyError>!
var endPollPollStartIDClosure: ((String) async -> Result<Void, RoomProxyError>)?
var endPollPollStartIDTextReceivedArguments: (pollStartID: String, text: String)?
var endPollPollStartIDTextReceivedInvocations: [(pollStartID: String, text: String)] = []
var endPollPollStartIDTextReturnValue: Result<Void, RoomProxyError>!
var endPollPollStartIDTextClosure: ((String, String) async -> Result<Void, RoomProxyError>)?
func endPoll(pollStartID: String) async -> Result<Void, RoomProxyError> {
endPollPollStartIDCallsCount += 1
endPollPollStartIDReceivedPollStartID = pollStartID
endPollPollStartIDReceivedInvocations.append(pollStartID)
if let endPollPollStartIDClosure = endPollPollStartIDClosure {
return await endPollPollStartIDClosure(pollStartID)
func endPoll(pollStartID: String, text: String) async -> Result<Void, RoomProxyError> {
endPollPollStartIDTextCallsCount += 1
endPollPollStartIDTextReceivedArguments = (pollStartID: pollStartID, text: text)
endPollPollStartIDTextReceivedInvocations.append((pollStartID: pollStartID, text: text))
if let endPollPollStartIDTextClosure = endPollPollStartIDTextClosure {
return await endPollPollStartIDTextClosure(pollStartID, text)
} else {
return endPollPollStartIDReturnValue
return endPollPollStartIDTextReturnValue
}
}
}

View File

@@ -66,13 +66,11 @@ struct RoomAttachmentPicker: View {
}
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerLocation)
if ServiceLocator.shared.settings.pollsCreationEnabled {
Button {
context.showAttachmentPopover = false
context.send(viewAction: .displayPollForm)
} label: {
PickerLabel(title: L10n.screenRoomAttachmentSourcePoll, icon: Image(asset: Asset.Images.timelinePollAttachment))
}
Button {
context.showAttachmentPopover = false
context.send(viewAction: .displayPollForm)
} label: {
PickerLabel(title: L10n.screenRoomAttachmentSourcePoll, icon: Image(asset: Asset.Images.timelinePollAttachment))
}
}
.padding(.top, isPresented ? 20 : 0)

View File

@@ -39,6 +39,12 @@ struct CreatePollScreenViewStateBindings {
var isCreateButtonDisabled: Bool {
question.isEmpty || options.count < 2 || options.contains { $0.text.isEmpty }
}
var hasContent: Bool {
!question.isEmpty || options.contains(where: { !$0.text.isEmpty }) || isUndisclosed
}
var alertInfo: AlertInfo<UUID>?
}
enum CreatePollScreenViewAction {

View File

@@ -39,7 +39,15 @@ class CreatePollScreenViewModel: CreatePollScreenViewModelType, CreatePollScreen
options: state.bindings.options.map(\.text),
pollKind: state.bindings.isUndisclosed ? .undisclosed : .disclosed))
case .cancel:
actionsSubject.send(.cancel)
if state.bindings.hasContent {
state.bindings.alertInfo = .init(id: .init(),
title: L10n.screenCreatePollDiscardConfirmationTitle,
message: L10n.screenCreatePollDiscardConfirmation,
primaryButton: .init(title: L10n.actionCancel, action: nil),
secondaryButton: .init(title: L10n.actionOk, action: { self.actionsSubject.send(.cancel) }))
} else {
actionsSubject.send(.cancel)
}
case .deleteOption(let index):
// fixes a crash that caused an index out of range when an option with the keyboard focus was deleted
Task {

View File

@@ -39,6 +39,8 @@ struct CreatePollScreen: View {
.navigationBarTitleDisplayMode(.inline)
.toolbar { toolbar }
.animation(.elementDefault, value: context.options)
.interactiveDismissDisabled(context.viewState.bindings.hasContent)
.alert(item: $context.alertInfo)
}
// MARK: - Private
@@ -55,6 +57,10 @@ struct CreatePollScreen: View {
.textFieldStyle(.compoundForm)
.focused($focus, equals: .question)
.accessibilityIdentifier(A11yIdentifiers.createPollScreen.question)
.onSubmit {
focus = context.options.indices.first.map { .option(index: $0) }
}
.submitLabel(.next)
}
.compoundFormSection()
}
@@ -74,6 +80,11 @@ struct CreatePollScreen: View {
}
.focused($focus, equals: .option(index: index))
.accessibilityIdentifier(A11yIdentifiers.createPollScreen.optionID(index))
.onSubmit {
let nextOptionIndex = index == context.options.endIndex - 1 ? nil : index + 1
focus = nextOptionIndex.map { .option(index: $0) }
}
.submitLabel(index == context.options.endIndex - 1 ? .done : .next)
}
}
.onMove { offsets, toOffset in
@@ -83,6 +94,7 @@ struct CreatePollScreen: View {
if context.options.count < context.viewState.maxNumberOfOptions {
Button(L10n.screenCreatePollAddOptionBtn) {
context.send(viewAction: .addOption)
focus = context.options.indices.last.map { .option(index: $0) }
}
.accessibilityIdentifier(A11yIdentifiers.createPollScreen.addOption)
}

View File

@@ -814,7 +814,8 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
private func endPoll(pollStartID: String) {
Task {
let endPollResult = await roomProxy.endPoll(pollStartID: pollStartID)
let endPollResult = await roomProxy.endPoll(pollStartID: pollStartID,
text: "The poll with event id: \(pollStartID) has ended")
switch endPollResult {
case .success:
break

View File

@@ -21,6 +21,7 @@ struct PollRoomTimelineView: View {
@Environment(\.timelineStyle) var timelineStyle
@EnvironmentObject private var context: RoomScreenViewModel.Context
@ScaledMetric private var summaryPadding = 32
@ScaledMetric private var iconSize = 22
var body: some View {
TimelineStyler(timelineItem: timelineItem) {
@@ -58,7 +59,11 @@ struct PollRoomTimelineView: View {
private var questionView: some View {
HStack(alignment: .top, spacing: 12) {
Image(Asset.Images.timelinePoll.name)
let asset = poll.hasEnded ? Asset.Images.timelineEndedPoll : Asset.Images.timelinePoll
Image(asset.name)
.resizable()
.frame(width: iconSize, height: iconSize)
Text(poll.question)
.multilineTextAlignment(.leading)

View File

@@ -97,19 +97,31 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
}
/// The action's label.
@ViewBuilder
var label: some View {
switch self {
case .copy: return Label(L10n.actionCopy, systemImage: "doc.on.doc")
case .edit: return Label(L10n.actionEdit, systemImage: "pencil.line")
case .copyPermalink: return Label(L10n.actionCopyLinkToMessage, systemImage: "link")
case .reply: return Label(L10n.actionReply, systemImage: "arrowshape.turn.up.left")
case .forward: return Label(L10n.actionForward, systemImage: "arrowshape.turn.up.right")
case .redact: return Label(L10n.actionRemove, systemImage: "trash")
case .viewSource: return Label(L10n.actionViewSource, systemImage: "doc.text.below.ecg")
case .retryDecryption: return Label(L10n.actionRetryDecryption, systemImage: "arrow.down.message")
case .report: return Label(L10n.actionReportContent, systemImage: "exclamationmark.bubble")
case .react: return Label(L10n.actionReact, systemImage: "hand.thumbsup")
case .endPoll: return Label { Text(L10n.actionEndPoll) } icon: { Image.compound.check.resizable() }
case .copy:
Label(L10n.actionCopy, systemImage: "doc.on.doc")
case .edit:
Label(L10n.actionEdit, systemImage: "pencil.line")
case .copyPermalink:
Label(L10n.actionCopyLinkToMessage, systemImage: "link")
case .reply:
Label(L10n.actionReply, systemImage: "arrowshape.turn.up.left")
case .forward:
Label(L10n.actionForward, systemImage: "arrowshape.turn.up.right")
case .redact:
Label(L10n.actionRemove, systemImage: "trash")
case .viewSource:
Label(L10n.actionViewSource, systemImage: "doc.text.below.ecg")
case .retryDecryption:
Label(L10n.actionRetryDecryption, systemImage: "arrow.down.message")
case .report:
Label(L10n.actionReportContent, systemImage: "exclamationmark.bubble")
case .react:
Label(L10n.actionReact, systemImage: "hand.thumbsup")
case .endPoll:
Label { Text(L10n.actionEndPoll) } icon: { CompoundIcon(customImage: Asset.Images.endedPoll.swiftUIImage) }
}
}
}

View File

@@ -50,9 +50,7 @@ protocol DeveloperOptionsProtocol: AnyObject {
var readReceiptsEnabled: Bool { get set }
var notificationSettingsEnabled: Bool { get set }
var swiftUITimelineEnabled: Bool { get set }
var pollsInTimelineEnabled: Bool { get set }
var richTextEditorEnabled: Bool { get set }
var pollsCreationEnabled: Bool { get set }
}
extension AppSettings: DeveloperOptionsProtocol { }

View File

@@ -60,16 +60,6 @@ struct DeveloperOptionsScreen: View {
}
}
Section("Polls") {
Toggle(isOn: $context.pollsInTimelineEnabled) {
Text("View polls in timeline")
}
Toggle(isOn: $context.pollsCreationEnabled) {
Text("View polls creation flow")
}
}
Section("Rich Text Editor") {
Toggle(isOn: $context.richTextEditorEnabled) {
Text("Use the Rich Text Editor")

View File

@@ -712,10 +712,10 @@ class RoomProxy: RoomProxyProtocol {
}
}
func endPoll(pollStartID: String) async -> Result<Void, RoomProxyError> {
func endPoll(pollStartID: String, text: String) async -> Result<Void, RoomProxyError> {
await Task.dispatch(on: .global()) {
do {
return try .success(self.room.endPoll(pollStartId: pollStartID, text: .init(), txnId: genTransactionId()))
return try .success(self.room.endPoll(pollStartId: pollStartID, text: text, txnId: genTransactionId()))
} catch {
MXLog.error("Failed ending a poll: \(error), pollStartID: \(pollStartID)")
return .failure(.failedEndingPoll)

View File

@@ -173,7 +173,7 @@ protocol RoomProxyProtocol {
func sendPollResponse(pollStartID: String, answers: [String]) async -> Result<Void, RoomProxyError>
func endPoll(pollStartID: String) async -> Result<Void, RoomProxyError>
func endPoll(pollStartID: String, text: String) async -> Result<Void, RoomProxyError>
}
extension RoomProxyProtocol {

View File

@@ -69,9 +69,6 @@ struct RoomTimelineItemFactory: RoomTimelineItemFactoryProtocol {
previousAvatarURLString: prevAvatarUrl,
isOutgoing: isOutgoing)
case .poll(question: let question, kind: let kind, maxSelections: let maxSelections, answers: let answers, votes: let votes, endTime: let endTime):
guard ServiceLocator.shared.settings.pollsInTimelineEnabled else {
return nil
}
return buildPollTimelineItem(question, kind, maxSelections, answers, votes, endTime, eventItemProxy, isOutgoing)
}
}

View File

@@ -56,6 +56,9 @@ class CreatePollScreenUITests: XCTestCase {
addOption.tap()
}
if app.keyboards.count > 0 {
app.typeText("\n")
}
app.swipeUp()
XCTAssertFalse(addOption.exists)

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:71686cab957bbfe4a68318b43a85cd33ab09927dc947e4636767164b4c64ac07
size 165464
oid sha256:6c85ea14e8c7399560a0e1cb808983a60e70431b8eee7c060515df8216b36790
size 165644

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:2a7591243a894a142ae01f85799f5c79d3c40376df766f4770d9945e7a443eba
size 162656
oid sha256:49703bc56ceb2c05edc04a3a963d22b109e64d644d6eff8f0d0c3bb9694d7177
size 162836

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4c77d14599ea65b2580f5a0600645c5227249e5e203690670f943819afccf30f
size 400253
oid sha256:68fd796a714e20762932c02f37d09750f57235af93564bf7ce01c6accdb273e1
size 401992

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6d6f1ba20ba8b4f4105c99e22f3e2a05ded1779f2e89e1368a6976d4038f801e
size 256560
oid sha256:f28aa90c1921e4a6d47f12ea66a608f1f36e893b46951c9a7b17b7d242598d13
size 256763

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c171ab518ab414b0e5f1bfbc5c1dea0bc4b5758946313d1f35b468a0a4e842d9
size 253524
oid sha256:7ce9878396fbdaae20932741b89c227f1534a5f7cb658f582839579b1802ff8b
size 253727

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d5e8e9532d78545b9e44ba0107afc4b30f976244af9ab22a6c6ed085ba12f01a
size 273943
oid sha256:0d442d84a69478695e2a876dddc0a4c8177cf6d337669b364dd92997b0c5139a
size 258730

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c0301afba8134b75d8ab95deb88e14430761e7b9342632985c6582edf9428c5f
size 191289
oid sha256:854c4b00493f89988906fac51aa3bdc2a019779f5db335c0b0c8d5ffe596b980
size 191462

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:125ea64ac800100ce165b4a5a496919ba67b27a459bda9ccaad9b0dcc32081e9
size 180247
oid sha256:959f3d9c3f866f5d9175a6a3a078d248a29d0003e5cbb23bf9a9c7750e9de6e7
size 180430

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fe8c3b06399abc9fdd36399ff65ce2b9b08aee40e9d4919940973d05f27253a0
size 392559
oid sha256:618685911d9e6917b5c13ff2b55ea9f0d3b204ce82ea39333fab000830dd4c94
size 390970

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:0b046f21855d3e8a26dc9778fd5f790312517bad17555e1c969dacdbca0fd932
size 275682
oid sha256:dc48fc413d3647e3fe5ab461cf97e869767995a99be8b6d8fe3b8e1d72352449
size 275889

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:81f8ed509be95b8c23f86d41f5a6999a53411a4b9e0e66a518371a02e7b082dc
size 268701
oid sha256:4673fc84f4630c58afd953338b0c5e9dfaf7dc17294bb4727148c6a30eac95c9
size 268908

View File

@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:416e46f361ee1460ff0523f87009fce1bd57d32e04ef47d2d6d7974857ab25c7
size 281058
oid sha256:ff0d3ac87ae409840771c3daf29740a6d79b1ecd4a9a00faf754dd2ba3415cd7
size 258399