mirror of
https://github.com/vector-im/element-ios.git
synced 2024-09-28 23:32:41 +00:00
Merge branch 'develop' into ismail/5878_threads_design_tweaks
This commit is contained in:
commit
028861d28c
10 changed files with 582 additions and 3 deletions
|
@ -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";
|
||||
|
|
|
@ -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"
|
||||
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
66
Riot/Modules/Threads/Beta/ThreadsBetaCoordinator.swift
Normal file
66
Riot/Modules/Threads/Beta/ThreadsBetaCoordinator.swift
Normal 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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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 }
|
||||
}
|
149
Riot/Modules/Threads/Beta/ThreadsBetaViewController.storyboard
Normal file
149
Riot/Modules/Threads/Beta/ThreadsBetaViewController.storyboard
Normal 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>
|
171
Riot/Modules/Threads/Beta/ThreadsBetaViewController.swift
Normal file
171
Riot/Modules/Threads/Beta/ThreadsBetaViewController.swift
Normal 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
1
changelog.d/5772.change
Normal file
|
@ -0,0 +1 @@
|
|||
RoomViewController: Enable thread menu option and display opt-in screen if threads disabled.
|
Loading…
Reference in a new issue