Merge branch 'develop' into ismail/5878_threads_design_tweaks

This commit is contained in:
ismailgulek 2022-04-05 13:02:19 +03:00
commit 028861d28c
No known key found for this signature in database
GPG key ID: E96336D42D9470A9
10 changed files with 582 additions and 3 deletions

View file

@ -479,6 +479,11 @@ Tap the + to start adding people.";
"threads_notice_title" = "Threads no longer experimental 🎉";
"threads_notice_information" = "All threads created during the experimental period will now be <b>rendered as regular replies</b>.<br/><br/>This will be a one-off transition, as threads are now part of the Matrix specification.";
"threads_notice_done" = "Got it";
"threads_beta_title" = "Threads";
"threads_beta_information" = "Keep discussions organised with threads.\n\nThreads help keep your conversations on-topic and easy to track. ";
"threads_beta_information_link" = "Learn more";
"threads_beta_enable" = "Try it out";
"threads_beta_cancel" = "Not now";
"media_type_accessibility_image" = "Image";
"media_type_accessibility_audio" = "Audio";

View file

@ -294,6 +294,11 @@ internal enum StoryboardScene {
internal static let initialScene = InitialSceneType<Riot.ThreadListViewController>(storyboard: ThreadListViewController.self)
}
internal enum ThreadsBetaViewController: StoryboardType {
internal static let storyboardName = "ThreadsBetaViewController"
internal static let initialScene = InitialSceneType<Riot.ThreadsBetaViewController>(storyboard: ThreadsBetaViewController.self)
}
internal enum ThreadsNoticeViewController: StoryboardType {
internal static let storyboardName = "ThreadsNoticeViewController"

View file

@ -7591,6 +7591,26 @@ public class VectorL10n: NSObject {
public static var threadsActionMyThreads: String {
return VectorL10n.tr("Vector", "threads_action_my_threads")
}
/// Not now
public static var threadsBetaCancel: String {
return VectorL10n.tr("Vector", "threads_beta_cancel")
}
/// Try it out
public static var threadsBetaEnable: String {
return VectorL10n.tr("Vector", "threads_beta_enable")
}
/// Keep discussions organised with threads.\n\nThreads help keep your conversations on-topic and easy to track.
public static var threadsBetaInformation: String {
return VectorL10n.tr("Vector", "threads_beta_information")
}
/// Learn more
public static var threadsBetaInformationLink: String {
return VectorL10n.tr("Vector", "threads_beta_information_link")
}
/// Threads
public static var threadsBetaTitle: String {
return VectorL10n.tr("Vector", "threads_beta_title")
}
/// Threads help keep your conversations on-topic and easy to track.
public static var threadsEmptyInfoAll: String {
return VectorL10n.tr("Vector", "threads_empty_info_all")

View file

@ -97,7 +97,7 @@ static CGSize kThreadListBarButtonItemImageSize;
@interface RoomViewController () <UISearchBarDelegate, UIGestureRecognizerDelegate, UIScrollViewAccessibilityDelegate, RoomTitleViewTapGestureDelegate, MXKRoomMemberDetailsViewControllerDelegate, ContactsTableViewControllerDelegate, MXServerNoticesDelegate, RoomContextualMenuViewControllerDelegate,
ReactionsMenuViewModelCoordinatorDelegate, EditHistoryCoordinatorBridgePresenterDelegate, MXKDocumentPickerPresenterDelegate, EmojiPickerCoordinatorBridgePresenterDelegate,
ReactionHistoryCoordinatorBridgePresenterDelegate, CameraPresenterDelegate, MediaPickerCoordinatorBridgePresenterDelegate,
RoomDataSourceDelegate, RoomCreationModalCoordinatorBridgePresenterDelegate, RoomInfoCoordinatorBridgePresenterDelegate, DialpadViewControllerDelegate, RemoveJitsiWidgetViewDelegate, VoiceMessageControllerDelegate, SpaceDetailPresenterDelegate, UserSuggestionCoordinatorBridgeDelegate, ThreadsCoordinatorBridgePresenterDelegate, MXThreadingServiceDelegate, RoomParticipantsInviteCoordinatorBridgePresenterDelegate>
RoomDataSourceDelegate, RoomCreationModalCoordinatorBridgePresenterDelegate, RoomInfoCoordinatorBridgePresenterDelegate, DialpadViewControllerDelegate, RemoveJitsiWidgetViewDelegate, VoiceMessageControllerDelegate, SpaceDetailPresenterDelegate, UserSuggestionCoordinatorBridgeDelegate, ThreadsCoordinatorBridgePresenterDelegate, ThreadsBetaCoordinatorBridgePresenterDelegate, MXThreadingServiceDelegate, RoomParticipantsInviteCoordinatorBridgePresenterDelegate>
{
// The preview header
@ -210,6 +210,7 @@ static CGSize kThreadListBarButtonItemImageSize;
@property (nonatomic, strong) CustomSizedPresentationController *customSizedPresentationController;
@property (nonatomic, strong) RoomParticipantsInviteCoordinatorBridgePresenter *participantsInvitePresenter;
@property (nonatomic, strong) ThreadsCoordinatorBridgePresenter *threadsBridgePresenter;
@property (nonatomic, strong) ThreadsBetaCoordinatorBridgePresenter *threadsBetaBridgePresenter;
@property (nonatomic, strong) SlidingModalPresenter *threadsNoticeModalPresenter;
@property (nonatomic, getter=isActivitiesViewExpanded) BOOL activitiesViewExpanded;
@property (nonatomic, getter=isScrollToBottomHidden) BOOL scrollToBottomHidden;
@ -6405,7 +6406,7 @@ static CGSize kThreadListBarButtonItemImageSize;
BOOL showMoreOption = (event.isState && RiotSettings.shared.roomContextualMenuShowMoreOptionForStates)
|| (!event.isState && RiotSettings.shared.roomContextualMenuShowMoreOptionForMessages);
BOOL showThreadOption = RiotSettings.shared.enableThreads && !self.roomDataSource.threadId && !event.threadId;
BOOL showThreadOption = !self.roomDataSource.threadId && !event.threadId;
NSMutableArray<RoomContextualMenuItem*> *items = [NSMutableArray arrayWithCapacity:5];
@ -6767,7 +6768,14 @@ static CGSize kThreadListBarButtonItemImageSize;
[self hideContextualMenuAnimated:YES cancelEventSelection:NO completion:nil];
[self openThreadWithId:event.eventId];
if (RiotSettings.shared.enableThreads)
{
[self openThreadWithId:event.eventId];
}
else
{
[self showThreadsBetaForEvent:event];
}
};
return item;
@ -6818,6 +6826,20 @@ static CGSize kThreadListBarButtonItemImageSize;
completion:nil];
}
- (void)showThreadsBetaForEvent:(MXEvent *)event
{
if (self.threadsBetaBridgePresenter)
{
[self.threadsBetaBridgePresenter dismissWithAnimated:YES completion:nil];
self.threadsBetaBridgePresenter = nil;
}
self.threadsBetaBridgePresenter = [[ThreadsBetaCoordinatorBridgePresenter alloc] initWithThreadId:event.eventId];
self.threadsBetaBridgePresenter.delegate = self;
[self.threadsBetaBridgePresenter presentFrom:self.presentedViewController?:self animated:YES];
}
- (void)openThreadWithId:(NSString *)threadId
{
if (self.threadsBridgePresenter)
@ -7416,6 +7438,28 @@ static CGSize kThreadListBarButtonItemImageSize;
self.threadsBridgePresenter = nil;
}
#pragma mark - ThreadsBetaCoordinatorBridgePresenterDelegate
- (void)threadsBetaCoordinatorBridgePresenterDelegateDidTapEnable:(ThreadsBetaCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
MXWeakify(self);
[self.threadsBetaBridgePresenter dismissWithAnimated:YES completion:^{
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
[self.roomDataSource reload];
[self openThreadWithId:coordinatorBridgePresenter.threadId];
}];
}
- (void)threadsBetaCoordinatorBridgePresenterDelegateDidTapCancel:(ThreadsBetaCoordinatorBridgePresenter *)coordinatorBridgePresenter
{
MXWeakify(self);
[self.threadsBetaBridgePresenter dismissWithAnimated:YES completion:^{
MXStrongifyAndReturnIfNil(self);
[self cancelEventSelection];
}];
}
#pragma mark - MXThreadingServiceDelegate
- (void)threadingServiceDidUpdateThreads:(MXThreadingService *)service

View file

@ -0,0 +1,66 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Threads ThreadsBeta
/*
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 UIKit
@objcMembers
final class ThreadsBetaCoordinator: NSObject, ThreadsBetaCoordinatorProtocol {
// MARK: - Properties
// MARK: Private
private let threadId: String
private lazy var viewController: ThreadsBetaViewController = {
let result = ThreadsBetaViewController.instantiate()
result.didTapEnableButton = { [weak self] in
guard let self = self else { return }
RiotSettings.shared.enableThreads = true
MXSDKOptions.sharedInstance().enableThreads = true
self.delegate?.threadsBetaCoordinatorDidTapEnable(self)
}
result.didTapCancelButton = { [weak self] in
guard let self = self else { return }
self.delegate?.threadsBetaCoordinatorDidTapCancel(self)
}
return result
}()
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
weak var delegate: ThreadsBetaCoordinatorDelegate?
// MARK: - Setup
init(threadId: String) {
self.threadId = threadId
}
// MARK: - Public
func start() {
// no-op. this is a static screen
}
func toPresentable() -> UIViewController {
return viewController
}
}

View file

@ -0,0 +1,89 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Threads ThreadsBeta
/*
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 MatrixSDK
@objc protocol ThreadsBetaCoordinatorBridgePresenterDelegate {
func threadsBetaCoordinatorBridgePresenterDelegateDidTapEnable(_ coordinatorBridgePresenter: ThreadsBetaCoordinatorBridgePresenter)
func threadsBetaCoordinatorBridgePresenterDelegateDidTapCancel(_ coordinatorBridgePresenter: ThreadsBetaCoordinatorBridgePresenter)
}
/// ThreadsBetaCoordinatorBridgePresenter enables to start ThreadsBetaCoordinator from a view controller.
/// This bridge is used while waiting for global usage of coordinator pattern.
/// **WARNING**: This class breaks the Coordinator abstraction and it has been introduced for **Objective-C compatibility only** (mainly for integration in legacy view controllers). Each bridge should be removed once the underlying Coordinator has been integrated by another Coordinator.
@objcMembers
final class ThreadsBetaCoordinatorBridgePresenter: NSObject {
// MARK: - Constants
// MARK: - Properties
// MARK: Private
public let threadId: String
private let slidingModalPresenter = SlidingModalPresenter()
private var coordinator: ThreadsBetaCoordinator?
// MARK: Public
weak var delegate: ThreadsBetaCoordinatorBridgePresenterDelegate?
// MARK: - Setup
init(threadId: String) {
self.threadId = threadId
super.init()
}
// MARK: - Public
func present(from viewController: UIViewController, animated: Bool) {
let threadsBetaCoordinator = ThreadsBetaCoordinator(threadId: threadId)
threadsBetaCoordinator.delegate = self
guard let presentable = threadsBetaCoordinator.toPresentable() as? SlidingModalPresentable.ViewControllerType else {
MXLog.error("[ThreadsBetaCoordinatorBridgePresenter] Presentable is not 'SlidingModalPresentable'")
return
}
slidingModalPresenter.present(presentable,
from: viewController,
animated: animated,
options: .spanning,
completion: nil)
threadsBetaCoordinator.start()
self.coordinator = threadsBetaCoordinator
}
func dismiss(animated: Bool, completion: (() -> Void)?) {
slidingModalPresenter.dismiss(animated: animated, completion: completion)
}
}
// MARK: - ThreadsBetaCoordinatorDelegate
extension ThreadsBetaCoordinatorBridgePresenter: ThreadsBetaCoordinatorDelegate {
func threadsBetaCoordinatorDidTapEnable(_ coordinator: ThreadsBetaCoordinatorProtocol) {
self.delegate?.threadsBetaCoordinatorBridgePresenterDelegateDidTapEnable(self)
}
func threadsBetaCoordinatorDidTapCancel(_ coordinator: ThreadsBetaCoordinatorProtocol) {
self.delegate?.threadsBetaCoordinatorBridgePresenterDelegateDidTapCancel(self)
}
}

View file

@ -0,0 +1,29 @@
// File created from FlowTemplate
// $ createRootCoordinator.sh Threads ThreadsBeta
/*
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 ThreadsBetaCoordinatorDelegate: AnyObject {
func threadsBetaCoordinatorDidTapEnable(_ coordinator: ThreadsBetaCoordinatorProtocol)
func threadsBetaCoordinatorDidTapCancel(_ coordinator: ThreadsBetaCoordinatorProtocol)
}
/// `ThreadsBetaCoordinatorProtocol` is a protocol describing a Coordinator that handle xxxxxxx navigation flow.
protocol ThreadsBetaCoordinatorProtocol: Coordinator, Presentable {
var delegate: ThreadsBetaCoordinatorDelegate? { get }
}

View file

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19529" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="Y6W-OH-hqX">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19519"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Threads Beta View Controller-->
<scene sceneID="s0d-6b-0kx">
<objects>
<viewController id="Y6W-OH-hqX" customClass="ThreadsBetaViewController" customModule="Riot" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="5EZ-qb-Rvc">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="rx5-uV-e8E">
<rect key="frame" x="20" y="68" width="374" height="774"/>
<subviews>
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" verticalHuggingPriority="251" text="Threads" textAlignment="center" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tre-1i-GOd">
<rect key="frame" x="0.0" y="0.0" width="374" height="20.5"/>
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="gEs-ZC-Pju">
<rect key="frame" x="0.0" y="20.5" width="374" height="20"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="20" id="xUG-Oh-eyc"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="T3e-J2-hJW">
<rect key="frame" x="0.0" y="40.5" width="374" height="1"/>
<color key="backgroundColor" systemColor="separatorColor"/>
<constraints>
<constraint firstAttribute="height" constant="1" id="cHN-fx-JTR"/>
</constraints>
</view>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="GX0-MS-yeP">
<rect key="frame" x="0.0" y="41.5" width="374" height="12"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="12" id="7C2-8P-K3W"/>
</constraints>
</view>
<textView clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleToFill" verticalCompressionResistancePriority="751" bounces="NO" scrollEnabled="NO" showsHorizontalScrollIndicator="NO" showsVerticalScrollIndicator="NO" contentInsetAdjustmentBehavior="never" bouncesZoom="NO" editable="NO" textAlignment="natural" translatesAutoresizingMaskIntoConstraints="NO" id="qbP-qu-o0V">
<rect key="frame" x="0.0" y="53.5" width="374" height="596.5"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<string key="text">Keep discussions organised with threads.
Threads help keep your conversations on-topic and easy to track. Learn more.</string>
<color key="textColor" systemColor="labelColor"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<textInputTraits key="textInputTraits" autocapitalizationType="sentences"/>
</textView>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Eln-NO-tcW">
<rect key="frame" x="0.0" y="650" width="374" height="24"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="24" id="qOQ-o8-vuD"/>
</constraints>
</view>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="Tmd-wH-3Z2">
<rect key="frame" x="0.0" y="674" width="374" height="50"/>
<color key="backgroundColor" red="0.01176470588" green="0.70196078429999997" blue="0.50588235290000005" alpha="1" colorSpace="calibratedRGB"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="PKT-BC-IIR"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Try it out">
<color key="titleColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="8"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="enableButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="XYF-57-jtn"/>
</connections>
</button>
<button opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="y5Q-lI-JUe">
<rect key="frame" x="0.0" y="724" width="374" height="50"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstAttribute="height" constant="50" id="Ee3-eB-Lzt"/>
</constraints>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
<state key="normal" title="Not now">
<color key="titleColor" red="0.01176470588" green="0.70196078429999997" blue="0.50588235290000005" alpha="1" colorSpace="calibratedRGB"/>
</state>
<userDefinedRuntimeAttributes>
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
<integer key="value" value="8"/>
</userDefinedRuntimeAttribute>
</userDefinedRuntimeAttributes>
<connections>
<action selector="cancelButtonAction:" destination="Y6W-OH-hqX" eventType="touchUpInside" id="5Ph-Jj-cbu"/>
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="tre-1i-GOd" firstAttribute="width" secondItem="rx5-uV-e8E" secondAttribute="width" id="3Lj-N4-Oap"/>
</constraints>
</stackView>
</subviews>
<viewLayoutGuide key="safeArea" id="vDu-zF-Fre"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
<constraints>
<constraint firstItem="vDu-zF-Fre" firstAttribute="bottom" secondItem="rx5-uV-e8E" secondAttribute="bottom" constant="20" id="Cue-th-wBf"/>
<constraint firstItem="rx5-uV-e8E" firstAttribute="leading" secondItem="vDu-zF-Fre" secondAttribute="leading" constant="20" id="qyO-6I-iAJ"/>
<constraint firstItem="rx5-uV-e8E" firstAttribute="top" secondItem="vDu-zF-Fre" secondAttribute="top" constant="24" id="tmI-9R-Tp7"/>
<constraint firstAttribute="trailing" secondItem="rx5-uV-e8E" secondAttribute="trailing" constant="20" id="um1-A3-OIa"/>
</constraints>
</view>
<nil key="simulatedTopBarMetrics"/>
<nil key="simulatedBottomBarMetrics"/>
<freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
<connections>
<outlet property="cancelButton" destination="y5Q-lI-JUe" id="z5X-g9-N5e"/>
<outlet property="enableButton" destination="Tmd-wH-3Z2" id="5cO-XD-VPS"/>
<outlet property="informationTextView" destination="qbP-qu-o0V" id="1Kd-im-4sE"/>
<outlet property="separatorLineView" destination="T3e-J2-hJW" id="6RE-Ns-I8v"/>
<outlet property="titleLabel" destination="tre-1i-GOd" id="vAt-P9-PTT"/>
</connections>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="Ief-a0-LHa" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="57" y="105"/>
</scene>
</scenes>
<resources>
<systemColor name="labelColor">
<color white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
<systemColor name="separatorColor">
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.28999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
</systemColor>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View file

@ -0,0 +1,171 @@
//
// Copyright 2022 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 UIKit
class ThreadsBetaViewController: UIViewController {
// MARK: Constants
private enum Constants {
static let learnMoreLink = "https://element.io/help#threads"
}
// MARK: Outlets
@IBOutlet private weak var titleLabel: UILabel!
@IBOutlet private weak var separatorLineView: UIView!
@IBOutlet private weak var informationTextView: UITextView! {
didSet {
informationTextView.textContainer.lineFragmentPadding = 0
}
}
@IBOutlet private weak var enableButton: UIButton!
@IBOutlet private weak var cancelButton: UIButton!
// MARK: Private
private var theme: Theme!
// MARK: Public
@objc var didTapEnableButton: (() -> Void)?
@objc var didTapCancelButton: (() -> Void)?
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
self.setupViews()
self.registerThemeServiceDidChangeThemeNotification()
self.update(theme: self.theme)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Hide back button
self.navigationItem.setHidesBackButton(true, animated: animated)
}
override var preferredStatusBarStyle: UIStatusBarStyle {
return self.theme.statusBarStyle
}
// MARK: - Setup
@objc class func instantiate() -> ThreadsBetaViewController {
let viewController = StoryboardScene.ThreadsBetaViewController.initialScene.instantiate()
viewController.theme = ThemeService.shared().theme
return viewController
}
// MARK: - Private
private func registerThemeServiceDidChangeThemeNotification() {
NotificationCenter.default.addObserver(self,
selector: #selector(themeDidChange),
name: .themeServiceDidChangeTheme,
object: nil)
}
@objc private func themeDidChange() {
self.update(theme: ThemeService.shared().theme)
}
private func setupViews() {
self.vc_removeBackTitle()
self.enableButton.setTitle(VectorL10n.threadsBetaEnable, for: .normal)
self.cancelButton.setTitle(VectorL10n.threadsBetaCancel, for: .normal)
self.titleLabel.text = VectorL10n.threadsBetaTitle
guard let font = self.informationTextView.font else {
return
}
let attributedString = NSMutableAttributedString(string: VectorL10n.threadsBetaInformation,
attributes: [.font: font])
let link = NSAttributedString(string: VectorL10n.threadsBetaInformationLink,
attributes: [.link: Constants.learnMoreLink,
.font: font])
attributedString.append(link)
self.informationTextView.attributedText = attributedString
}
// MARK: - Actions
@IBAction private func enableButtonAction(_ sender: UIButton) {
self.didTapEnableButton?()
}
@IBAction private func cancelButtonAction(_ sender: UIButton) {
self.didTapCancelButton?()
}
}
// MARK: - Themable
extension ThreadsBetaViewController: Themable {
func update(theme: Theme) {
self.theme = theme
self.view.backgroundColor = theme.colors.background
if let navigationBar = self.navigationController?.navigationBar {
theme.applyStyle(onNavigationBar: navigationBar)
}
self.titleLabel.textColor = theme.textPrimaryColor
self.separatorLineView.backgroundColor = theme.colors.system
self.informationTextView.textColor = theme.textPrimaryColor
self.enableButton.vc_setBackgroundColor(theme.tintColor, for: .normal)
self.enableButton.setTitleColor(theme.baseTextPrimaryColor, for: .normal)
self.cancelButton.vc_setBackgroundColor(.clear, for: .normal)
self.cancelButton.setTitleColor(theme.tintColor, for: .normal)
}
}
// MARK: - SlidingModalPresentable
extension ThreadsBetaViewController: SlidingModalPresentable {
func allowsDismissOnBackgroundTap() -> Bool {
return false
}
func layoutHeightFittingWidth(_ width: CGFloat) -> CGFloat {
guard let view = ThreadsNoticeViewController.instantiate().view else {
return 0
}
view.widthAnchor.constraint(equalToConstant: width).isActive = true
view.setNeedsLayout()
view.layoutIfNeeded()
let fittingSize = CGSize(width: width, height: UIView.layoutFittingCompressedSize.height)
return view.systemLayoutSizeFitting(fittingSize).height
+ UIWindow().safeAreaInsets.top
+ UIWindow().safeAreaInsets.bottom
}
}

1
changelog.d/5772.change Normal file
View file

@ -0,0 +1 @@
RoomViewController: Enable thread menu option and display opt-in screen if threads disabled.