diff --git a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7b45c9e98..8a5741eda 100644 --- a/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Riot.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -18,6 +18,15 @@ "version" : "5.12.2" } }, + { + "identity" : "matrix-wysiwyg-composer-swift", + "kind" : "remoteSourceControl", + "location" : "https://github.com/matrix-org/matrix-wysiwyg-composer-swift", + "state" : { + "branch" : "main", + "revision" : "532183124d973b8432694f29bce3619d184fe1a7" + } + }, { "identity" : "ogg-swift", "kind" : "remoteSourceControl", diff --git a/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold.png b/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold.png new file mode 100644 index 000000000..186ab7a26 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold@2x.png b/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold@2x.png new file mode 100644 index 000000000..0f57f9bd0 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold@3x.png b/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold@3x.png new file mode 100644 index 000000000..040357f99 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Bold@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Contents.json new file mode 100644 index 000000000..224203f93 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Bold.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Bold.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Bold@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Bold@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code.png b/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code.png new file mode 100644 index 000000000..65d16fb1a Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code@2x.png b/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code@2x.png new file mode 100644 index 000000000..a6b6b2b42 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code@3x.png b/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code@3x.png new file mode 100644 index 000000000..830f50d7e Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Code.imageset/Code@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Code.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Code.imageset/Contents.json new file mode 100644 index 000000000..12e2e8f2c --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Code.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Code.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Code@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Code@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Contents.json b/Riot/Assets/Images.xcassets/Composer/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Contents.json new file mode 100644 index 000000000..4643bee9f --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Indent increase.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Indent increase@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Indent increase@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase.png b/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase.png new file mode 100644 index 000000000..7cfba0707 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase@2x.png b/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase@2x.png new file mode 100644 index 000000000..7b1a16642 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase@3x.png b/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase@3x.png new file mode 100644 index 000000000..46818f71c Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Indent_increase.imageset/Indent increase@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Contents.json new file mode 100644 index 000000000..b261ad13d --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Italic.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Italic@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Italic@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic.png b/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic.png new file mode 100644 index 000000000..9954f5b10 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic@2x.png b/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic@2x.png new file mode 100644 index 000000000..fa1be861c Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic@3x.png b/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic@3x.png new file mode 100644 index 000000000..ac46fccca Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Italic.imageset/Italic@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Link.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Link.imageset/Contents.json new file mode 100644 index 000000000..712a0658e --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Link.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Link.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Link@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Link@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link.png b/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link.png new file mode 100644 index 000000000..b2ca41015 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link@2x.png b/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link@2x.png new file mode 100644 index 000000000..66e4e869f Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link@3x.png b/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link@3x.png new file mode 100644 index 000000000..bb4a6fa05 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Link.imageset/Link@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Contents.json new file mode 100644 index 000000000..03c78408f --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Numbered list.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Numbered list@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Numbered list@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list.png b/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list.png new file mode 100644 index 000000000..b2798d0d5 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list@2x.png b/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list@2x.png new file mode 100644 index 000000000..8eee74056 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list@3x.png b/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list@3x.png new file mode 100644 index 000000000..be93c4bad Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Numbered list.imageset/Numbered list@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Contents.json new file mode 100644 index 000000000..2c07b7fbc --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Quote.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Quote@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Quote@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote.png b/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote.png new file mode 100644 index 000000000..5a03db995 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote@2x.png b/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote@2x.png new file mode 100644 index 000000000..7461b0b17 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote@3x.png b/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote@3x.png new file mode 100644 index 000000000..cef7775c9 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Quote.imageset/Quote@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Contents.json new file mode 100644 index 000000000..f6b269190 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Strikethrough.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Strikethrough@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Strikethrough@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough.png b/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough.png new file mode 100644 index 000000000..a06fdf80d Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough@2x.png b/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough@2x.png new file mode 100644 index 000000000..f01b9b897 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough@3x.png b/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough@3x.png new file mode 100644 index 000000000..22ccc6db5 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Strikethrough.imageset/Strikethrough@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Contents.json new file mode 100644 index 000000000..c394abde4 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Underlined.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Underlined@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Underlined@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined.png b/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined.png new file mode 100644 index 000000000..ec6995c2c Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined@2x.png b/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined@2x.png new file mode 100644 index 000000000..b5ad0ec57 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined@3x.png b/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined@3x.png new file mode 100644 index 000000000..b467bdc17 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/Underlined.imageset/Underlined@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list.png b/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list.png new file mode 100644 index 000000000..8b235c331 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list@2x.png b/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list@2x.png new file mode 100644 index 000000000..8e6e0f9e9 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list@3x.png b/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list@3x.png new file mode 100644 index 000000000..356357067 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Bullet list@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Contents.json new file mode 100644 index 000000000..7fe1ca95e --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/bullet_list.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Bullet list.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Bullet list@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Bullet list@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Contents.json new file mode 100644 index 000000000..73db7a0bf --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Indent decrease.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Indent decrease@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Indent decrease@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease.png b/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease.png new file mode 100644 index 000000000..bba698715 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease@2x.png b/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease@2x.png new file mode 100644 index 000000000..12d52fb69 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease@3x.png b/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease@3x.png new file mode 100644 index 000000000..b434d77c3 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/indent_decrease.imageset/Indent decrease@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/Contents.json new file mode 100644 index 000000000..7229787b4 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "maximise_composer.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "maximise_composer@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "maximise_composer@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer.png b/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer.png new file mode 100644 index 000000000..7cf08e08e Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer@2x.png b/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer@2x.png new file mode 100644 index 000000000..02bfab959 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer@3x.png b/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer@3x.png new file mode 100644 index 000000000..5d90ebcd7 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/maximise_composer.imageset/maximise_composer@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/Contents.json new file mode 100644 index 000000000..e67839028 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "minimise_composer.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "minimise_composer@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "minimise_composer@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer.png b/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer.png new file mode 100644 index 000000000..13c74aa38 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer@2x.png b/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer@2x.png new file mode 100644 index 000000000..e2d4ab522 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer@3x.png b/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer@3x.png new file mode 100644 index 000000000..072c505ad Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/minimise_composer.imageset/minimise_composer@3x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/Contents.json b/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/Contents.json new file mode 100644 index 000000000..397c5d0dc --- /dev/null +++ b/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "start_compose_module.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "start_compose_module@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "start_compose_module@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module.png b/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module.png new file mode 100644 index 000000000..c30133af2 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module@2x.png b/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module@2x.png new file mode 100644 index 000000000..30b5b0253 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module@2x.png differ diff --git a/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module@3x.png b/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module@3x.png new file mode 100644 index 000000000..fc5065a60 Binary files /dev/null and b/Riot/Assets/Images.xcassets/Composer/start_compose_module.imageset/start_compose_module@3x.png differ diff --git a/Riot/Assets/en.lproj/Vector.strings b/Riot/Assets/en.lproj/Vector.strings index a243a1afc..fdd3e47a4 100644 --- a/Riot/Assets/en.lproj/Vector.strings +++ b/Riot/Assets/en.lproj/Vector.strings @@ -797,6 +797,7 @@ Tap the + to start adding people."; "settings_labs_enable_new_session_manager" = "New session manager"; "settings_labs_enable_new_client_info_feature" = "Record the client name, version, and url to recognise sessions more easily in session manager"; "settings_labs_enable_new_app_layout" = "New Application Layout"; +"settings_labs_enable_wysiwyg_composer" = "Try out the rich text editor (plain text mode coming soon)"; "settings_version" = "Version %@"; "settings_olm_version" = "Olm Version %@"; @@ -2489,6 +2490,19 @@ To enable access, tap Settings> Location and select Always"; "user_session_overview_current_session_title" = "Current session"; "user_session_overview_session_title" = "Session"; "user_session_overview_session_details_button_title" = "Session details"; + + +// Mark: - WYSIWYG Composer + +//Send Media Actions +"wysiwyg_composer_start_action_media_picker" = "Photo Library"; +"wysiwyg_composer_start_action_stickers" = "Stickers"; +"wysiwyg_composer_start_action_attachments" = "Attachments"; +"wysiwyg_composer_start_action_polls" = "Polls"; +"wysiwyg_composer_start_action_location" = "Location"; +"wysiwyg_composer_start_action_camera" = "Camera"; +"wysiwyg_composer_start_action_text_formatting" = "Text Formatting"; + // MARK: - MatrixKit diff --git a/Riot/Generated/Images.swift b/Riot/Generated/Images.swift index fbfbbe9a8..59fd3bb34 100644 --- a/Riot/Generated/Images.swift +++ b/Riot/Generated/Images.swift @@ -100,6 +100,20 @@ internal class Asset: NSObject { internal static let touchidIcon = ImageAsset(name: "touchid_icon") internal static let addGroupParticipant = ImageAsset(name: "add_group_participant") internal static let removeIconBlue = ImageAsset(name: "remove_icon_blue") + internal static let indentIncrease = ImageAsset(name: "Indent_increase") + internal static let bold = ImageAsset(name: "bold") + internal static let bulletList = ImageAsset(name: "bullet_list") + internal static let code = ImageAsset(name: "code") + internal static let indentDecrease = ImageAsset(name: "indent_decrease") + internal static let italic = ImageAsset(name: "italic") + internal static let link = ImageAsset(name: "link") + internal static let maximiseComposer = ImageAsset(name: "maximise_composer") + internal static let minimiseComposer = ImageAsset(name: "minimise_composer") + internal static let numberedList = ImageAsset(name: "numbered list") + internal static let quote = ImageAsset(name: "quote") + internal static let startComposeModule = ImageAsset(name: "start_compose_module") + internal static let strikethrough = ImageAsset(name: "strikethrough") + internal static let underlined = ImageAsset(name: "underlined") internal static let findYourContactsFacepile = ImageAsset(name: "find_your_contacts_facepile") internal static let captureAvatar = ImageAsset(name: "capture_avatar") internal static let deleteAvatar = ImageAsset(name: "delete_avatar") diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index 2f9bc5376..5a60209ff 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -7535,6 +7535,10 @@ public class VectorL10n: NSObject { public static var settingsLabsEnableThreads: String { return VectorL10n.tr("Vector", "settings_labs_enable_threads") } + /// Try out the rich text editor (plain text mode coming soon) + public static var settingsLabsEnableWysiwygComposer: String { + return VectorL10n.tr("Vector", "settings_labs_enable_wysiwyg_composer") + } /// Polls public static var settingsLabsEnabledPolls: String { return VectorL10n.tr("Vector", "settings_labs_enabled_polls") @@ -9155,6 +9159,34 @@ public class VectorL10n: NSObject { public static var widgetStickerPickerNoStickerpacksAlertAddNow: String { return VectorL10n.tr("Vector", "widget_sticker_picker_no_stickerpacks_alert_add_now") } + /// Attachments + public static var wysiwygComposerStartActionAttachments: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_attachments") + } + /// Camera + public static var wysiwygComposerStartActionCamera: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_camera") + } + /// Location + public static var wysiwygComposerStartActionLocation: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_location") + } + /// Photo Library + public static var wysiwygComposerStartActionMediaPicker: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_media_picker") + } + /// Polls + public static var wysiwygComposerStartActionPolls: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_polls") + } + /// Stickers + public static var wysiwygComposerStartActionStickers: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_stickers") + } + /// Text Formatting + public static var wysiwygComposerStartActionTextFormatting: String { + return VectorL10n.tr("Vector", "wysiwyg_composer_start_action_text_formatting") + } /// Yes public static var yes: String { return VectorL10n.tr("Vector", "yes") diff --git a/Riot/Managers/Settings/RiotSettings.swift b/Riot/Managers/Settings/RiotSettings.swift index ef5b80f4f..9f3d36a2a 100644 --- a/Riot/Managers/Settings/RiotSettings.swift +++ b/Riot/Managers/Settings/RiotSettings.swift @@ -172,6 +172,10 @@ final class RiotSettings: NSObject { @UserDefault(key: "enableClientInformationFeature", defaultValue: false, storage: defaults) var enableClientInformationFeature + /// Flag indicating if the wysiwyg composer feature is enabled + @UserDefault(key: "enableWysiwygComposer", defaultValue: false, storage: defaults) + var enableWysiwygComposer + // MARK: Calls /// Indicate if `allowStunServerFallback` settings has been set once. diff --git a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift index 493c29560..141c676e2 100644 --- a/Riot/Modules/Common/SwiftUI/VectorHostingController.swift +++ b/Riot/Modules/Common/SwiftUI/VectorHostingController.swift @@ -16,6 +16,7 @@ import Foundation import SwiftUI +import Combine /** UIHostingController that applies some app-level specific configuration @@ -25,7 +26,9 @@ class VectorHostingController: UIHostingController { // MARK: Private + private let forceZeroSafeAreaInsets: Bool private var theme: Theme + private var heightSubject = CurrentValueSubject(0) // MARK: Public @@ -40,8 +43,12 @@ class VectorHostingController: UIHostingController { var enableNavigationBarScrollEdgeAppearance = false /// When non-nil, the style will be applied to the status bar. var statusBarStyle: UIStatusBarStyle? - - private let forceZeroSafeAreaInsets: Bool + /// Whether or not to publish when the height of the view changes. + var publishHeightChanges: Bool = false + /// The publisher to subscribe to if `publishHeightChanges` is enabled. + var heightPublisher: AnyPublisher { + return heightSubject.eraseToAnyPublisher() + } override var preferredStatusBarStyle: UIStatusBarStyle { statusBarStyle ?? super.preferredStatusBarStyle @@ -104,6 +111,10 @@ class VectorHostingController: UIHostingController { if #available(iOS 15.0, *) { self.view.invalidateIntrinsicContentSize() } + if publishHeightChanges { + let height = sizeThatFits(in: CGSize(width: self.view.frame.width, height: UIView.layoutFittingExpandedSize.height)).height + heightSubject.send(height) + } } override func viewSafeAreaInsetsDidChange() { diff --git a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.h b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.h index 503441a52..32d5d834b 100644 --- a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.h +++ b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.h @@ -92,6 +92,22 @@ typedef enum : NSUInteger */ - (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView sendTextMessage:(NSString*)textMessage; +/** + Tells the delegate that the user wants to send a formatted text message. + + @param toolbarView the room input toolbar view. + @param formattedTextMessage the formatted message to send. + @param rawText the raw message to send. + */ +- (void)roomInputToolbarView:(MXKRoomInputToolbarView *)toolbarView sendFormattedTextMessage:(NSString *)formattedTextMessage withRawText:(NSString *)rawText; + +/** + Tells the delegate that the user wants to display the send media actions. + + @param toolbarView the room input toolbar view. + */ +- (void)roomInputToolbarViewShowSendMediaActions:(MXKRoomInputToolbarView *)toolbarView; + /** Tells the delegate that the user wants to send an image. @@ -222,7 +238,7 @@ typedef enum : NSUInteger @discussion This is the designated initializer for programmatic instantiation. @return An initialized `MXKRoomInputToolbarView-inherited` object if successful, `nil` otherwise. */ -+ (instancetype)roomInputToolbarView; ++ (MXKRoomInputToolbarView *)instantiateRoomInputToolbarView; /** The delegate notified when inputs are ready. diff --git a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.m b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.m index 2b9b06691..9581df2a7 100644 --- a/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.m +++ b/Riot/Modules/MatrixKit/Views/RoomInputToolbar/MXKRoomInputToolbarView.m @@ -69,7 +69,7 @@ bundle:[NSBundle bundleForClass:[MXKRoomInputToolbarView class]]]; } -+ (instancetype)roomInputToolbarView ++ (MXKRoomInputToolbarView *)instantiateRoomInputToolbarView { if ([[self class] nib]) { diff --git a/Riot/Modules/Room/DataSources/RoomDataSource.swift b/Riot/Modules/Room/DataSources/RoomDataSource.swift index c2c3227d3..281a7a046 100644 --- a/Riot/Modules/Room/DataSources/RoomDataSource.swift +++ b/Riot/Modules/Room/DataSources/RoomDataSource.swift @@ -21,7 +21,7 @@ extension RoomDataSource { private enum Constants { static let emoteMessageSlashCommandPrefix = String(format: "%@ ", kMXKSlashCmdEmote) } - + // MARK: - NSAttributedString Sending /// Send a text message to the room. /// While sending, a fake event will be echoed in the messages list. @@ -33,7 +33,7 @@ extension RoomDataSource { func sendAttributedTextMessage(_ attributedText: NSAttributedString, completion: @escaping (MXResponse) -> Void) { var localEcho: MXEvent? - + let isEmote = isAttributedTextMessageAnEmote(attributedText) let sanitized = sanitizedAttributedMessageText(attributedText) let rawText: String @@ -43,7 +43,7 @@ extension RoomDataSource { } else { rawText = sanitized.string } - + if isEmote { room.sendEmote(rawText, formattedText: html, @@ -57,13 +57,38 @@ extension RoomDataSource { localEcho: &localEcho, completion: completion) } - + if localEcho != nil { self.queueEvent(forProcessing: localEcho, with: self.roomState, direction: .forwards) self.processQueuedEvents(nil) } } - + + // MARK: - NSAttributedString Sending + /// Send a text message to the room. + /// While sending, a fake event will be echoed in the messages list. + /// Once complete, this local echo will be replaced by the event saved by the homeserver. + /// + /// - Parameters: + /// - rawText: the raw text to send + /// - html: the formatted html to send + /// - completion: http operation completion block + func sendFormattedTextMessage(_ rawText: String, + html: String, + completion: @escaping (MXResponse) -> Void) { + var localEcho: MXEvent? + room.sendTextMessage(rawText, + formattedText: html, + threadId: self.threadId, + localEcho: &localEcho, + completion: completion) + + if localEcho != nil { + self.queueEvent(forProcessing: localEcho, with: self.roomState, direction: .forwards) + self.processQueuedEvents(nil) + } + } + /// Send a reply to an event with text message to the room. /// /// While sending, a fake event will be echoed in the messages list. @@ -76,8 +101,6 @@ extension RoomDataSource { func sendReply(to eventToReply: MXEvent, withAttributedTextMessage attributedText: NSAttributedString, completion: @escaping (MXResponse) -> Void) { - var localEcho: MXEvent? - let sanitized = sanitizedAttributedMessageText(attributedText) let rawText: String let html: String? = htmlMessageFromSanitizedAttributedText(sanitized) @@ -86,23 +109,29 @@ extension RoomDataSource { } else { rawText = sanitized.string } - - let stringLocalizer: MXSendReplyEventStringLocalizerProtocol = MXKSendReplyEventStringLocalizer() - - room.sendReply(to: eventToReply, - textMessage: rawText, - formattedTextMessage: html, - stringLocalizer: stringLocalizer, - threadId: self.threadId, - localEcho: &localEcho, - completion: completion) - - if localEcho != nil { - self.queueEvent(forProcessing: localEcho, with: self.roomState, direction: .forwards) - self.processQueuedEvents(nil) - } + + handleFormattedSendReply(to: eventToReply, rawText: rawText, html: html, completion: completion) } - + + /// Send a reply to an event with a html formatted text message to the room. + /// + /// While sending, a fake event will be echoed in the messages list. + /// Once complete, this local echo will be replaced by the event saved by the homeserver. + /// + /// - Parameters: + /// - eventToReply: the event to reply + /// - rawText: the raw text to send + /// - htmlText: the html text to send + /// - completion: http operation completion block + func sendReply(to eventToReply: MXEvent, + rawText: String, + htmlText: String, + completion: @escaping (MXResponse) -> Void) { + + handleFormattedSendReply(to: eventToReply, rawText: rawText, html: htmlText, completion: completion) + } + + /// Replace a text in an event. /// /// - Parameters: @@ -122,29 +151,24 @@ extension RoomDataSource { } else { rawText = sanitized.string } - - let eventBody = event.content[kMXMessageBodyKey] as? String - let eventFormattedBody = event.content["formatted_body"] as? String - - if rawText != eventBody && (eventFormattedBody == nil || html != eventFormattedBody) { - self.mxSession.aggregations.replaceTextMessageEvent( - event, - withTextMessage: rawText, - formattedText: html, - localEcho: { localEcho in - // Apply the local echo to the timeline - self.updateEvent(withReplace: localEcho) - - // Integrate the replace local event into the timeline like when sending a message - // This also allows to manage read receipt on this replace event - self.queueEvent(forProcessing: localEcho, with: self.roomState, direction: .forwards) - self.processQueuedEvents(nil) - }, - success: success, - failure: failure) - } else { - failure(nil) - } + + handleReplaceFormattedMessage(for: event, rawText: rawText, html: html, success: success, failure: failure) + } + + /// Replace a formatted html text in an event + /// + /// - Parameters: + /// - event: The event to replace + /// - rawText: The new rawText + /// - html: The new html text + /// - success: A block object called when the operation succeeds. It returns the event id of the event generated on the homeserver + /// - failure: A block object called when the operation fails + func replaceFormattedTextMessage( for event: MXEvent, + rawText: String, + html: String, + success: @escaping ((String?) -> Void), + failure: @escaping ((Error?) -> Void)) { + handleReplaceFormattedMessage(for: event, rawText: rawText, html: html, success: success, failure: failure) } /// Retrieve editable attributed text message from an event. @@ -197,6 +221,10 @@ extension RoomDataSource { return editableTextMessage } + + @objc func editableHtmlTextMessage(for event: MXEvent) -> String { + event.content["formatted_body"] as? String ?? event.content["body"] as? String ?? "" + } } // MARK: - Private Helpers @@ -230,4 +258,54 @@ private extension RoomDataSource { func isAttributedTextMessageAnEmote(_ attributedText: NSAttributedString) -> Bool { return attributedText.string.starts(with: Constants.emoteMessageSlashCommandPrefix) } + + func handleReplaceFormattedMessage(for event: MXEvent, + rawText: String, + html: String?, + success: @escaping ((String?) -> Void), + failure: @escaping ((Error?) -> Void)) { + let eventBody = event.content[kMXMessageBodyKey] as? String + let eventFormattedBody = event.content["formatted_body"] as? String + if rawText != eventBody && (eventFormattedBody == nil || html != eventFormattedBody) { + self.mxSession.aggregations.replaceTextMessageEvent( + event, + withTextMessage: rawText, + formattedText: html, + localEcho: { localEcho in + // Apply the local echo to the timeline + self.updateEvent(withReplace: localEcho) + + // Integrate the replace local event into the timeline like when sending a message + // This also allows to manage read receipt on this replace event + self.queueEvent(forProcessing: localEcho, with: self.roomState, direction: .forwards) + self.processQueuedEvents(nil) + }, + success: success, + failure: failure) + } else { + failure(nil) + } + } + + func handleFormattedSendReply(to eventToReply: MXEvent, + rawText: String, + html: String?, + completion: @escaping (MXResponse) -> Void) { + var localEcho: MXEvent? + + let stringLocalizer: MXSendReplyEventStringLocalizerProtocol = MXKSendReplyEventStringLocalizer() + + room.sendReply(to: eventToReply, + textMessage: rawText, + formattedTextMessage: html, + stringLocalizer: stringLocalizer, + threadId: self.threadId, + localEcho: &localEcho, + completion: completion) + + if localEcho != nil { + self.queueEvent(forProcessing: localEcho, with: self.roomState, direction: .forwards) + self.processQueuedEvents(nil) + } + } } diff --git a/Riot/Modules/Room/MXKRoomViewController.m b/Riot/Modules/Room/MXKRoomViewController.m index 9eb3ad3b2..b0f547bc4 100644 --- a/Riot/Modules/Room/MXKRoomViewController.m +++ b/Riot/Modules/Room/MXKRoomViewController.m @@ -1116,7 +1116,7 @@ MXLogDebug(@"[MXKRoomVC] setRoomInputToolbarViewClass: Set inputToolbarView to class %@", roomInputToolbarViewClass); - id inputToolbarView = [roomInputToolbarViewClass roomInputToolbarView]; + id inputToolbarView = [roomInputToolbarViewClass instantiateRoomInputToolbarView]; self->inputToolbarView = inputToolbarView; self->inputToolbarView.delegate = self; @@ -3359,32 +3359,34 @@ - (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView heightDidChanged:(CGFloat)height completion:(void (^)(BOOL finished))completion { - _roomInputToolbarContainerHeightConstraint.constant = height; - - // Update layout with animation - [UIView animateWithDuration:self.resizeComposerAnimationDuration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn - animations:^{ - // We will scroll to bottom if the bottom of the table is currently visible - BOOL shouldScrollToBottom = [self isBubblesTableScrollViewAtTheBottom]; - - CGFloat bubblesTableViewBottomConst = self->_roomInputToolbarContainerBottomConstraint.constant + self->_roomInputToolbarContainerHeightConstraint.constant + self->_roomActivitiesContainerHeightConstraint.constant; - - self->_bubblesTableViewBottomConstraint.constant = bubblesTableViewBottomConst; - - // Force to render the view - [self.view layoutIfNeeded]; - - if (shouldScrollToBottom) - { - [self scrollBubblesTableViewToBottomAnimated:NO]; - } - } - completion:^(BOOL finished){ - if (completion) - { - completion(finished); - } - }]; + // This dispatch fixes a simultaneous accesses crash if this gets called twice quickly in succession + dispatch_async(dispatch_get_main_queue(), ^{ + // Update layout with animation + [UIView animateWithDuration:self.resizeComposerAnimationDuration delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn + animations:^{ + // We will scroll to bottom if the bottom of the table is currently visible + BOOL shouldScrollToBottom = [self isBubblesTableScrollViewAtTheBottom]; + + self->_roomInputToolbarContainerHeightConstraint.constant = height; + CGFloat bubblesTableViewBottomConst = self->_roomInputToolbarContainerBottomConstraint.constant + self->_roomInputToolbarContainerHeightConstraint.constant + self->_roomActivitiesContainerHeightConstraint.constant; + + self->_bubblesTableViewBottomConstraint.constant = bubblesTableViewBottomConst; + + // Force to render the view + [self.view layoutIfNeeded]; + + if (shouldScrollToBottom) + { + [self scrollBubblesTableViewToBottomAnimated:NO]; + } + } + completion:^(BOOL finished){ + if (completion) + { + completion(finished); + } + }]; + }); } - (void)roomInputToolbarView:(MXKRoomInputToolbarView*)toolbarView sendTextMessage:(NSString*)textMessage diff --git a/Riot/Modules/Room/MXKRoomViewController.xib b/Riot/Modules/Room/MXKRoomViewController.xib index 1ad5d0650..c1a016ff5 100644 --- a/Riot/Modules/Room/MXKRoomViewController.xib +++ b/Riot/Modules/Room/MXKRoomViewController.xib @@ -1,10 +1,9 @@ - - - - + + - + + @@ -59,6 +58,7 @@ + diff --git a/Riot/Modules/Room/RoomViewController.m b/Riot/Modules/Room/RoomViewController.m index 8e394b21f..c01620d4e 100644 --- a/Riot/Modules/Room/RoomViewController.m +++ b/Riot/Modules/Room/RoomViewController.m @@ -97,7 +97,7 @@ static CGSize kThreadListBarButtonItemImageSize; @interface RoomViewController () + RoomDataSourceDelegate, RoomCreationModalCoordinatorBridgePresenterDelegate, RoomInfoCoordinatorBridgePresenterDelegate, DialpadViewControllerDelegate, RemoveJitsiWidgetViewDelegate, VoiceMessageControllerDelegate, SpaceDetailPresenterDelegate, UserSuggestionCoordinatorBridgeDelegate, ThreadsCoordinatorBridgePresenterDelegate, ThreadsBetaCoordinatorBridgePresenterDelegate, MXThreadingServiceDelegate, RoomParticipantsInviteCoordinatorBridgePresenterDelegate, RoomInputToolbarViewDelegate, ComposerCreateActionListBridgePresenterDelegate> { // The preview header @@ -195,6 +195,7 @@ static CGSize kThreadListBarButtonItemImageSize; @property (nonatomic, strong) RoomContextualMenuPresenter *roomContextualMenuPresenter; @property (nonatomic, strong) MXKErrorAlertPresentation *errorPresenter; @property (nonatomic, strong) NSAttributedString *textMessageBeforeEditing; +@property (nonatomic, strong) NSString *htmlTextBeforeEditing; @property (nonatomic, strong) EditHistoryCoordinatorBridgePresenter *editHistoryPresenter; @property (nonatomic, strong) MXKDocumentPickerPresenter *documentPickerPresenter; @property (nonatomic, strong) EmojiPickerCoordinatorBridgePresenter *emojiPickerCoordinatorBridgePresenter; @@ -209,6 +210,7 @@ static CGSize kThreadListBarButtonItemImageSize; @property (nonatomic, strong) ThreadsCoordinatorBridgePresenter *threadsBridgePresenter; @property (nonatomic, strong) ThreadsBetaCoordinatorBridgePresenter *threadsBetaBridgePresenter; @property (nonatomic, strong) SlidingModalPresenter *threadsNoticeModalPresenter; +@property (nonatomic, strong) ComposerCreateActionListBridgePresenter *composerCreateActionListBridgePresenter; @property (nonatomic, getter=isActivitiesViewExpanded) BOOL activitiesViewExpanded; @property (nonatomic, getter=isScrollToBottomHidden) BOOL scrollToBottomHidden; @property (nonatomic, getter=isMissedDiscussionsBadgeHidden) BOOL missedDiscussionsBadgeHidden; @@ -672,9 +674,7 @@ static CGSize kThreadListBarButtonItemImageSize; { // Retrieve the potential message partially typed during last room display. // Note: We have to wait for viewDidAppear before updating growingTextView (viewWillAppear is too early) - RoomInputToolbarView *inputToolbar = (RoomInputToolbarView *)self.inputToolbarView; - - inputToolbar.attributedTextMessage = self.roomDataSource.partialAttributedTextMessage; + self.inputToolbarView.attributedTextMessage = self.roomDataSource.partialAttributedTextMessage; } } @@ -1152,10 +1152,23 @@ static CGSize kThreadListBarButtonItemImageSize; [self notifyDelegateOnLeaveRoomIfNecessary]; } + ++ (Class) mainToolbarClass +{ + if (RiotSettings.shared.enableWysiwygComposer) + { + return WysiwygInputToolbarView.class; + } + else + { + return RoomInputToolbarView.class; + } +} + // Set the input toolbar according to the current display - (void)updateRoomInputToolbarViewClassIfNeeded { - Class roomInputToolbarViewClass = RoomInputToolbarView.class; + Class roomInputToolbarViewClass = [RoomViewController mainToolbarClass]; BOOL shouldDismissContextualMenu = NO; @@ -1198,10 +1211,10 @@ static CGSize kThreadListBarButtonItemImageSize; { [super setRoomInputToolbarViewClass:roomInputToolbarViewClass]; - // The voice message toolbar cannot be set on DisabledInputToolbarView and on new direct chat. - if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class] && !self.isNewDirectChat) - { - [(RoomInputToolbarView *)self.inputToolbarView setVoiceMessageToolbarView:self.voiceMessageController.voiceMessageToolbarView]; + + if ([self.inputToolbarView.class conformsToProtocol:@protocol(RoomInputToolbarViewProtocol)]) { + id inputToolbar = (id)self.inputToolbarView; + [inputToolbar setVoiceMessageToolbarView:self.voiceMessageController.voiceMessageToolbarView]; } [self updateInputToolBarViewHeight]; @@ -1214,9 +1227,9 @@ static CGSize kThreadListBarButtonItemImageSize; { CGFloat height = 0; - if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) - { - height = ((RoomInputToolbarView*)self.inputToolbarView).mainToolbarHeightConstraint.constant; + if ([self.inputToolbarView.class conformsToProtocol:@protocol(RoomInputToolbarViewProtocol)]) { + id inputToolbar = (id)self.inputToolbarView; + height = inputToolbar.toolbarHeight; } else if ([self.inputToolbarView isKindOfClass:DisabledRoomInputToolbarView.class]) { @@ -2029,9 +2042,9 @@ static CGSize kThreadListBarButtonItemImageSize; - (void)setInputToolBarSendMode:(RoomInputToolbarViewSendMode)sendMode forEventWithId:(NSString *)eventId { - if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:[RoomInputToolbarView class]]) + if (self.inputToolbarView && [self inputToolbarConformsToToolbarViewProtocol]) { - RoomInputToolbarView *roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView; + MXKRoomInputToolbarView *roomInputToolbarView = (MXKRoomInputToolbarView *) self.inputToolbarView; if (eventId) { MXEvent *event = [self.roomDataSource eventWithEventId:eventId]; @@ -2165,11 +2178,9 @@ static CGSize kThreadListBarButtonItemImageSize; UIView *sourceView; - RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView]; - - if (roomInputToolbarView) + if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { - sourceView = roomInputToolbarView.attachMediaButton; + sourceView = ((RoomInputToolbarView*)self.inputToolbarView).attachMediaButton; } else { @@ -2241,6 +2252,7 @@ static CGSize kThreadListBarButtonItemImageSize; } - (void)setupActions { + if (![self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) { return; } @@ -2432,8 +2444,7 @@ static CGSize kThreadListBarButtonItemImageSize; */ - (void)sendVideoAsset:(AVAsset *)videoAsset isPhotoLibraryAsset:(BOOL)isPhotoLibraryAsset { - RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView]; - if (!roomInputToolbarView) + if (![self inputToolbarConformsToToolbarViewProtocol]) { return; } @@ -2454,15 +2465,27 @@ static CGSize kThreadListBarButtonItemImageSize; // Create before sending the message in case of a discussion (direct chat) [self createDiscussionIfNeeded:^(BOOL readyToSend) { - if (readyToSend) + if (readyToSend && [self inputToolbarConformsToToolbarViewProtocol]) { - [[self inputToolbarViewAsRoomInputToolbarView] sendSelectedVideoAsset:videoAsset isPhotoLibraryAsset:isPhotoLibraryAsset]; + [self.inputToolbarView sendSelectedVideoAsset:videoAsset isPhotoLibraryAsset:isPhotoLibraryAsset]; } // Errors are handled at the request level. This should be improved in case of code rewriting. }]; }]; - compressionPrompt.popoverPresentationController.sourceView = roomInputToolbarView.attachMediaButton; - compressionPrompt.popoverPresentationController.sourceRect = roomInputToolbarView.attachMediaButton.bounds; + + UIView *sourceView; + + if ([self.inputToolbarView isKindOfClass:RoomInputToolbarView.class]) + { + sourceView = ((RoomInputToolbarView*)self.inputToolbarView).attachMediaButton; + } + else + { + sourceView = self.inputToolbarView; + } + + compressionPrompt.popoverPresentationController.sourceView = sourceView; + compressionPrompt.popoverPresentationController.sourceRect = sourceView.bounds; [self presentViewController:compressionPrompt animated:YES completion:nil]; } @@ -2473,9 +2496,9 @@ static CGSize kThreadListBarButtonItemImageSize; // Create before sending the message in case of a discussion (direct chat) [self createDiscussionIfNeeded:^(BOOL readyToSend) { - if (readyToSend) + if (readyToSend && [self inputToolbarConformsToToolbarViewProtocol]) { - [[self inputToolbarViewAsRoomInputToolbarView] sendSelectedVideoAsset:videoAsset isPhotoLibraryAsset:isPhotoLibraryAsset]; + [self.inputToolbarView sendSelectedVideoAsset:videoAsset isPhotoLibraryAsset:isPhotoLibraryAsset]; } // Errors are handled at the request level. This should be improved in case of code rewriting. }]; @@ -4608,12 +4631,16 @@ static CGSize kThreadListBarButtonItemImageSize; { MXEvent *event = [self.roomDataSource eventWithEventId:eventId]; - RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView]; - - if (roomInputToolbarView) + if ([self inputToolbarConformsToHtmlToolbarViewProtocol]) { - self.textMessageBeforeEditing = roomInputToolbarView.attributedTextMessage; - roomInputToolbarView.attributedTextMessage = [self.customizedRoomDataSource editableAttributedTextMessageFor:event]; + MXKRoomInputToolbarView *htmlInputToolBarView = (MXKRoomInputToolbarView *) self.inputToolbarView; + self.htmlTextBeforeEditing = htmlInputToolBarView.htmlContent; + htmlInputToolBarView.htmlContent = [self.customizedRoomDataSource editableHtmlTextMessageFor:event]; + } + else if ([self inputToolbarConformsToToolbarViewProtocol]) + { + self.textMessageBeforeEditing = self.inputToolbarView.attributedTextMessage; + self.inputToolbarView.attributedTextMessage = [self.customizedRoomDataSource editableAttributedTextMessageFor:event]; } [self selectEventWithId:eventId inputToolBarSendMode:RoomInputToolbarViewSendModeEdit showTimestamp:YES]; @@ -4621,26 +4648,30 @@ static CGSize kThreadListBarButtonItemImageSize; - (void)restoreTextMessageBeforeEditing { - RoomInputToolbarView *roomInputToolbarView = [self inputToolbarViewAsRoomInputToolbarView]; - if (self.textMessageBeforeEditing) + + if (self.htmlTextBeforeEditing && [self inputToolbarConformsToHtmlToolbarViewProtocol]) { - roomInputToolbarView.attributedTextMessage = self.textMessageBeforeEditing; + MXKRoomInputToolbarView *htmlInputToolBarView = (MXKRoomInputToolbarView *) self.inputToolbarView; + htmlInputToolBarView.htmlContent = self.htmlTextBeforeEditing; + } + else if (self.textMessageBeforeEditing && [self inputToolbarConformsToToolbarViewProtocol]) + { + self.inputToolbarView.attributedTextMessage = self.textMessageBeforeEditing; } self.textMessageBeforeEditing = nil; + self.htmlTextBeforeEditing = nil; } -- (RoomInputToolbarView*)inputToolbarViewAsRoomInputToolbarView +- (BOOL)inputToolbarConformsToHtmlToolbarViewProtocol { - RoomInputToolbarView *roomInputToolbarView; - - if (self.inputToolbarView && [self.inputToolbarView isKindOfClass:[RoomInputToolbarView class]]) - { - roomInputToolbarView = (RoomInputToolbarView*)self.inputToolbarView; - } - - return roomInputToolbarView; + return [self.inputToolbarView conformsToProtocol:@protocol(HtmlRoomInputToolbarViewProtocol)]; +} + +- (BOOL)inputToolbarConformsToToolbarViewProtocol +{ + return [self.inputToolbarView conformsToProtocol:@protocol(RoomInputToolbarViewProtocol)]; } - (void)showDifferentURLsAlertFor:(NSURL *)url visibleURLString:(NSString *)visibleURLString @@ -4933,7 +4964,7 @@ static CGSize kThreadListBarButtonItemImageSize; { if (self.roomInputToolbarContainerHeightConstraint.constant != height) { - // Hide temporarily the placeholder to prevent its distorsion during height animation + // Hide temporarily the placeholder to prevent its distortion during height animation if (!savedInputToolbarPlaceholder) { savedInputToolbarPlaceholder = toolbarView.placeholder.length ? toolbarView.placeholder : @""; @@ -4958,7 +4989,7 @@ static CGSize kThreadListBarButtonItemImageSize; } } -- (void)roomInputToolbarViewDidTapCancel:(RoomInputToolbarView*)toolbarView +- (void)roomInputToolbarViewDidTapCancel:(MXKRoomInputToolbarView*)toolbarView { [self cancelEventSelection]; } @@ -4977,6 +5008,53 @@ static CGSize kThreadListBarButtonItemImageSize; } } +- (void)roomInputToolbarView:(RoomInputToolbarView *)toolbarView sendFormattedTextMessage:(NSString *)formattedTextMessage withRawText:(NSString *)rawText +{ + // Create before sending the message in case of a discussion (direct chat) + MXWeakify(self); + [self createDiscussionIfNeeded:^(BOOL readyToSend) { + MXStrongifyAndReturnIfNil(self); + + if (readyToSend) { + [self sendFormattedTextMessage:rawText htmlMsg:formattedTextMessage]; + } + // Errors are handled at the request level. This should be improved in case of code rewriting. + }]; +} + +- (void)roomInputToolbarViewShowSendMediaActions:(MXKRoomInputToolbarView *)toolbarView +{ + NSMutableArray *actionItems = [NSMutableArray new]; + if (RiotSettings.shared.roomScreenAllowMediaLibraryAction) + { + [actionItems addObject:@(ComposerCreateActionPhotoLibrary)]; + } + if (RiotSettings.shared.roomScreenAllowStickerAction && !self.isNewDirectChat) + { + [actionItems addObject:@(ComposerCreateActionStickers)]; + } + if (RiotSettings.shared.roomScreenAllowFilesAction) + { + [actionItems addObject:@(ComposerCreateActionAttachments)]; + } + if (BuildSettings.pollsEnabled && self.displayConfiguration.sendingPollsEnabled && !self.isNewDirectChat) + { + [actionItems addObject:@(ComposerCreateActionPolls)]; + } + if (BuildSettings.locationSharingEnabled && !self.isNewDirectChat) + { + [actionItems addObject:@(ComposerCreateActionLocation)]; + } + if (RiotSettings.shared.roomScreenAllowCameraAction) + { + [actionItems addObject:@(ComposerCreateActionCamera)]; + } + + self.composerCreateActionListBridgePresenter = [[ComposerCreateActionListBridgePresenter alloc] initWithActions:actionItems]; + self.composerCreateActionListBridgePresenter.delegate = self; + [self.composerCreateActionListBridgePresenter presentFrom:self animated:YES]; +} + - (void)roomInputToolbarView:(RoomInputToolbarView *)toolbarView sendAttributedTextMessage:(NSAttributedString *)attributedTextMessage { // Create before sending the message in case of a discussion (direct chat) @@ -5323,7 +5401,7 @@ static CGSize kThreadListBarButtonItemImageSize; else { // Enable back the text input - [self setRoomInputToolbarViewClass:RoomInputToolbarView.class]; + [self setRoomInputToolbarViewClass:[RoomViewController mainToolbarClass]]; [self updateInputToolBarViewHeight]; // And the extra area @@ -7575,9 +7653,9 @@ static CGSize kThreadListBarButtonItemImageSize; // Create before sending the message in case of a discussion (direct chat) [self createDiscussionIfNeeded:^(BOOL readyToSend) { - if (readyToSend) + if (readyToSend && [self inputToolbarConformsToToolbarViewProtocol]) { - [[self inputToolbarViewAsRoomInputToolbarView] sendSelectedImage:imageData + [self.inputToolbarView sendSelectedImage:imageData withMimeType:MXKUTI.jpeg.mimeType andCompressionMode:MediaCompressionHelper.defaultCompressionMode isPhotoLibraryAsset:NO]; @@ -7610,9 +7688,9 @@ static CGSize kThreadListBarButtonItemImageSize; // Create before sending the message in case of a discussion (direct chat) [self createDiscussionIfNeeded:^(BOOL readyToSend) { - if (readyToSend) + if (readyToSend && [self inputToolbarConformsToToolbarViewProtocol]) { - [[self inputToolbarViewAsRoomInputToolbarView] sendSelectedImage:imageData + [self.inputToolbarView sendSelectedImage:imageData withMimeType:uti.mimeType andCompressionMode:MediaCompressionHelper.defaultCompressionMode isPhotoLibraryAsset:YES]; @@ -7639,9 +7717,9 @@ static CGSize kThreadListBarButtonItemImageSize; // Create before sending the message in case of a discussion (direct chat) [self createDiscussionIfNeeded:^(BOOL readyToSend) { - if (readyToSend) + if (readyToSend && [self inputToolbarConformsToToolbarViewProtocol]) { - [[self inputToolbarViewAsRoomInputToolbarView] sendSelectedAssets:assets withCompressionMode:MediaCompressionHelper.defaultCompressionMode]; + [self.inputToolbarView sendSelectedAssets:assets withCompressionMode:MediaCompressionHelper.defaultCompressionMode]; } // Errors are handled at the request level. This should be improved in case of code rewriting. }]; @@ -7880,4 +7958,39 @@ static CGSize kThreadListBarButtonItemImageSize; } } +#pragma mark - ComposerCreateActionListBridgePresenter + +- (void)composerCreateActionListBridgePresenterDelegateDidComplete:(ComposerCreateActionListBridgePresenter *)coordinatorBridgePresenter action:(enum ComposerCreateAction)action +{ + + [coordinatorBridgePresenter dismissWithAnimated:true completion:^{ + switch (action) { + case ComposerCreateActionPhotoLibrary: + [self showMediaPickerAnimated:YES]; + break; + case ComposerCreateActionStickers: + [self roomInputToolbarViewPresentStickerPicker]; + break; + case ComposerCreateActionAttachments: + [self roomInputToolbarViewDidTapFileUpload]; + break; + case ComposerCreateActionPolls: + [self.delegate roomViewControllerDidRequestPollCreationFormPresentation:self]; + break; + case ComposerCreateActionLocation: + [self.delegate roomViewControllerDidRequestLocationSharingFormPresentation:self]; + break; + case ComposerCreateActionCamera: + [self showCameraControllerAnimated:YES]; + break; + } + self.composerCreateActionListBridgePresenter = nil; + }]; +} + +- (void)composerCreateActionListBridgePresenterDidDismissInteractively:(ComposerCreateActionListBridgePresenter *)coordinatorBridgePresenter +{ + self.composerCreateActionListBridgePresenter = nil; +} + @end diff --git a/Riot/Modules/Room/RoomViewController.swift b/Riot/Modules/Room/RoomViewController.swift index 3fc72f6ab..7bbc6812c 100644 --- a/Riot/Modules/Room/RoomViewController.swift +++ b/Riot/Modules/Room/RoomViewController.swift @@ -52,6 +52,55 @@ extension RoomViewController { } + /// Send the formatted text message and its raw counterpat to the room + /// + /// - Parameter rawTextMsg: the raw text message + /// - Parameter htmlMsg: the html text message + @objc func sendFormattedTextMessage(_ rawTextMsg: String, htmlMsg: String) { + let eventModified = self.roomDataSource.event(withEventId: customizedRoomDataSource?.selectedEventId) + self.setupRoomDataSource { roomDataSource in + guard let roomDataSource = roomDataSource as? RoomDataSource else { return } + if self.wysiwygInputToolbar?.sendMode == .reply, let eventModified = eventModified { + roomDataSource.sendReply(to: eventModified, rawText: rawTextMsg, htmlText: htmlMsg) { response in + switch response { + case .success: + break + case .failure: + MXLog.error("[RoomViewController] sendFormattedTextMessage failed while updating event", context: [ + "event_id": eventModified.eventId + ]) + } + } + } else if self.wysiwygInputToolbar?.sendMode == .edit, let eventModified = eventModified { + roomDataSource.replaceFormattedTextMessage( + for: eventModified, + rawText: rawTextMsg, + html: htmlMsg, + success: { _ in + // + }, + failure: { _ in + MXLog.error("[RoomViewController] sendFormattedTextMessage failed while updating event", context: [ + "event_id": eventModified.eventId + ]) + }) + } else { + roomDataSource.sendFormattedTextMessage(rawTextMsg, html: htmlMsg) { response in + switch response { + case .success: + break + case .failure: + MXLog.error("[RoomViewController] sendFormattedTextMessage failed") + } + } + } + + if self.customizedRoomDataSource?.selectedEventId != nil { + self.cancelEventSelection() + } + } + } + /// Send given attributed text message to the room /// /// - Parameter attributedTextMsg: the attributed text message @@ -107,4 +156,8 @@ private extension RoomViewController { var inputToolbar: RoomInputToolbarView? { return self.inputToolbarView as? RoomInputToolbarView } + + var wysiwygInputToolbar: WysiwygInputToolbarView? { + return self.inputToolbarView as? WysiwygInputToolbarView + } } diff --git a/Riot/Modules/Room/Views/InputToolbar/DisabledRoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/DisabledRoomInputToolbarView.m index c3337e887..32859feeb 100644 --- a/Riot/Modules/Room/Views/InputToolbar/DisabledRoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/DisabledRoomInputToolbarView.m @@ -27,7 +27,7 @@ bundle:[NSBundle bundleForClass:[DisabledRoomInputToolbarView class]]]; } -+ (instancetype)roomInputToolbarView ++ (MXKRoomInputToolbarView *)instantiateRoomInputToolbarView { if ([[self class] nib]) { diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h index 1f889bb9d..72341ff2a 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.h @@ -33,6 +33,16 @@ typedef NS_ENUM(NSUInteger, RoomInputToolbarViewSendMode) }; +@protocol RoomInputToolbarViewProtocol + +@property (nonatomic, strong) NSString *eventSenderDisplayName; +@property (nonatomic, assign) RoomInputToolbarViewSendMode sendMode; +- (void)setVoiceMessageToolbarView:(UIView *)voiceMessageToolbarView; +- (CGFloat)toolbarHeight; + + +@end + @protocol RoomInputToolbarViewDelegate /** @@ -40,7 +50,7 @@ typedef NS_ENUM(NSUInteger, RoomInputToolbarViewSendMode) @param toolbarView the room input toolbar view */ -- (void)roomInputToolbarViewDidTapCancel:(RoomInputToolbarView*)toolbarView; +- (void)roomInputToolbarViewDidTapCancel:(MXKRoomInputToolbarView*)toolbarView; /** Inform the delegate that the text message has changed. diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index f3b93ad44..cd9195516 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -30,7 +30,7 @@ static const NSTimeInterval kActionMenuAttachButtonAnimationDuration = .4; static const NSTimeInterval kActionMenuContentAlphaAnimationDuration = .2; static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; -@interface RoomInputToolbarView() +@interface RoomInputToolbarView() @property (nonatomic, weak) IBOutlet UIView *mainToolbarView; @@ -59,7 +59,7 @@ static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; @implementation RoomInputToolbarView @dynamic delegate; -+ (instancetype)roomInputToolbarView ++ (MXKRoomInputToolbarView *)instantiateRoomInputToolbarView { UINib *nib = [UINib nibWithNibName:NSStringFromClass([RoomInputToolbarView class]) bundle:nil]; return [nib instantiateWithOwner:nil options:nil].firstObject; @@ -85,25 +85,6 @@ static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; self.textView.inputAccessoryView = inputAccessoryViewForKeyboard; } -- (void)setVoiceMessageToolbarView:(UIView *)voiceMessageToolbarView -{ - if (voiceMessageToolbarView) { - _voiceMessageToolbarView = voiceMessageToolbarView; - self.voiceMessageToolbarView.translatesAutoresizingMaskIntoConstraints = NO; - [self addSubview:self.voiceMessageToolbarView]; - - [NSLayoutConstraint activateConstraints:@[[self.mainToolbarView.topAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.topAnchor], - [self.mainToolbarView.leftAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.leftAnchor], - [self.mainToolbarView.bottomAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.bottomAnchor], - [self.mainToolbarView.rightAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.rightAnchor]]]; - } - else - { - [self.voiceMessageToolbarView removeFromSuperview]; - _voiceMessageToolbarView = nil; - } -} - #pragma mark - Override MXKView -(void)customizeViewRendering @@ -543,4 +524,28 @@ static const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; }]; } +#pragma mark - RoomInputToolbarViewProtocol + +- (CGFloat)toolbarHeight { + return self.mainToolbarHeightConstraint.constant; +} + +- (void)setVoiceMessageToolbarView:(UIView *)voiceMessageToolbarView +{ + if (voiceMessageToolbarView) { + _voiceMessageToolbarView = voiceMessageToolbarView; + self.voiceMessageToolbarView.translatesAutoresizingMaskIntoConstraints = NO; + [self addSubview:self.voiceMessageToolbarView]; + + [NSLayoutConstraint activateConstraints:@[[self.mainToolbarView.topAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.topAnchor], + [self.mainToolbarView.leftAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.leftAnchor], + [self.mainToolbarView.bottomAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.bottomAnchor], + [self.mainToolbarView.rightAnchor constraintEqualToAnchor:self.voiceMessageToolbarView.rightAnchor]]]; + } + else + { + [self.voiceMessageToolbarView removeFromSuperview]; + _voiceMessageToolbarView = nil; + } +} @end diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib index ff1cee7be..16118e659 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.xib @@ -1,9 +1,9 @@ - + - + @@ -27,7 +27,7 @@ -