From 8fc3632863415a9bfc52de2d79a1dd0c36565a80 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Thu, 7 Oct 2021 15:37:20 +0200 Subject: [PATCH 1/6] [Spaces] M10.4.1 Home space data filtering #4570 - Added settings menu for Home space --- .../space_private_icon.imageset/Contents.json | 26 ++++++ .../space_private_icon.png | Bin 0 -> 1003 bytes .../space_private_icon@2x.png | Bin 0 -> 1448 bytes .../space_private_icon@3x.png | Bin 0 -> 1914 bytes Riot/Assets/en.lproj/Vector.strings | 1 + Riot/Generated/Images.swift | 16 +++- Riot/Generated/Strings.swift | 4 + .../Recents/DataSources/RecentsDataSource.m | 2 +- .../SpaceDetailViewController.swift | 3 + .../Spaces/SpaceList/SpaceListViewCell.swift | 2 +- .../Spaces/SpaceList/SpaceListViewModel.swift | 3 +- .../Spaces/SpaceMenu/SpaceMenuCell.swift | 21 +++++ .../SpaceMenu/SpaceMenuListItemViewData.swift | 2 + .../SpaceMenu/SpaceMenuListViewCell.swift | 2 +- .../SpaceMenu/SpaceMenuSwitchViewCell.swift | 74 ++++++++++++++++++ .../SpaceMenu/SpaceMenuSwitchViewCell.xib | 64 +++++++++++++++ .../SpaceMenu/SpaceMenuViewController.swift | 26 ++++-- .../Spaces/SpaceMenu/SpaceMenuViewModel.swift | 28 +++++-- .../Spaces/SpaceMenu/SpaceMenuViewState.swift | 1 + 19 files changed, 259 insertions(+), 16 deletions(-) create mode 100644 Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/Contents.json create mode 100644 Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/space_private_icon.png create mode 100644 Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/space_private_icon@2x.png create mode 100644 Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/space_private_icon@3x.png create mode 100644 Riot/Modules/Spaces/SpaceMenu/SpaceMenuCell.swift create mode 100644 Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift create mode 100644 Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.xib diff --git a/Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/Contents.json b/Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/Contents.json new file mode 100644 index 000000000..6bb7945f7 --- /dev/null +++ b/Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/Contents.json @@ -0,0 +1,26 @@ +{ + "images" : [ + { + "filename" : "space_private_icon.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "space_private_icon@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "space_private_icon@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "template-rendering-intent" : "template" + } +} diff --git a/Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/space_private_icon.png b/Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/space_private_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..10bc95ab82ab6db1ea7d4fd44aa9486f9c2bce9b GIT binary patch literal 1003 zcmeAS@N?(olHy`uVBq!ia0vp^JRr=$1|-8uW1a&kmUKs7M+SzC{oH>NS%G|oWRD45bDP46hOx7_4S6Fo+k-*%fF5lxRtf@J#ddWzYh$IT%VstT4fPE4;bsH1+JHo@{EISEfi{E8w==W>t3(ll+GC>+vK+}V5TAlYfnK%a zveAbJn;n;A+(UL?Am@0xIEHA5#s=>8Vh$9r-8_40iZHWa$Oger;zB$t82>hfm?_>D z(79{3wY9aCqpq1zalw+!Teq5bWSbfCmw)dwF8Af&yOa8Ej%E6}JFL?LSme}=90*H)`c@Cv6?!iRGXYnn)Q{T%qdK` zZ{25mmpWUW#=U#0K0JJt_vy;a#X0WbJ~NhlxXzrdnG>RZq{3;APQsb#H#*;3wDGHB zspez!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBDAAG{;hE;^%b*2hb1<+n z3NbJPS&Tr)z$nE4G7ZRL@M4sPvx68lplX;H7}_%#SfFa6fHVk90Ai3H2+h2J5n0+>{umUo3Q%e#RDspr3imfVamB1>jfNYSkzLEl1NlCV?QiN}Sf^&XRs)CuG zfu4bq9hZWFf=y9MnpKdC8&o@xXRDM^Qc_^0uU}qXu2*iXmtT~wZ)j<0sc&GUZ)Btk zRH0j3nOBlnp_^B%3^4>|j!SBBa#3bMNoIbY0?6FNr2NtnTO}osMQ{LdXGvxn!lt}p zsJDO~)CbAv8|oS8!_5Y2wE>A*`4?rT0&NDFZ)a!&R*518wZ}#uWI2*!AU*|)0=;U- zWup%dHajlKxQFZv49uFIE{-7{ym!OS`wKgY)aCDP&Go)mq!iOr_2%La<2Vk(c@{y* zk7CzL9Ozcd{-W}tarXZPwWl6Qj~Fd8coaWsn5(KSsbb39{nlx>bzOei_7ZPF4;#yq z8;n2S`@Yxw{c({+kKHP@Tu*J~Te;Ih_@-g=e=Ys#!690#QO`fGK64`9S~pw$Mqaqu z-8Q@T*DkW3w0D;lSbvkZJv=^cvB8`$pXr_#Z*s(?KHFN>{Y-j$`o0&^PUfDuWq)~H z;+}?yy^9jt|L_chp!3zPE6j(N+Sv1SzONKE&VSi4TgFVGP*;~V{hoddSMFB7vQtXF zE9^Et(hbm7d9ujsqiAl3Pe8$5tA823&slD~n||hspiS^)s`>2m&&({I&k2qz-fL!9 z9@_k9Rp*{F^)vpT*m=hHMb5Q3Artd#a+q>0l(cQ1vOhQE>YLnl%Rte};^#8{@YvZ; z%8XKb4=U!q+@ql-vZq$XtL>pd_M&+LGr4b;*=4lzuI&sm+G_W1Te84DwbMSw99yFS<<*%Z^^n(D_=TD@-90v-(+vJjLs5izUP~7typDRv3%-R(YH%2j|c{99lE0V zsAI0Bf%w(hy3{(Ms~@G#EZ(v1M0A9?<0P4+Q;T>~g#311_OE$=T`g_;?%#(FJUUXN z%m1YQ?+urt;~{sa|8j0wAauR{5{pM7>-Lt36WRj`=f$t=t$M!l#iB0_ZUb;_p?K%v>`!$~!Em&>n;@sM$EFv;+h4YOC(c#Ov#|fj7IF3>;}?k+bN+8WTA!W}?KJtsJI!R)HP2og5SqK@ z65phjPg8!`%~dRE$~!gX;foK)LsPiY)6e8LZC^J(Vb9TuHy=3WBshg9cdTl2=8^E5 zoKm^y>BKSh5CMV_vdcOsgKc23BF6*2U FngHx;9)bV> literal 0 HcmV?d00001 diff --git a/Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/space_private_icon@3x.png b/Riot/Assets/Images.xcassets/Spaces/space_private_icon.imageset/space_private_icon@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..eaf48592a8316995ad9f9468f7eacd45410351e5 GIT binary patch literal 1914 zcmY*a2~<;88h!yph=a70vI$HG9408)Ku|&g1Oizgge^5tBm@W;H3X9&DN|O7Yyt{X ziB%vfi*W&hU>ju-L2#5Jic*we6Pbd5C?1rx&WkcQbKkk||L%Xk@B8n6&pYSk2KbZp zpk`120Q9I7QVt|#~?V>n$J;F4*B9J*Vts+$Th!UwJdJW@f~2$O`wE|0KjUm zdP2NUVoMO zr_pfOCyMwG0nVZaz`O)PF3cH)L1Ex-P#6q`7skZmf=E6aa&Sd}9}tTZaA8>ZGG1i;+2p@IB(5l0$V(9O1o1GnFDFWnC?>$+ z>O!02^Et)5*e{jhMH_8_4WiXMXe7XOL)f4Pl1c(l6s z{~YFXr=PN5t8P#{dh^=cpr#SyTfvWLl}aLpuyp5G^c-_^y(JCU=sWiHY~KTl<^lkYN1$Ezg8a+}`R zq~t$qZmmTyf8XmkOdxP_YLHWNKVSaP`r+w5X5Hm|eQ@tnnAbZ=x@ph`y?nX*@w&^j z=R+a`!mon)Fk#vS>%dqacB8KyrG6JBL~}UwifU#@(Ly_q=xhDJViEH(q||wr=J3PL zX(O?rXqVAJ^8yQ=LZkU$&eealv?$C~1&C5kl4TLI<8KCO28zGKby{&LiGP%LnZ7F+ z^LOeY-_sZv5tgDO*3MP_7;HaDU9{@#%W=B%8^yq-G~^cf`}VU z4eU>1PdwdzIn^n6d){b2?TBw#2mKRDhVl&JxSkMB$yJ{%K@zw|NE9;kS zxyq=VLED}qG{wF~`Wae6BX~Tos_NwiKOn%_blMA?%lmE=<5zrr|ovD4jI~vng zH6K5^AN2^r9X3|%naZ#7G-@5HCseL%bM&T^JNPI6l*h<+GQQYcX^g2eJy1zX@GV&$ z?>8SXsA+#gEE|#`uGFvsgoDOS53=azv1DB*+&M#O&cLmr1w!$|>0ujh?f$V-u4|!Q z{FeTT9;``okgPQct9``d*!gU4HZap*b=iWucJzvDXL*A%dnll>pjN60 zu{(CR<>s+nr;7a$TD{u60r~;AG<24vPnp)Paq+!@6AamJO?o0_d*JUZv+k1vPZjQO z58boDlw}gSkyICq-=HaPVX3ldWtLNb>+Y^Kv7fFtDrH7G94^V2JCI!ej6gk{ zl8YTbG=?lr$v*yYe&0Ji<_b$OmRP^HhB@YT_^(=TBP}7gJC!V0N?X4#Nz3f-Q$SO9ieu9OI;pA*;ukz#~5*TOZSz79VWLQ%PL!!SDQYBt+*r1MOXbEPZTP*nT_Vk zdv_d|EU6$HH<2gIrD*47t*|ABn-ebA&QgsVmjElf?VU*%+iAV$wb6H$G_hp?n#OC* zt7rqLXadQxXip3T(t Image { + let bundle = BundleToken.bundle + guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else { + fatalError("Unable to load image asset named \(name).") + } + return result + } + #endif } internal extension ImageAsset.Image { + @available(iOS 8.0, tvOS 9.0, watchOS 2.0, *) @available(macOS, deprecated, message: "This initializer is unsafe on macOS, please use the ImageAsset.image property") convenience init!(asset: ImageAsset) { diff --git a/Riot/Generated/Strings.swift b/Riot/Generated/Strings.swift index d82e45c6f..e53eed47b 100644 --- a/Riot/Generated/Strings.swift +++ b/Riot/Generated/Strings.swift @@ -4855,6 +4855,10 @@ public class VectorL10n: NSObject { public static var spaceFeatureUnavailableTitle: String { return VectorL10n.tr("Vector", "space_feature_unavailable_title") } + /// Show all rooms + public static var spaceHomeShowAllRooms: String { + return VectorL10n.tr("Vector", "space_home_show_all_rooms") + } /// Ban from this space public static var spaceParticipantsActionBan: String { return VectorL10n.tr("Vector", "space_participants_action_ban") diff --git a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m index a219ab3c9..cdd7d014e 100644 --- a/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m +++ b/Riot/Modules/Common/Recents/DataSources/RecentsDataSource.m @@ -1610,7 +1610,7 @@ NSString *const kRecentsDataSourceTapOnDirectoryServerChange = @"kRecentsDataSou } MXLogDebug(@"[RecentsDataSource] refreshRoomsSections: Done in %.0fms", [[NSDate date] timeIntervalSinceDate:startDate] * 1000); - MXLogDebug(@"[Spaces] refreshRoomsSections with %ld suggested room", suggestedRoomCellDataArray.count); + MXLogDebug(@"[RecentsDataSource] refreshRoomsSections with %ld suggested room", suggestedRoomCellDataArray.count); return [[RecentsDataSourceState alloc] initWithInvitesCellDataArray:invitesCellDataArray diff --git a/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewController.swift b/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewController.swift index 95f7e7b19..bca282949 100644 --- a/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewController.swift +++ b/Riot/Modules/Spaces/SpaceDetail/SpaceDetailViewController.swift @@ -231,6 +231,9 @@ class SpaceDetailViewController: UIViewController { let membersString = membersCount == 1 ? VectorL10n.roomTitleOneMember : VectorL10n.roomTitleMembers("\(membersCount)") self.spaceTypeLabel.text = "\(joinRuleString) · \(membersString)" + let joinRuleIcon = parameters.joinRule == .public ? Asset.Images.spaceTypeIcon : Asset.Images.spacePrivateIcon + self.spaceTypeIconView.image = joinRuleIcon.image + self.inviterIdLabel.text = parameters.inviterId if let inviterId = parameters.inviterId { self.inviterTitleLabel.text = "\(parameters.inviter?.displayname ?? inviterId) invited you" diff --git a/Riot/Modules/Spaces/SpaceList/SpaceListViewCell.swift b/Riot/Modules/Spaces/SpaceList/SpaceListViewCell.swift index d0ea314dd..eaf3a5e09 100644 --- a/Riot/Modules/Spaces/SpaceList/SpaceListViewCell.swift +++ b/Riot/Modules/Spaces/SpaceList/SpaceListViewCell.swift @@ -58,7 +58,7 @@ final class SpaceListViewCell: UITableViewCell, Themable, NibReusable { func fill(with viewData: SpaceListItemViewData) { self.avatarView.fill(with: viewData.avatarViewData) self.titleLabel.text = viewData.title - self.moreButton.isHidden = viewData.spaceId == SpaceListViewModel.Constants.homeSpaceId || viewData.isInvite + self.moreButton.isHidden = viewData.isInvite if viewData.isInvite { self.isBadgeAlert = true self.badgeLabel.isHidden = false diff --git a/Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift b/Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift index ed11d2df9..3f9924531 100644 --- a/Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift +++ b/Riot/Modules/Spaces/SpaceList/SpaceListViewModel.swift @@ -91,7 +91,8 @@ final class SpaceListViewModel: SpaceListViewModelType { case .moreAction(at: let indexPath, from: let sourceView): let section = self.sections[indexPath.section] switch section { - case .home: break + case .home: + self.coordinatorDelegate?.spaceListViewModel(self, didPressMoreForSpaceWithId: Constants.homeSpaceId, from: sourceView) case .spaces(let viewDataList): let spaceViewData = viewDataList[indexPath.row] self.coordinatorDelegate?.spaceListViewModel(self, didPressMoreForSpaceWithId: spaceViewData.spaceId, from: sourceView) diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuCell.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuCell.swift new file mode 100644 index 000000000..2447ae918 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuCell.swift @@ -0,0 +1,21 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation + +protocol SpaceMenuCell: Themable { + func fill(with viewData: SpaceMenuListItemViewData) +} diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift index 6b7a67fa5..ca555e580 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift @@ -19,6 +19,7 @@ import Foundation /// Style of the `SpaceMenuListViewCell` enum SpaceMenuListItemStyle { case normal + case boolean case destructive } @@ -28,4 +29,5 @@ struct SpaceMenuListItemViewData { let style: SpaceMenuListItemStyle let title: String? let icon: UIImage? + var value: Any? } diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListViewCell.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListViewCell.swift index 952d89d4d..86bfe8805 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListViewCell.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListViewCell.swift @@ -17,7 +17,7 @@ import Foundation import Reusable -class SpaceMenuListViewCell: UITableViewCell, Themable, NibReusable { +class SpaceMenuListViewCell: UITableViewCell, SpaceMenuCell, NibReusable { // MARK: - Properties diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift new file mode 100644 index 000000000..6cd7a7a39 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift @@ -0,0 +1,74 @@ +// +// Copyright 2021 New Vector Ltd +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +import Foundation +import Reusable + +class SpaceMenuSwitchViewCell: UITableViewCell, SpaceMenuCell, NibReusable { + + // MARK: - Properties + + @IBOutlet private weak var titleLabel: UILabel! + @IBOutlet private weak var selectionView: UIView! + @IBOutlet private weak var switchView: UISwitch! + + // MARK: - Private + + private var theme: Theme? + + // MARK: - Life cycle + + override func awakeFromNib() { + super.awakeFromNib() + + self.selectionStyle = .none + self.selectionView.layer.cornerRadius = 8.0 + self.selectionView.layer.masksToBounds = true + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + UIView.animate(withDuration: animated ? 0.3 : 0.0) { + self.selectionView.alpha = selected ? 1.0 : 0.0 + } + } + + // MARK: - Public + + func fill(with viewData: SpaceMenuListItemViewData) { + self.titleLabel.text = viewData.title + self.switchView.isOn = (viewData.value as? Bool) ?? false + + guard let theme = self.theme else { + return + } + + if viewData.style == .destructive { + self.titleLabel.textColor = theme.colors.alert + } else { + self.titleLabel.textColor = theme.colors.primaryContent + } + } + + func update(theme: Theme) { + self.theme = theme + self.backgroundColor = theme.colors.background + self.titleLabel.textColor = theme.colors.primaryContent + self.titleLabel.font = theme.fonts.body + self.selectionView.backgroundColor = theme.colors.separator + } +} diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.xib b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.xib new file mode 100644 index 000000000..f652ee4b7 --- /dev/null +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.xib @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift index 8f9710e56..9cb954398 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift @@ -114,6 +114,17 @@ class SpaceMenuViewController: UIViewController { } private func setupViews() { + setupTableView() + + if self.spaceId == SpaceListViewModel.Constants.homeSpaceId { + let avatarViewData = AvatarViewData(matrixItemId: self.spaceId, displayName: nil, avatarUrl: nil, mediaManager: session.mediaManager, fallbackImage: .image(Asset.Images.spaceHomeIcon.image, .center)) + self.avatarView.fill(with: avatarViewData) + self.titleLabel.text = VectorL10n.titleHome + self.subtitleLabel.text = VectorL10n.settingsTitle + + return + } + guard let space = self.session.spaceService.getSpace(withId: self.spaceId), let summary = space.summary else { MXLog.error("[SpaceMenuViewController] setupViews: no space found") return @@ -130,7 +141,6 @@ class SpaceMenuViewController: UIViewController { self.closeButton.layer.masksToBounds = true self.closeButton.layer.cornerRadius = self.closeButton.bounds.height / 2 - setupTableView() } private func setupTableView() { @@ -139,6 +149,7 @@ class SpaceMenuViewController: UIViewController { self.tableView.estimatedRowHeight = Constants.estimatedRowHeight self.tableView.allowsSelection = true self.tableView.register(cellType: SpaceMenuListViewCell.self) + self.tableView.register(cellType: SpaceMenuSwitchViewCell.self) self.tableView.tableFooterView = UIView() } @@ -150,6 +161,8 @@ class SpaceMenuViewController: UIViewController { self.renderLoaded() case .leaveOptions(let displayName, let isAdmin): self.renderLeaveOptions(displayName: displayName, isAdmin: isAdmin) + case .updateItem(let indexPath): + self.tableView.reloadRows(at: [indexPath], with: .fade) case .error(let error): self.render(error: error) case .deselect: @@ -235,12 +248,15 @@ extension SpaceMenuViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(for: indexPath, cellType: SpaceMenuListViewCell.self) - let viewData = viewModel.menuItems[indexPath.row] - cell.update(theme: self.theme) - cell.fill(with: viewData) + let cell = viewData.style == .boolean ? tableView.dequeueReusableCell(for: indexPath, cellType: SpaceMenuSwitchViewCell.self) : + tableView.dequeueReusableCell(for: indexPath, cellType: SpaceMenuListViewCell.self) + + if let cell = cell as? SpaceMenuCell { + cell.update(theme: self.theme) + cell.fill(with: viewData) + } return cell } diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift index be6ad2514..cd3c99f9c 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift @@ -22,6 +22,7 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { // MARK: - Enum enum ActionId: String { + case showAllRoomsInHome = "showAllRoomsInHome" case members = "members" case rooms = "rooms" case leave = "leave" @@ -32,12 +33,14 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { weak var coordinatorDelegate: SpaceMenuModelViewModelCoordinatorDelegate? weak var viewDelegate: SpaceMenuViewModelViewDelegate? - var menuItems: [SpaceMenuListItemViewData] = [ - SpaceMenuListItemViewData(actionId: ActionId.members.rawValue, style: .normal, title: VectorL10n.roomDetailsPeople, icon: UIImage(named: "space_menu_members")), - SpaceMenuListItemViewData(actionId: ActionId.rooms.rawValue, style: .normal, title: VectorL10n.spacesExploreRooms, icon: UIImage(named: "space_menu_rooms")), - SpaceMenuListItemViewData(actionId: ActionId.leave.rawValue, style: .destructive, title: VectorL10n.leave, icon: UIImage(named: "space_menu_leave")) + private let spaceMenuItems: [SpaceMenuListItemViewData] = [ + SpaceMenuListItemViewData(actionId: ActionId.members.rawValue, style: .normal, title: VectorL10n.roomDetailsPeople, icon: Asset.Images.spaceMenuMembers.image, value: nil), + SpaceMenuListItemViewData(actionId: ActionId.rooms.rawValue, style: .normal, title: VectorL10n.spacesExploreRooms, icon: Asset.Images.spaceMenuRooms.image, value: nil), + SpaceMenuListItemViewData(actionId: ActionId.leave.rawValue, style: .destructive, title: VectorL10n.leave, icon: Asset.Images.spaceMenuLeave.image, value: nil) ] + var menuItems: [SpaceMenuListItemViewData] = [] + private let session: MXSession private let spaceId: String @@ -46,6 +49,14 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { init(session: MXSession, spaceId: String) { self.session = session self.spaceId = spaceId + + if spaceId != SpaceListViewModel.Constants.homeSpaceId { + self.menuItems = spaceMenuItems + } else { + self.menuItems = [ + SpaceMenuListItemViewData(actionId: ActionId.showAllRoomsInHome.rawValue, style: .boolean, title: VectorL10n.spaceHomeShowAllRooms, icon: nil, value: MXKAppSettings.standard().isShowAllRoomsInHomeEnabled) + ] + } } // MARK: - Public @@ -55,7 +66,7 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { case .dismiss: self.coordinatorDelegate?.spaceMenuViewModelDidDismiss(self) case .selectRow(at: let indexPath): - self.processAction(with: menuItems[indexPath.row].actionId) + self.processAction(with: menuItems[indexPath.row].actionId, at: indexPath) case .leaveSpaceAndKeepRooms: self.leaveSpaceAndKeepRooms() case .leaveSpaceAndLeaveRooms: @@ -65,9 +76,14 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { // MARK: - Private - private func processAction(with actionStringId: String) { + private func processAction(with actionStringId: String, at indexPath: IndexPath) { let actionId = ActionId(rawValue: actionStringId) switch actionId { + case .showAllRoomsInHome: + MXKAppSettings.standard().isShowAllRoomsInHomeEnabled = !MXKAppSettings.standard().isShowAllRoomsInHomeEnabled + self.menuItems[indexPath.row].value = MXKAppSettings.standard().isShowAllRoomsInHomeEnabled + self.viewDelegate?.spaceMenuViewModel(self, didUpdateViewState: .deselect) + self.viewDelegate?.spaceMenuViewModel(self, didUpdateViewState: .updateItem(indexPath)) case .leave: self.leaveSpace() default: diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewState.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewState.swift index 9801ffaf5..0e37aad97 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewState.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewState.swift @@ -21,6 +21,7 @@ enum SpaceMenuViewState { case loading case loaded case deselect + case updateItem(_ indexPath: IndexPath) case leaveOptions(_ displayName: String, _ isAdmin: Bool) case error(Error) } From 2c6c81d61c10acdd667b917107a08b4887ae8603 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 11 Oct 2021 09:52:21 +0200 Subject: [PATCH 2/6] [Spaces] M10.4.1 Home space data filtering #4570 - Improved visual feedback when switching home settings --- .../SpaceMenu/SpaceMenuListItemViewData.swift | 23 +++++++++++++++++-- .../SpaceMenu/SpaceMenuSwitchViewCell.swift | 9 ++++++++ .../SpaceMenu/SpaceMenuViewController.swift | 2 -- .../Spaces/SpaceMenu/SpaceMenuViewModel.swift | 1 - .../Spaces/SpaceMenu/SpaceMenuViewState.swift | 1 - 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift index ca555e580..18a1f29bd 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift @@ -23,11 +23,30 @@ enum SpaceMenuListItemStyle { case destructive } +/// `SpaceMenuListItemViewDataDelegate` allows the table view cell to update its view accordingly with it's related data change +protocol SpaceMenuListItemViewDataDelegate: AnyObject { + func spaceMenuItemValueDidChange(_ item: SpaceMenuListItemViewData) +} + /// `SpaceMenuListViewCell` view data -struct SpaceMenuListItemViewData { +class SpaceMenuListItemViewData { let actionId: String let style: SpaceMenuListItemStyle let title: String? let icon: UIImage? - var value: Any? + + var value: Any? { + didSet { + delegate?.spaceMenuItemValueDidChange(self) + } + } + weak var delegate: SpaceMenuListItemViewDataDelegate? + + init(actionId: String, style: SpaceMenuListItemStyle, title: String?, icon: UIImage?, value: Any?) { + self.actionId = actionId + self.style = style + self.title = title + self.icon = icon + self.value = value + } } diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift index 6cd7a7a39..3887f668f 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift @@ -62,6 +62,8 @@ class SpaceMenuSwitchViewCell: UITableViewCell, SpaceMenuCell, NibReusable { } else { self.titleLabel.textColor = theme.colors.primaryContent } + + viewData.delegate = self } func update(theme: Theme) { @@ -72,3 +74,10 @@ class SpaceMenuSwitchViewCell: UITableViewCell, SpaceMenuCell, NibReusable { self.selectionView.backgroundColor = theme.colors.separator } } + +// MARK: - SpaceMenuListItemViewDataDelegate +extension SpaceMenuSwitchViewCell: SpaceMenuListItemViewDataDelegate { + func spaceMenuItemValueDidChange(_ item: SpaceMenuListItemViewData) { + self.switchView.setOn((item.value as? Bool) ?? false, animated: true) + } +} diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift index 9cb954398..83ab85bdf 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift @@ -161,8 +161,6 @@ class SpaceMenuViewController: UIViewController { self.renderLoaded() case .leaveOptions(let displayName, let isAdmin): self.renderLeaveOptions(displayName: displayName, isAdmin: isAdmin) - case .updateItem(let indexPath): - self.tableView.reloadRows(at: [indexPath], with: .fade) case .error(let error): self.render(error: error) case .deselect: diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift index cd3c99f9c..4d14eed4d 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift @@ -83,7 +83,6 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { MXKAppSettings.standard().isShowAllRoomsInHomeEnabled = !MXKAppSettings.standard().isShowAllRoomsInHomeEnabled self.menuItems[indexPath.row].value = MXKAppSettings.standard().isShowAllRoomsInHomeEnabled self.viewDelegate?.spaceMenuViewModel(self, didUpdateViewState: .deselect) - self.viewDelegate?.spaceMenuViewModel(self, didUpdateViewState: .updateItem(indexPath)) case .leave: self.leaveSpace() default: diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewState.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewState.swift index 0e37aad97..9801ffaf5 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewState.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewState.swift @@ -21,7 +21,6 @@ enum SpaceMenuViewState { case loading case loaded case deselect - case updateItem(_ indexPath: IndexPath) case leaveOptions(_ displayName: String, _ isAdmin: Bool) case error(Error) } From b159652f9842d77fa279871960f0b8900ee5af89 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 11 Oct 2021 10:29:27 +0200 Subject: [PATCH 3/6] Added changelogs --- changelog.d/4570.feature | 1 + changelog.d/4886.bugfix | 1 + 2 files changed, 2 insertions(+) create mode 100644 changelog.d/4570.feature create mode 100644 changelog.d/4886.bugfix diff --git a/changelog.d/4570.feature b/changelog.d/4570.feature new file mode 100644 index 000000000..e1a38944f --- /dev/null +++ b/changelog.d/4570.feature @@ -0,0 +1 @@ +M10.4.1 Home space data filtering \ No newline at end of file diff --git a/changelog.d/4886.bugfix b/changelog.d/4886.bugfix new file mode 100644 index 000000000..ad58b6c36 --- /dev/null +++ b/changelog.d/4886.bugfix @@ -0,0 +1 @@ +Fixed private space invite should use lock icon instead of planet \ No newline at end of file From 9c2531c59a294a5b736ef25254b259aa744a8233 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 18 Oct 2021 15:01:11 +0200 Subject: [PATCH 4/6] [Spaces] M10.4.1 Home space data filtering #4570 - update after review --- .../Spaces/SpaceMenu/SpaceMenuCell.swift | 2 +- .../SpaceMenu/SpaceMenuListItemViewData.swift | 14 +++++++-- .../SpaceMenu/SpaceMenuListViewCell.swift | 2 +- .../Spaces/SpaceMenu/SpaceMenuPresenter.swift | 11 ++++--- .../SpaceMenu/SpaceMenuSwitchViewCell.swift | 12 +------- .../SpaceMenu/SpaceMenuViewController.swift | 2 +- .../Spaces/SpaceMenu/SpaceMenuViewModel.swift | 30 +++++++------------ .../SpaceMenu/SpaceMenuViewModelType.swift | 2 +- 8 files changed, 31 insertions(+), 44 deletions(-) diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuCell.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuCell.swift index 2447ae918..d7a3d1fe0 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuCell.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuCell.swift @@ -17,5 +17,5 @@ import Foundation protocol SpaceMenuCell: Themable { - func fill(with viewData: SpaceMenuListItemViewData) + func update(with viewData: SpaceMenuListItemViewData) } diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift index 18a1f29bd..bae8bf13f 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift @@ -16,6 +16,14 @@ import Foundation +/// Possible action ID related to a `SpaceMenuListViewCell` view data +enum SpaceMenuListItemActionId { + case showAllRoomsInHomeSpace + case exploreSpaceMembers + case exploreSpaceRooms + case leaveSpace +} + /// Style of the `SpaceMenuListViewCell` enum SpaceMenuListItemStyle { case normal @@ -30,11 +38,11 @@ protocol SpaceMenuListItemViewDataDelegate: AnyObject { /// `SpaceMenuListViewCell` view data class SpaceMenuListItemViewData { - let actionId: String + let actionId: SpaceMenuListItemActionId let style: SpaceMenuListItemStyle let title: String? let icon: UIImage? - + /// Any value related to the type of data (e.g. `Bool` for `boolean` style, `nil` for `normal` and `destructive` style) var value: Any? { didSet { delegate?.spaceMenuItemValueDidChange(self) @@ -42,7 +50,7 @@ class SpaceMenuListItemViewData { } weak var delegate: SpaceMenuListItemViewDataDelegate? - init(actionId: String, style: SpaceMenuListItemStyle, title: String?, icon: UIImage?, value: Any?) { + init(actionId: SpaceMenuListItemActionId, style: SpaceMenuListItemStyle, title: String?, icon: UIImage?, value: Any?) { self.actionId = actionId self.style = style self.title = title diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListViewCell.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListViewCell.swift index 86bfe8805..bc8243a9a 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListViewCell.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListViewCell.swift @@ -49,7 +49,7 @@ class SpaceMenuListViewCell: UITableViewCell, SpaceMenuCell, NibReusable { // MARK: - Public - func fill(with viewData: SpaceMenuListItemViewData) { + func update(with viewData: SpaceMenuListItemViewData) { self.iconView.image = viewData.icon self.titleLabel.text = viewData.title diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift index 697c65b43..dfc755675 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift @@ -101,13 +101,12 @@ extension SpaceMenuPresenter: SpaceMenuModelViewModelCoordinatorDelegate { self.dismiss(animated: true, completion: nil) } - func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didSelectItemWithId itemId: String) { - let actionId = SpaceMenuViewModel.ActionId(rawValue: itemId) - switch actionId { - case .leave: break - case .members: + func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didSelectItemWithId itemId: SpaceMenuListItemActionId) { + switch itemId { + case .leaveSpace: break + case .exploreSpaceMembers: self.delegate?.spaceMenuPresenter(self, didCompleteWith: .exploreMembers, forSpaceWithId: self.spaceId, with: self.session) - case .rooms: + case .exploreSpaceRooms: self.delegate?.spaceMenuPresenter(self, didCompleteWith: .exploreRooms, forSpaceWithId: self.spaceId, with: self.session) default: MXLog.error("[SpaceMenuPresenter] spaceListViewModel didSelectItemWithId: invalid itemId \(itemId)") diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift index 3887f668f..2d50e9df1 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuSwitchViewCell.swift @@ -49,20 +49,10 @@ class SpaceMenuSwitchViewCell: UITableViewCell, SpaceMenuCell, NibReusable { // MARK: - Public - func fill(with viewData: SpaceMenuListItemViewData) { + func update(with viewData: SpaceMenuListItemViewData) { self.titleLabel.text = viewData.title self.switchView.isOn = (viewData.value as? Bool) ?? false - guard let theme = self.theme else { - return - } - - if viewData.style == .destructive { - self.titleLabel.textColor = theme.colors.alert - } else { - self.titleLabel.textColor = theme.colors.primaryContent - } - viewData.delegate = self } diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift index 83ab85bdf..32bc9a76f 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift @@ -253,7 +253,7 @@ extension SpaceMenuViewController: UITableViewDataSource { if let cell = cell as? SpaceMenuCell { cell.update(theme: self.theme) - cell.fill(with: viewData) + cell.update(with: viewData) } return cell diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift index 4d14eed4d..1150ff155 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift @@ -19,24 +19,15 @@ import Foundation /// View model used by `SpaceMenuViewController` class SpaceMenuViewModel: SpaceMenuViewModelType { - // MARK: - Enum - - enum ActionId: String { - case showAllRoomsInHome = "showAllRoomsInHome" - case members = "members" - case rooms = "rooms" - case leave = "leave" - } - // MARK: - Properties weak var coordinatorDelegate: SpaceMenuModelViewModelCoordinatorDelegate? weak var viewDelegate: SpaceMenuViewModelViewDelegate? private let spaceMenuItems: [SpaceMenuListItemViewData] = [ - SpaceMenuListItemViewData(actionId: ActionId.members.rawValue, style: .normal, title: VectorL10n.roomDetailsPeople, icon: Asset.Images.spaceMenuMembers.image, value: nil), - SpaceMenuListItemViewData(actionId: ActionId.rooms.rawValue, style: .normal, title: VectorL10n.spacesExploreRooms, icon: Asset.Images.spaceMenuRooms.image, value: nil), - SpaceMenuListItemViewData(actionId: ActionId.leave.rawValue, style: .destructive, title: VectorL10n.leave, icon: Asset.Images.spaceMenuLeave.image, value: nil) + SpaceMenuListItemViewData(actionId: .exploreSpaceMembers, style: .normal, title: VectorL10n.roomDetailsPeople, icon: Asset.Images.spaceMenuMembers.image, value: nil), + SpaceMenuListItemViewData(actionId: .exploreSpaceRooms, style: .normal, title: VectorL10n.spacesExploreRooms, icon: Asset.Images.spaceMenuRooms.image, value: nil), + SpaceMenuListItemViewData(actionId: .leaveSpace, style: .destructive, title: VectorL10n.leave, icon: Asset.Images.spaceMenuLeave.image, value: nil) ] var menuItems: [SpaceMenuListItemViewData] = [] @@ -54,7 +45,7 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { self.menuItems = spaceMenuItems } else { self.menuItems = [ - SpaceMenuListItemViewData(actionId: ActionId.showAllRoomsInHome.rawValue, style: .boolean, title: VectorL10n.spaceHomeShowAllRooms, icon: nil, value: MXKAppSettings.standard().isShowAllRoomsInHomeEnabled) + SpaceMenuListItemViewData(actionId: .showAllRoomsInHomeSpace, style: .boolean, title: VectorL10n.spaceHomeShowAllRooms, icon: nil, value: MXKAppSettings.standard().showAllRoomsInHomeSpace) ] } } @@ -76,17 +67,16 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { // MARK: - Private - private func processAction(with actionStringId: String, at indexPath: IndexPath) { - let actionId = ActionId(rawValue: actionStringId) + private func processAction(with actionId: SpaceMenuListItemActionId, at indexPath: IndexPath) { switch actionId { - case .showAllRoomsInHome: - MXKAppSettings.standard().isShowAllRoomsInHomeEnabled = !MXKAppSettings.standard().isShowAllRoomsInHomeEnabled - self.menuItems[indexPath.row].value = MXKAppSettings.standard().isShowAllRoomsInHomeEnabled + case .showAllRoomsInHomeSpace: + MXKAppSettings.standard().showAllRoomsInHomeSpace = !MXKAppSettings.standard().showAllRoomsInHomeSpace + self.menuItems[indexPath.row].value = MXKAppSettings.standard().showAllRoomsInHomeSpace self.viewDelegate?.spaceMenuViewModel(self, didUpdateViewState: .deselect) - case .leave: + case .leaveSpace: self.leaveSpace() default: - self.coordinatorDelegate?.spaceMenuViewModel(self, didSelectItemWithId: actionStringId) + self.coordinatorDelegate?.spaceMenuViewModel(self, didSelectItemWithId: actionId) } } diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModelType.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModelType.swift index edaef39b3..dd55ebb26 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModelType.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModelType.swift @@ -22,7 +22,7 @@ protocol SpaceMenuViewModelViewDelegate: AnyObject { protocol SpaceMenuModelViewModelCoordinatorDelegate: AnyObject { func spaceMenuViewModelDidDismiss(_ viewModel: SpaceMenuViewModelType) - func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didSelectItemWithId itemId: String) + func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didSelectItemWithId itemId: SpaceMenuListItemActionId) } /// Protocol describing the view model used by `SpaceMenuViewController` From 22ee3d03edbc82f04c3e81d7576572744f7f3caa Mon Sep 17 00:00:00 2001 From: Doug Date: Mon, 18 Oct 2021 11:52:31 +0100 Subject: [PATCH 5/6] Allow images to be pasted from Safari rather than their URL. Use correct type. --- .../RoomInputToolbarTextView.swift | 22 ++++++++++++++++++- .../Views/InputToolbar/RoomInputToolbarView.m | 19 +++++++++++++++- changelog.d/2076.bugfix | 1 + 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 changelog.d/2076.bugfix diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarTextView.swift b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarTextView.swift index ad769b52e..729b3c2e7 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarTextView.swift +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarTextView.swift @@ -16,8 +16,14 @@ import GrowingTextView +@objc protocol RoomInputToolbarTextViewDelegate: AnyObject { + func textView(_ textView: RoomInputToolbarTextView, didReceivePasteForMediaFromSender sender: Any?) +} + class RoomInputToolbarTextView: GrowingTextView { + @objc weak var toolbarDelegate: RoomInputToolbarTextViewDelegate? + override var keyCommands: [UIKeyCommand]? { return [UIKeyCommand(input: "\r", modifierFlags: [], action: #selector(keyCommandSelector(_:)))] } @@ -28,5 +34,19 @@ class RoomInputToolbarTextView: GrowingTextView { } delegate.onTouchUp(inside: delegate.rightInputToolbarButton) - } + } + + /// Overrides paste to handle images pasted from Safari, passing them up to the input toolbar. + /// This is required as the pasteboard contains both the image and the image's URL, with the + /// default implementation choosing to paste the URL and completely ignore the image data. + override func paste(_ sender: Any?) { + let pasteboard = MXKPasteboardManager.shared.pasteboard + let types = pasteboard.types.map { UTI(rawValue: $0) } + + if types.contains(where: { $0.conforms(to: .image) }) { + toolbarDelegate?.textView(self, didReceivePasteForMediaFromSender: sender) + } else { + super.paste(sender) + } + } } diff --git a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m index a8c9f57e7..8cc889940 100644 --- a/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m +++ b/Riot/Modules/Room/Views/InputToolbar/RoomInputToolbarView.m @@ -38,7 +38,7 @@ const NSTimeInterval kActionMenuContentAlphaAnimationDuration = .2; const NSTimeInterval kActionMenuComposerHeightAnimationDuration = .3; const CGFloat kComposerContainerTrailingPadding = 12; -@interface RoomInputToolbarView() +@interface RoomInputToolbarView() { // The intermediate action sheet UIAlertController *actionSheet; @@ -83,6 +83,8 @@ const CGFloat kComposerContainerTrailingPadding = 12; self.isEncryptionEnabled = _isEncryptionEnabled; [self updateUIWithTextMessage:nil animated:NO]; + + self.textView.toolbarDelegate = self; } - (void)setVoiceMessageToolbarView:(UIView *)voiceMessageToolbarView @@ -382,6 +384,16 @@ const CGFloat kComposerContainerTrailingPadding = 12; [super onTouchUpInside:button]; } +- (BOOL)becomeFirstResponder +{ + return [self.textView becomeFirstResponder]; +} + +- (void)dismissKeyboard +{ + [self.textView resignFirstResponder]; +} + - (void)destroy { if (actionSheet) @@ -455,6 +467,11 @@ const CGFloat kComposerContainerTrailingPadding = 12; [super paste:sender]; } +- (void)textView:(GrowingTextView *)textView didReceivePasteForMediaFromSender:(id)sender +{ + [self paste:sender]; +} + #pragma mark - Private - (void)updateUIWithTextMessage:(NSString *)textMessage animated:(BOOL)animated diff --git a/changelog.d/2076.bugfix b/changelog.d/2076.bugfix new file mode 100644 index 000000000..e85ae76ce --- /dev/null +++ b/changelog.d/2076.bugfix @@ -0,0 +1 @@ +Message Composer: Pasting images from Safari now pastes the image and not its URL. \ No newline at end of file From 764c353913aae8e636ca4021c4fdc33c6ee887b9 Mon Sep 17 00:00:00 2001 From: Gil Eluard Date: Mon, 18 Oct 2021 16:44:55 +0200 Subject: [PATCH 6/6] [Spaces] M10.4.1 Home space data filtering #4570 - update after review --- .../SpaceMenu/SpaceMenuListItemViewData.swift | 12 ++++++------ .../Spaces/SpaceMenu/SpaceMenuPresenter.swift | 6 +++--- .../SpaceMenu/SpaceMenuViewController.swift | 2 +- .../Spaces/SpaceMenu/SpaceMenuViewModel.swift | 16 ++++++++-------- .../SpaceMenu/SpaceMenuViewModelType.swift | 2 +- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift index bae8bf13f..6f8b7378f 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuListItemViewData.swift @@ -16,8 +16,8 @@ import Foundation -/// Possible action ID related to a `SpaceMenuListViewCell` view data -enum SpaceMenuListItemActionId { +/// Possible action related to a `SpaceMenuListViewCell` view data +enum SpaceMenuListItemAction { case showAllRoomsInHomeSpace case exploreSpaceMembers case exploreSpaceRooms @@ -27,7 +27,7 @@ enum SpaceMenuListItemActionId { /// Style of the `SpaceMenuListViewCell` enum SpaceMenuListItemStyle { case normal - case boolean + case toggle case destructive } @@ -38,7 +38,7 @@ protocol SpaceMenuListItemViewDataDelegate: AnyObject { /// `SpaceMenuListViewCell` view data class SpaceMenuListItemViewData { - let actionId: SpaceMenuListItemActionId + let action: SpaceMenuListItemAction let style: SpaceMenuListItemStyle let title: String? let icon: UIImage? @@ -50,8 +50,8 @@ class SpaceMenuListItemViewData { } weak var delegate: SpaceMenuListItemViewDataDelegate? - init(actionId: SpaceMenuListItemActionId, style: SpaceMenuListItemStyle, title: String?, icon: UIImage?, value: Any?) { - self.actionId = actionId + init(action: SpaceMenuListItemAction, style: SpaceMenuListItemStyle, title: String?, icon: UIImage?, value: Any?) { + self.action = action self.style = style self.title = title self.icon = icon diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift index dfc755675..1949e03fd 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuPresenter.swift @@ -101,15 +101,15 @@ extension SpaceMenuPresenter: SpaceMenuModelViewModelCoordinatorDelegate { self.dismiss(animated: true, completion: nil) } - func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didSelectItemWithId itemId: SpaceMenuListItemActionId) { - switch itemId { + func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didSelectItemWith action: SpaceMenuListItemAction) { + switch action { case .leaveSpace: break case .exploreSpaceMembers: self.delegate?.spaceMenuPresenter(self, didCompleteWith: .exploreMembers, forSpaceWithId: self.spaceId, with: self.session) case .exploreSpaceRooms: self.delegate?.spaceMenuPresenter(self, didCompleteWith: .exploreRooms, forSpaceWithId: self.spaceId, with: self.session) default: - MXLog.error("[SpaceMenuPresenter] spaceListViewModel didSelectItemWithId: invalid itemId \(itemId)") + MXLog.error("[SpaceMenuPresenter] spaceListViewModel didSelectItem: invalid action \(action)") } } } diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift index 32bc9a76f..7b9bebea4 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewController.swift @@ -248,7 +248,7 @@ extension SpaceMenuViewController: UITableViewDataSource { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let viewData = viewModel.menuItems[indexPath.row] - let cell = viewData.style == .boolean ? tableView.dequeueReusableCell(for: indexPath, cellType: SpaceMenuSwitchViewCell.self) : + let cell = viewData.style == .toggle ? tableView.dequeueReusableCell(for: indexPath, cellType: SpaceMenuSwitchViewCell.self) : tableView.dequeueReusableCell(for: indexPath, cellType: SpaceMenuListViewCell.self) if let cell = cell as? SpaceMenuCell { diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift index 1150ff155..39e38934d 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModel.swift @@ -25,9 +25,9 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { weak var viewDelegate: SpaceMenuViewModelViewDelegate? private let spaceMenuItems: [SpaceMenuListItemViewData] = [ - SpaceMenuListItemViewData(actionId: .exploreSpaceMembers, style: .normal, title: VectorL10n.roomDetailsPeople, icon: Asset.Images.spaceMenuMembers.image, value: nil), - SpaceMenuListItemViewData(actionId: .exploreSpaceRooms, style: .normal, title: VectorL10n.spacesExploreRooms, icon: Asset.Images.spaceMenuRooms.image, value: nil), - SpaceMenuListItemViewData(actionId: .leaveSpace, style: .destructive, title: VectorL10n.leave, icon: Asset.Images.spaceMenuLeave.image, value: nil) + SpaceMenuListItemViewData(action: .exploreSpaceMembers, style: .normal, title: VectorL10n.roomDetailsPeople, icon: Asset.Images.spaceMenuMembers.image, value: nil), + SpaceMenuListItemViewData(action: .exploreSpaceRooms, style: .normal, title: VectorL10n.spacesExploreRooms, icon: Asset.Images.spaceMenuRooms.image, value: nil), + SpaceMenuListItemViewData(action: .leaveSpace, style: .destructive, title: VectorL10n.leave, icon: Asset.Images.spaceMenuLeave.image, value: nil) ] var menuItems: [SpaceMenuListItemViewData] = [] @@ -45,7 +45,7 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { self.menuItems = spaceMenuItems } else { self.menuItems = [ - SpaceMenuListItemViewData(actionId: .showAllRoomsInHomeSpace, style: .boolean, title: VectorL10n.spaceHomeShowAllRooms, icon: nil, value: MXKAppSettings.standard().showAllRoomsInHomeSpace) + SpaceMenuListItemViewData(action: .showAllRoomsInHomeSpace, style: .toggle, title: VectorL10n.spaceHomeShowAllRooms, icon: nil, value: MXKAppSettings.standard().showAllRoomsInHomeSpace) ] } } @@ -57,7 +57,7 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { case .dismiss: self.coordinatorDelegate?.spaceMenuViewModelDidDismiss(self) case .selectRow(at: let indexPath): - self.processAction(with: menuItems[indexPath.row].actionId, at: indexPath) + self.processAction(with: menuItems[indexPath.row].action, at: indexPath) case .leaveSpaceAndKeepRooms: self.leaveSpaceAndKeepRooms() case .leaveSpaceAndLeaveRooms: @@ -67,8 +67,8 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { // MARK: - Private - private func processAction(with actionId: SpaceMenuListItemActionId, at indexPath: IndexPath) { - switch actionId { + private func processAction(with action: SpaceMenuListItemAction, at indexPath: IndexPath) { + switch action { case .showAllRoomsInHomeSpace: MXKAppSettings.standard().showAllRoomsInHomeSpace = !MXKAppSettings.standard().showAllRoomsInHomeSpace self.menuItems[indexPath.row].value = MXKAppSettings.standard().showAllRoomsInHomeSpace @@ -76,7 +76,7 @@ class SpaceMenuViewModel: SpaceMenuViewModelType { case .leaveSpace: self.leaveSpace() default: - self.coordinatorDelegate?.spaceMenuViewModel(self, didSelectItemWithId: actionId) + self.coordinatorDelegate?.spaceMenuViewModel(self, didSelectItemWith: action) } } diff --git a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModelType.swift b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModelType.swift index dd55ebb26..7f9511f7b 100644 --- a/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModelType.swift +++ b/Riot/Modules/Spaces/SpaceMenu/SpaceMenuViewModelType.swift @@ -22,7 +22,7 @@ protocol SpaceMenuViewModelViewDelegate: AnyObject { protocol SpaceMenuModelViewModelCoordinatorDelegate: AnyObject { func spaceMenuViewModelDidDismiss(_ viewModel: SpaceMenuViewModelType) - func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didSelectItemWithId itemId: SpaceMenuListItemActionId) + func spaceMenuViewModel(_ viewModel: SpaceMenuViewModelType, didSelectItemWith action: SpaceMenuListItemAction) } /// Protocol describing the view model used by `SpaceMenuViewController`