mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-05 03:48:27 +00:00
Run SwiftFormat.
This commit is contained in:
@@ -48,7 +48,7 @@ final class AppLockScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(AppLockScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -30,12 +30,12 @@ struct AppLockScreenViewState: BindableState {
|
||||
var numberOfDigitsEntered: Int {
|
||||
bindings.pinCode.count
|
||||
}
|
||||
|
||||
|
||||
/// Whether the subtitle is in a warning state or not.
|
||||
var isSubtitleWarning: Bool {
|
||||
numberOfPINAttempts > 0
|
||||
}
|
||||
|
||||
|
||||
/// The string shown in the screen's subtitle.
|
||||
var subtitle: String {
|
||||
if !isSubtitleWarning {
|
||||
|
||||
@@ -18,7 +18,7 @@ class AppLockScreenViewModel: AppLockScreenViewModelType, AppLockScreenViewModel
|
||||
var actions: AnyPublisher<AppLockScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(appLockService: AppLockServiceProtocol) {
|
||||
self.appLockService = appLockService
|
||||
|
||||
|
||||
+1
-1
@@ -42,7 +42,7 @@ final class AppLockSetupBiometricsScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(AppLockSetupBiometricsScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+2
-2
@@ -20,11 +20,11 @@ struct AppLockSetupBiometricsScreenViewState: BindableState {
|
||||
var icon: SFSymbol {
|
||||
biometryType.systemSymbol
|
||||
}
|
||||
|
||||
|
||||
var title: String {
|
||||
L10n.screenAppLockSetupBiometricUnlockAllowTitle(biometryType.localizedString)
|
||||
}
|
||||
|
||||
|
||||
var subtitle: String {
|
||||
L10n.screenAppLockSetupBiometricUnlockSubtitle(biometryType.localizedString)
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ class AppLockSetupBiometricsScreenViewModel: AppLockSetupBiometricsScreenViewMod
|
||||
var actions: AnyPublisher<AppLockSetupBiometricsScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(appLockService: AppLockServiceProtocol) {
|
||||
self.appLockService = appLockService
|
||||
super.init(initialViewState: AppLockSetupBiometricsScreenViewState(biometryType: appLockService.biometryType))
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ final class AppLockSetupPINScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(AppLockSetupPINScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ struct AppLockSetupPINScreenViewState: BindableState {
|
||||
var isSubtitleWarning: Bool {
|
||||
mode == .unlock && numberOfUnlockAttempts > 0
|
||||
}
|
||||
|
||||
|
||||
var subtitle: String {
|
||||
guard mode == .unlock else { return L10n.screenAppLockSetupPinContext(InfoPlistReader.main.bundleDisplayName) }
|
||||
if !isSubtitleWarning {
|
||||
|
||||
+2
-2
@@ -21,7 +21,7 @@ class AppLockSetupPINScreenViewModel: AppLockSetupPINScreenViewModelType, AppLoc
|
||||
var actions: AnyPublisher<AppLockSetupPINScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(initialMode: AppLockSetupPINScreenMode, isMandatory: Bool, appLockService: AppLockServiceProtocol) {
|
||||
self.appLockService = appLockService
|
||||
|
||||
@@ -103,7 +103,7 @@ class AppLockSetupPINScreenViewModel: AppLockSetupPINScreenViewModelType, AppLoc
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
actionsSubject.send(.complete)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -45,7 +45,7 @@ final class AppLockSetupSettingsScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(AppLockSetupSettingsScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ struct AppLockSetupSettingsScreenViewState: BindableState {
|
||||
var supportsBiometrics: Bool {
|
||||
biometryType != .none
|
||||
}
|
||||
|
||||
|
||||
var enableBiometricsTitle: String {
|
||||
L10n.screenAppLockSetupBiometricUnlockAllowTitle(biometryType.localizedString)
|
||||
}
|
||||
|
||||
@@ -30,11 +30,11 @@ enum LoginScreenCoordinatorAction {
|
||||
final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: LoginScreenCoordinatorParameters
|
||||
private var viewModel: LoginScreenViewModelProtocol
|
||||
|
||||
|
||||
private var authenticationService: AuthenticationServiceProtocol {
|
||||
parameters.authenticationService
|
||||
}
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<LoginScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
@@ -55,7 +55,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
|
||||
func start() {
|
||||
viewModel.actions
|
||||
.sink { [weak self] action in
|
||||
@@ -70,7 +70,7 @@ final class LoginScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func stop() {
|
||||
viewModel.stopLoading()
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
|
||||
var actions: AnyPublisher<LoginScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(authenticationService: AuthenticationServiceProtocol,
|
||||
loginHint: String?,
|
||||
userIndicatorController: UserIndicatorControllerProtocol,
|
||||
@@ -48,7 +48,7 @@ class LoginScreenViewModel: LoginScreenViewModelType, LoginScreenViewModelProtoc
|
||||
.weakAssign(to: \.state.homeserver, on: self)
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
override func process(viewAction: LoginScreenViewAction) {
|
||||
switch viewAction {
|
||||
case .parseUsername:
|
||||
|
||||
@@ -92,7 +92,7 @@ struct LoginScreen: View {
|
||||
.onSubmit(submit)
|
||||
|
||||
Spacer().frame(height: 32)
|
||||
|
||||
|
||||
Button(action: submit) {
|
||||
Text(L10n.actionContinue)
|
||||
}
|
||||
|
||||
@@ -139,7 +139,7 @@ class OAuthAuthenticationPresenter: NSObject {
|
||||
private var loadingIndicatorID: String {
|
||||
"\(Self.self)-Loading"
|
||||
}
|
||||
|
||||
|
||||
private var failureIndicatorID: String {
|
||||
"\(Self.self)-Failure"
|
||||
}
|
||||
|
||||
+1
-1
@@ -60,7 +60,7 @@ final class ServerConfirmationScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(ServerConfirmationScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+1
-1
@@ -52,7 +52,7 @@ struct ServerConfirmationScreenViewState: BindableState {
|
||||
/// The message shown beneath the title.
|
||||
var message: String? {
|
||||
guard case let .confirmation(homeserverAddress) = mode else { return nil }
|
||||
|
||||
|
||||
return switch authenticationFlow {
|
||||
case .login:
|
||||
if homeserverAddress == "matrix.org" {
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ class ServerConfirmationScreenViewModel: ServerConfirmationScreenViewModelType,
|
||||
var actions: AnyPublisher<ServerConfirmationScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(authenticationService: AuthenticationServiceProtocol,
|
||||
mode: ServerConfirmationScreenMode,
|
||||
authenticationFlow: AuthenticationFlow,
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
|
||||
private var authenticationService: AuthenticationServiceProtocol {
|
||||
parameters.authenticationService
|
||||
}
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<ServerSelectionScreenCoordinatorAction, Never> = .init()
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server
|
||||
var actions: AnyPublisher<ServerSelectionScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(authenticationService: AuthenticationServiceProtocol,
|
||||
authenticationFlow: AuthenticationFlow,
|
||||
appSettings: AppSettings,
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ enum MockSoftLogoutScreenState: String, CaseIterable {
|
||||
case oAuth
|
||||
case unsupported
|
||||
case keyBackupNeeded
|
||||
|
||||
|
||||
/// Generate the view struct for the screen state.
|
||||
@MainActor var viewModel: SoftLogoutScreenViewModel {
|
||||
let credentials = SoftLogoutScreenCredentials(userID: "@mock:matrix.org",
|
||||
|
||||
+8
-8
@@ -46,7 +46,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
private var authenticationService: AuthenticationServiceProtocol {
|
||||
parameters.authenticationService
|
||||
}
|
||||
|
||||
|
||||
private var oAuthPresenter: OAuthAuthenticationPresenter?
|
||||
|
||||
var actions: AnyPublisher<SoftLogoutScreenCoordinatorResult, Never> {
|
||||
@@ -69,7 +69,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
MXLog.info("Did complete with result: \(action).")
|
||||
|
||||
|
||||
switch action {
|
||||
case .login(let password):
|
||||
login(withPassword: password)
|
||||
@@ -117,18 +117,18 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
@MainActor private func stopLoading() {
|
||||
parameters.userIndicatorController.retractIndicatorWithId(Self.loadingIndicatorIdentifier)
|
||||
}
|
||||
|
||||
|
||||
/// Shows the forgot password screen.
|
||||
@MainActor private func showForgotPasswordScreen() {
|
||||
viewModel.displayError(.alert("Not implemented."))
|
||||
}
|
||||
|
||||
|
||||
/// Login with the supplied username and password.
|
||||
@MainActor private func login(withPassword password: String) {
|
||||
let username = parameters.credentials.userID
|
||||
|
||||
|
||||
startLoading()
|
||||
|
||||
|
||||
Task {
|
||||
switch await authenticationService.login(username: username,
|
||||
password: password,
|
||||
@@ -143,7 +143,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func continueWithOAuth(presentationAnchor: UIWindow?) {
|
||||
guard let presentationAnchor else { return }
|
||||
|
||||
@@ -174,7 +174,7 @@ final class SoftLogoutScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Processes an error to either update the flow or display it to the user.
|
||||
private func handleError(_ error: AuthenticationServiceError) {
|
||||
switch error {
|
||||
|
||||
+6
-6
@@ -43,16 +43,16 @@ enum SoftLogoutScreenViewModelAction: CustomStringConvertible {
|
||||
struct SoftLogoutScreenViewState: BindableState {
|
||||
/// Soft logout credentials
|
||||
var credentials: SoftLogoutScreenCredentials
|
||||
|
||||
|
||||
/// Data about the selected homeserver.
|
||||
var homeserver: LoginHomeserver
|
||||
|
||||
|
||||
/// Flag indicating soft logged out user needs backup for some keys
|
||||
var keyBackupNeeded: Bool
|
||||
|
||||
|
||||
/// View state that can be bound to from SwiftUI.
|
||||
var bindings: SoftLogoutScreenBindings
|
||||
|
||||
|
||||
/// The types of login supported by the homeserver.
|
||||
var loginMode: LoginMode {
|
||||
homeserver.loginMode
|
||||
@@ -60,12 +60,12 @@ struct SoftLogoutScreenViewState: BindableState {
|
||||
|
||||
/// The presentation anchor used for OAuth authentication.
|
||||
var window: UIWindow?
|
||||
|
||||
|
||||
/// Whether to show recover encryption keys message
|
||||
var showRecoverEncryptionKeysMessage: Bool {
|
||||
keyBackupNeeded
|
||||
}
|
||||
|
||||
|
||||
/// `true` when valid credentials have been entered and a homeserver has been loaded.
|
||||
var canSubmit: Bool {
|
||||
!bindings.password.isEmpty
|
||||
|
||||
+2
-2
@@ -17,7 +17,7 @@ class SoftLogoutScreenViewModel: SoftLogoutScreenViewModelType, SoftLogoutScreen
|
||||
var actions: AnyPublisher<SoftLogoutScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(credentials: SoftLogoutScreenCredentials,
|
||||
homeserver: LoginHomeserver,
|
||||
keyBackupNeeded: Bool,
|
||||
@@ -45,7 +45,7 @@ class SoftLogoutScreenViewModel: SoftLogoutScreenViewModelType, SoftLogoutScreen
|
||||
Task { state.window = window }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@MainActor func displayError(_ type: SoftLogoutScreenErrorType) {
|
||||
switch type {
|
||||
case .alert(let message):
|
||||
|
||||
+18
-18
@@ -10,10 +10,10 @@ import SwiftUI
|
||||
|
||||
struct SoftLogoutScreen: View {
|
||||
@State private var showingClearDataConfirmation = false
|
||||
|
||||
|
||||
/// The focus state of the password text field.
|
||||
@FocusState private var isPasswordFocused: Bool
|
||||
|
||||
|
||||
@Bindable var context: SoftLogoutScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
@@ -22,7 +22,7 @@ struct SoftLogoutScreen: View {
|
||||
header
|
||||
.padding(.top, UIConstants.titleTopPaddingToNavigationBar)
|
||||
.padding(.bottom, 36)
|
||||
|
||||
|
||||
switch context.viewState.loginMode {
|
||||
case .password:
|
||||
loginForm
|
||||
@@ -31,7 +31,7 @@ struct SoftLogoutScreen: View {
|
||||
default:
|
||||
loginUnavailableText
|
||||
}
|
||||
|
||||
|
||||
clearDataForm
|
||||
.padding(.top, 16)
|
||||
}
|
||||
@@ -45,7 +45,7 @@ struct SoftLogoutScreen: View {
|
||||
context.send(viewAction: .updateWindow(window))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The title, message and icon at the top of the screen.
|
||||
var header: some View {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
@@ -54,13 +54,13 @@ struct SoftLogoutScreen: View {
|
||||
.multilineTextAlignment(.leading)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.accessibilityIdentifier(A11yIdentifiers.softLogoutScreen.title)
|
||||
|
||||
|
||||
Text(UntranslatedL10n.softLogoutSigninNotice(context.viewState.credentials.homeserverName, context.viewState.credentials.userDisplayName, context.viewState.credentials.userID))
|
||||
.font(.compound.bodyLG)
|
||||
.multilineTextAlignment(.leading)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.accessibilityIdentifier(A11yIdentifiers.softLogoutScreen.message)
|
||||
|
||||
|
||||
if context.viewState.showRecoverEncryptionKeysMessage {
|
||||
Text(UntranslatedL10n.softLogoutSigninE2eWarningNotice)
|
||||
.font(.compound.bodyLG)
|
||||
@@ -69,7 +69,7 @@ struct SoftLogoutScreen: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The form with text fields for username and password, along with a submit button.
|
||||
var loginForm: some View {
|
||||
VStack(spacing: 14) {
|
||||
@@ -80,7 +80,7 @@ struct SoftLogoutScreen: View {
|
||||
.submitLabel(.done)
|
||||
.onSubmit(submit)
|
||||
.accessibilityIdentifier(A11yIdentifiers.softLogoutScreen.password)
|
||||
|
||||
|
||||
Button { context.send(viewAction: .forgotPassword) } label: {
|
||||
Text(L10n.actionForgotPassword)
|
||||
.font(.compound.bodyLG)
|
||||
@@ -88,7 +88,7 @@ struct SoftLogoutScreen: View {
|
||||
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||
.padding(.bottom, 8)
|
||||
.accessibilityIdentifier(A11yIdentifiers.softLogoutScreen.forgotPassword)
|
||||
|
||||
|
||||
Button(action: submit) {
|
||||
Text(L10n.actionNext)
|
||||
}
|
||||
@@ -97,7 +97,7 @@ struct SoftLogoutScreen: View {
|
||||
.accessibilityIdentifier(A11yIdentifiers.softLogoutScreen.next)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// The OAuth button that can be used for login.
|
||||
var oAuthButton: some View {
|
||||
Button { context.send(viewAction: .continueWithOAuth) } label: {
|
||||
@@ -105,7 +105,7 @@ struct SoftLogoutScreen: View {
|
||||
}
|
||||
.buttonStyle(.compound(.primary))
|
||||
}
|
||||
|
||||
|
||||
/// Text shown if neither password or OAuth login is supported.
|
||||
var loginUnavailableText: some View {
|
||||
Text(L10n.screenLoginErrorUnsupportedAuthentication)
|
||||
@@ -114,7 +114,7 @@ struct SoftLogoutScreen: View {
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
|
||||
/// The text field and submit button where the user enters an email address.
|
||||
var clearDataForm: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
@@ -123,14 +123,14 @@ struct SoftLogoutScreen: View {
|
||||
.multilineTextAlignment(.leading)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.accessibilityIdentifier(A11yIdentifiers.softLogoutScreen.clearDataTitle)
|
||||
|
||||
|
||||
Text(UntranslatedL10n.softLogoutClearDataNotice)
|
||||
.font(.compound.bodyLG)
|
||||
.multilineTextAlignment(.leading)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.accessibilityIdentifier(A11yIdentifiers.softLogoutScreen.clearDataMessage)
|
||||
.padding(.bottom, 12)
|
||||
|
||||
|
||||
Button(role: .destructive, action: clearData) {
|
||||
Text(UntranslatedL10n.softLogoutClearDataSubmit)
|
||||
}
|
||||
@@ -146,13 +146,13 @@ struct SoftLogoutScreen: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Sends the `login` view action so long as a valid email address has been input.
|
||||
func submit() {
|
||||
guard context.viewState.canSubmit else { return }
|
||||
context.send(viewAction: .login)
|
||||
}
|
||||
|
||||
|
||||
/// Sends the `clearAllData` view action.
|
||||
func clearData() {
|
||||
context.send(viewAction: .clearAllData)
|
||||
@@ -167,7 +167,7 @@ struct SoftLogoutScreen_Previews: PreviewProvider, TestablePreview {
|
||||
screen(for: state.viewModel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static func screen(for viewModel: SoftLogoutScreenViewModel) -> some View {
|
||||
ElementNavigationStack {
|
||||
SoftLogoutScreen(context: viewModel.context)
|
||||
|
||||
+2
-2
@@ -64,12 +64,12 @@ final class AuthenticationStartScreenCoordinator: CoordinatorProtocol {
|
||||
actionsSubject.send(.login)
|
||||
case .register:
|
||||
actionsSubject.send(.register)
|
||||
|
||||
|
||||
case .loginDirectlyWithOAuth(let data, let window):
|
||||
actionsSubject.send(.loginDirectlyWithOAuth(data: data, window: window))
|
||||
case .loginDirectlyWithPassword(let loginHint):
|
||||
actionsSubject.send(.loginDirectlyWithPassword(loginHint: loginHint))
|
||||
|
||||
|
||||
case .reportProblem:
|
||||
actionsSubject.send(.reportProblem)
|
||||
case .developerOptions:
|
||||
|
||||
+2
-2
@@ -89,14 +89,14 @@ class AuthenticationStartScreenViewModel: AuthenticationStartScreenViewModelType
|
||||
}
|
||||
case .developerOptions:
|
||||
actionsSubject.send(.developerOptions)
|
||||
|
||||
|
||||
case .loginWithQR:
|
||||
actionsSubject.send(.loginWithQR)
|
||||
case .login:
|
||||
Task { await login() }
|
||||
case .register:
|
||||
actionsSubject.send(.register)
|
||||
|
||||
|
||||
case .continueWithClassic(let account):
|
||||
Task { await login(classicAppAccount: account) }
|
||||
case .otherOptions(let account):
|
||||
|
||||
+2
-2
@@ -65,12 +65,12 @@ private struct AuthenticationBrandLogoModifier: ViewModifier {
|
||||
private var isLight: Bool {
|
||||
colorScheme == .light
|
||||
}
|
||||
|
||||
|
||||
/// Extra padding needed to avoid cropping the shadows.
|
||||
private var extra: CGFloat {
|
||||
64 * scale
|
||||
}
|
||||
|
||||
|
||||
/// The shape that the logo is composed on top of.
|
||||
private var outerShape: RoundedRectangle {
|
||||
RoundedRectangle(cornerRadius: 44 * scale)
|
||||
|
||||
@@ -21,7 +21,7 @@ enum UIConstants {
|
||||
|
||||
/// The padding used to the top of the view for breaker screens that don't have a navigation bar.
|
||||
static let startScreenBreakerScreenTopPadding: CGFloat = 80
|
||||
|
||||
|
||||
/// The height to use for top/bottom spacers to pad the views to fit the `maxContentHeight`.
|
||||
static func spacerHeight(in geometry: GeometryProxy) -> CGFloat {
|
||||
max(0, (geometry.size.height - maxContentHeight) / 2)
|
||||
|
||||
@@ -27,7 +27,7 @@ final class BlockedUsersScreenCoordinator: CoordinatorProtocol {
|
||||
func stop() {
|
||||
viewModel.stop()
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(BlockedUsersScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ class BlockedUsersScreenViewModel: BlockedUsersScreenViewModelType, BlockedUsers
|
||||
let hideProfiles: Bool
|
||||
let clientProxy: ClientProxyProtocol
|
||||
let userIndicatorController: UserIndicatorControllerProtocol
|
||||
|
||||
|
||||
init(hideProfiles: Bool,
|
||||
userSession: UserSessionProtocol,
|
||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
|
||||
@@ -69,7 +69,7 @@ final class BugReportScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func stop() {
|
||||
stopLoading()
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ class BugReportScreenViewModel: BugReportScreenViewModelType, BugReportScreenVie
|
||||
private let actionsSubject: PassthroughSubject<BugReportScreenViewModelAction, Never> = .init()
|
||||
// periphery:ignore - when set to nil this is automatically cancelled
|
||||
@CancellableTask private var uploadTask: Task<Void, Never>?
|
||||
|
||||
|
||||
var actions: AnyPublisher<BugReportScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
@@ -79,7 +79,7 @@ class BugReportScreenViewModel: BugReportScreenViewModelType, BugReportScreenVie
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
private func submitBugReport() async {
|
||||
let progressSubject = CurrentValueSubject<Double, Never>(0.0)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ struct BugReportScreen: View {
|
||||
var canSendLogFiles: Bool {
|
||||
context.viewState.canSendLogFiles
|
||||
}
|
||||
|
||||
|
||||
var photosPickerTitle: String {
|
||||
context.viewState.screenshot == nil ? L10n.screenBugReportAttachScreenshot : L10n.screenBugReportEditScreenshot
|
||||
}
|
||||
@@ -82,7 +82,7 @@ struct BugReportScreen: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var canContactSection: some View {
|
||||
Section {
|
||||
ListRow(label: .plain(title: L10n.screenBugReportContactMeTitle),
|
||||
@@ -93,7 +93,7 @@ struct BugReportScreen: View {
|
||||
.compoundListSectionFooter()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var attachScreenshotSection: some View {
|
||||
Section {
|
||||
ListRow(kind: .custom {
|
||||
|
||||
@@ -70,7 +70,7 @@ final class CallScreenCoordinator: CoordinatorProtocol {
|
||||
func stop() {
|
||||
viewModel.stop()
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(CallScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
|
||||
@CancellableTask
|
||||
private var timeoutTask: Task<Void, Never>?
|
||||
|
||||
|
||||
/// Designated initialiser
|
||||
/// - Parameters:
|
||||
/// - elementCallService: service responsible for setting up CallKit
|
||||
@@ -140,7 +140,7 @@ class CallScreenViewModel: CallScreenViewModelType, CallScreenViewModelProtocol
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
|
||||
private func handleWidgetAction(message: String) async {
|
||||
if timeoutTask != nil,
|
||||
let decodedMessage = try? DecodedWidgetMessage.decode(message: message),
|
||||
|
||||
@@ -196,7 +196,7 @@ private struct CallView: UIViewRepresentable {
|
||||
guard let body = message.body as? [String: String],
|
||||
let level = body["level"],
|
||||
let logMessage = body["message"] else { return }
|
||||
|
||||
|
||||
switch level {
|
||||
case "log", "debug":
|
||||
MXLog.debug("[ElementCall]: \(logMessage)")
|
||||
@@ -363,7 +363,7 @@ struct CallScreen_Previews: PreviewProvider {
|
||||
widgetDriver.startBaseURLClientIDColorSchemeVoiceOnlyRageshakeURLAnalyticsConfigurationReturnValue = .success(URL.userDirectory)
|
||||
|
||||
roomProxy.elementCallWidgetDriverDeviceIDReturnValue = widgetDriver
|
||||
|
||||
|
||||
return CallScreenViewModel(elementCallService: ElementCallServiceMock(.init()),
|
||||
configuration: .init(roomProxy: roomProxy,
|
||||
clientProxy: clientProxy,
|
||||
|
||||
@@ -14,12 +14,12 @@ struct ChatsSpaceFilterCell: View {
|
||||
|
||||
let filter: SpaceServiceFilter
|
||||
let mediaProvider: MediaProviderProtocol!
|
||||
|
||||
|
||||
private let verticalInsets = 12.0
|
||||
private let horizontalInsets = 16.0
|
||||
|
||||
let action: (SpaceServiceFilter) -> Void
|
||||
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
action(filter)
|
||||
|
||||
@@ -51,7 +51,7 @@ final class PollFormScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(PollFormScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ struct PollFormScreenViewState: BindableState {
|
||||
case .edit(_, let poll):
|
||||
initialBindings = .init(poll: poll)
|
||||
}
|
||||
|
||||
|
||||
return bindings != initialBindings
|
||||
}
|
||||
}
|
||||
@@ -79,16 +79,16 @@ struct PollFormScreenViewStateBindings: Equatable {
|
||||
var question = ""
|
||||
var options: [Option] = [.init(), .init()]
|
||||
var isUndisclosed = false
|
||||
|
||||
|
||||
struct Option: Identifiable, Equatable {
|
||||
let id = UUID()
|
||||
var text = ""
|
||||
}
|
||||
|
||||
|
||||
var hasValidContent: Bool {
|
||||
!question.isEmpty && options.count >= 2 && options.allSatisfy { !$0.text.isEmpty }
|
||||
}
|
||||
|
||||
|
||||
var alertInfo: AlertInfo<UUID>?
|
||||
|
||||
static func == (lhs: PollFormScreenViewStateBindings, rhs: PollFormScreenViewStateBindings) -> Bool {
|
||||
|
||||
@@ -62,7 +62,7 @@ final class CreateRoomScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(CreateRoomScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ struct CreateRoomScreenViewState: BindableState {
|
||||
var canCreateRoom: Bool {
|
||||
!roomName.isEmpty && aliasErrors.isEmpty
|
||||
}
|
||||
|
||||
|
||||
var aliasErrors: Set<CreateRoomScreenAliasErrorState> = []
|
||||
var aliasErrorDescription: String? {
|
||||
if aliasErrors.contains(.alreadyExists) {
|
||||
@@ -62,7 +62,7 @@ struct CreateRoomScreenViewState: BindableState {
|
||||
nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var availableAccessTypes: [CreateRoomScreenAccessType] {
|
||||
var availableAccessTypes: [CreateRoomScreenAccessType] = []
|
||||
if isSpace {
|
||||
|
||||
@@ -58,7 +58,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
let bindings = CreateRoomScreenViewStateBindings(roomTopic: "",
|
||||
selectedAccessType: selectedAccessType,
|
||||
selectedSpace: selectedSpace)
|
||||
|
||||
|
||||
super.init(initialViewState: CreateRoomScreenViewState(isSpace: isSpace,
|
||||
shouldShowCancelButton: shouldShowCancelButton,
|
||||
roomName: "",
|
||||
@@ -137,7 +137,7 @@ class CreateRoomScreenViewModel: CreateRoomScreenViewModelType, CreateRoomScreen
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
|
||||
private func setupBindings() {
|
||||
// Reset the state related to public rooms if the user choses the room to be empty
|
||||
context.$viewState
|
||||
|
||||
@@ -12,7 +12,7 @@ import SwiftUI
|
||||
struct CreateRoomScreen: View {
|
||||
@ObservedObject var context: CreateRoomScreenViewModel.Context
|
||||
@FocusState private var focus: Focus?
|
||||
|
||||
|
||||
private enum Focus {
|
||||
case name
|
||||
case topic
|
||||
@@ -380,7 +380,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
viewModel.context.selectedAccessType = .askToJoinWithSpaceMembers
|
||||
return viewModel
|
||||
}()
|
||||
|
||||
|
||||
static var previews: some View {
|
||||
ElementNavigationStack {
|
||||
CreateRoomScreen(context: viewModel.context)
|
||||
@@ -455,7 +455,7 @@ struct CreateRoom_Previews: PreviewProvider, TestablePreview {
|
||||
let spaces = [SpaceServiceRoom].mockJoinedSpaces2
|
||||
clientProxy.spaceService = SpaceServiceProxyMock(.init(editableSpaces: spaces))
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
|
||||
|
||||
return CreateRoomScreenViewModel(isSpace: isSpace,
|
||||
spaceSelectionMode: selectionMode,
|
||||
shouldShowCancelButton: isSpace,
|
||||
|
||||
@@ -66,9 +66,9 @@ struct CreateRoomSpaceSelectionSheet_Previews: PreviewProvider, TestablePreview
|
||||
let clientProxy = ClientProxyMock(.init(userID: "@userid:example.com"))
|
||||
clientProxy.spaceService = SpaceServiceProxyMock(.init(editableSpaces: .mockJoinedSpaces2))
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
|
||||
|
||||
let appSettings = AppSettings.volatile()
|
||||
|
||||
|
||||
return CreateRoomScreenViewModel(isSpace: false,
|
||||
spaceSelectionMode: .editableSpacesList(preSelectedSpace: nil),
|
||||
shouldShowCancelButton: false,
|
||||
|
||||
+2
-2
@@ -27,7 +27,7 @@ final class DeclineAndBlockScreenCoordinator: CoordinatorProtocol {
|
||||
private let viewModel: DeclineAndBlockScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<DeclineAndBlockScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<DeclineAndBlockScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -54,7 +54,7 @@ final class DeclineAndBlockScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(DeclineAndBlockScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ class DeclineAndBlockScreenViewModel: DeclineAndBlockScreenViewModelType, Declin
|
||||
var actionsPublisher: AnyPublisher<DeclineAndBlockScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(userID: String,
|
||||
roomID: String,
|
||||
clientProxy: ClientProxyProtocol,
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ final class EditRoomAddressScreenCoordinator: CoordinatorProtocol {
|
||||
private let viewModel: EditRoomAddressScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<EditRoomAddressScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<EditRoomAddressScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -49,7 +49,7 @@ final class EditRoomAddressScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(EditRoomAddressScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class EditRoomAddressScreenViewModel: EditRoomAddressScreenViewModelType, EditRo
|
||||
}
|
||||
|
||||
@CancellableTask private var checkAliasAvailabilityTask: Task<Void, Never>?
|
||||
|
||||
|
||||
init(initialViewState: EditRoomAddressScreenViewState? = nil,
|
||||
roomProxy: JoinedRoomProxyProtocol,
|
||||
clientProxy: ClientProxyProtocol,
|
||||
@@ -169,7 +169,7 @@ class EditRoomAddressScreenViewModel: EditRoomAddressScreenViewModelType, EditRo
|
||||
if savedCanonicalAlias == nil || savedCanonicalAlias?.hasSuffix(state.serverName) == true {
|
||||
var newAlternativeAliases = roomProxy.infoPublisher.value.alternativeAliases
|
||||
newAlternativeAliases.removeAll { $0 == savedAliasFromHomeserver }
|
||||
|
||||
|
||||
if case .failure = await roomProxy.updateCanonicalAlias(desiredCanonicalAlias, altAliases: newAlternativeAliases) {
|
||||
userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown))
|
||||
return
|
||||
|
||||
@@ -47,7 +47,7 @@ class EmojiPickerScreenViewModel: EmojiPickerScreenViewModelType, EmojiPickerScr
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
|
||||
private func loadEmojis() {
|
||||
Task(priority: .userInitiated) { [weak self] in
|
||||
guard let self else { return }
|
||||
|
||||
+2
-2
@@ -22,7 +22,7 @@ final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol {
|
||||
private let viewModel: EncryptionResetPasswordScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<EncryptionResetPasswordScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<EncryptionResetPasswordScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -46,7 +46,7 @@ final class EncryptionResetPasswordScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(EncryptionResetPasswordScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ class EncryptionResetPasswordScreenViewModel: EncryptionResetPasswordScreenViewM
|
||||
var actionsPublisher: AnyPublisher<EncryptionResetPasswordScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(passwordPublisher: PassthroughSubject<String, Never>) {
|
||||
self.passwordPublisher = passwordPublisher
|
||||
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol {
|
||||
private let viewModel: EncryptionResetScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<EncryptionResetScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<EncryptionResetScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -60,7 +60,7 @@ final class EncryptionResetScreenCoordinator: CoordinatorProtocol {
|
||||
func stop() {
|
||||
viewModel.stop()
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(EncryptionResetScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ struct EncryptionResetScreenViewState: BindableState {
|
||||
AttributedString(L10n.screenCreateNewRecoveryKeyListItem5)
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
var bindings: EncryptionResetScreenViewStateBindings
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ class EncryptionResetScreenViewModel: EncryptionResetScreenViewModelType, Encryp
|
||||
|
||||
private var identityResetHandle: IdentityResetHandle?
|
||||
private var passwordCancellable: AnyCancellable?
|
||||
|
||||
|
||||
init(clientProxy: ClientProxyProtocol, userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
self.clientProxy = clientProxy
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
+1
-1
@@ -69,7 +69,7 @@ struct EncryptionResetScreen: View {
|
||||
.frame(maxWidth: .infinity)
|
||||
.environment(\.backgroundStyle, AnyShapeStyle(.compound.bgSubtleSecondary))
|
||||
}
|
||||
|
||||
|
||||
private func checkMarkItem(title: String, position: ListPosition, positive: Bool) -> some View {
|
||||
VisualListItem(title: title, position: position) {
|
||||
CompoundIcon(positive ? \.check : \.info)
|
||||
|
||||
@@ -43,14 +43,14 @@ private struct MediaPreviewViewController: UIViewControllerRepresentable {
|
||||
let allowEditing: Bool
|
||||
let dismissalPublisher: PassthroughSubject<Void, Never>
|
||||
let onDismiss: () -> Void
|
||||
|
||||
|
||||
func makeUIViewController(context: Context) -> PreviewHostingController {
|
||||
PreviewHostingController(previewItem: previewItem,
|
||||
allowEditing: allowEditing,
|
||||
dismissalPublisher: dismissalPublisher,
|
||||
onDismiss: onDismiss)
|
||||
}
|
||||
|
||||
|
||||
func updateUIViewController(_ uiViewController: PreviewHostingController, context: Context) { }
|
||||
|
||||
/// A view controller that hosts the QuickLook preview.
|
||||
@@ -66,7 +66,7 @@ private struct MediaPreviewViewController: UIViewControllerRepresentable {
|
||||
private var dismissalObserver: AnyCancellable?
|
||||
|
||||
var previewController: QLPreviewController?
|
||||
|
||||
|
||||
init(previewItem: MediaPreviewItem,
|
||||
allowEditing: Bool,
|
||||
dismissalPublisher: PassthroughSubject<Void, Never>,
|
||||
@@ -74,7 +74,7 @@ private struct MediaPreviewViewController: UIViewControllerRepresentable {
|
||||
self.previewItem = previewItem
|
||||
self.allowEditing = allowEditing
|
||||
self.onDismiss = onDismiss
|
||||
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
// The QLPreviewController will not automatically dismiss itself when the underlying view is removed
|
||||
@@ -128,7 +128,7 @@ private struct MediaPreviewViewController: UIViewControllerRepresentable {
|
||||
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
|
||||
1
|
||||
}
|
||||
|
||||
|
||||
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||
previewItem
|
||||
}
|
||||
@@ -156,9 +156,9 @@ class MediaPreviewItem: NSObject, QLPreviewItem {
|
||||
var previewItemURL: URL? {
|
||||
file.url
|
||||
}
|
||||
|
||||
|
||||
let previewItemTitle: String?
|
||||
|
||||
|
||||
init(file: MediaFileHandleProxy, title: String?) {
|
||||
self.file = file
|
||||
previewItemTitle = title
|
||||
|
||||
@@ -117,7 +117,7 @@ class TimelineMediaPreviewDataSource: NSObject, QLPreviewControllerDataSource {
|
||||
var firstPreviewItemIndex: Int {
|
||||
backwardPadding
|
||||
}
|
||||
|
||||
|
||||
var lastPreviewItemIndex: Int {
|
||||
backwardPadding + previewItems.count - 1
|
||||
}
|
||||
@@ -276,11 +276,11 @@ enum TimelineMediaPreviewItem: Equatable {
|
||||
var caption: String? {
|
||||
timelineItem.mediaCaption
|
||||
}
|
||||
|
||||
|
||||
var formattedCaption: AttributedString? {
|
||||
timelineItem.formattedMediaCaption
|
||||
}
|
||||
|
||||
|
||||
var contentType: String? {
|
||||
switch timelineItem {
|
||||
case let audioItem as AudioRoomTimelineItem:
|
||||
|
||||
@@ -59,7 +59,7 @@ struct TimelineMediaPreviewViewState: BindableState {
|
||||
var currentItem: TimelineMediaPreviewItem {
|
||||
dataSource.currentItem
|
||||
}
|
||||
|
||||
|
||||
/// All of the available actions for the current item.
|
||||
var currentItemActions: TimelineItemMenuActions?
|
||||
|
||||
|
||||
@@ -41,13 +41,13 @@ private struct MediaPreviewViewController: UIViewControllerRepresentable {
|
||||
let viewModel: TimelineMediaPreviewViewModel
|
||||
let dismissalPublisher: PassthroughSubject<Void, Never>
|
||||
let onDismiss: () -> Void
|
||||
|
||||
|
||||
func makeUIViewController(context: Context) -> PreviewHostingController {
|
||||
PreviewHostingController(viewModel: viewModel,
|
||||
dismissalPublisher: dismissalPublisher,
|
||||
onDismiss: onDismiss)
|
||||
}
|
||||
|
||||
|
||||
func updateUIViewController(_ uiViewController: PreviewHostingController, context: Context) { }
|
||||
|
||||
/// A view controller that hosts the QuickLook preview.
|
||||
@@ -69,7 +69,7 @@ private struct MediaPreviewViewController: UIViewControllerRepresentable {
|
||||
onDismiss: @escaping () -> Void) {
|
||||
self.onDismiss = onDismiss
|
||||
previewController = TimelineMediaPreviewController(context: viewModel.context)
|
||||
|
||||
|
||||
super.init(nibName: nil, bundle: nil)
|
||||
|
||||
// The QLPreviewController will not automatically dismiss itself when the underlying view is removed
|
||||
|
||||
+5
-5
@@ -27,7 +27,7 @@ class TimelineMediaPreviewController: QLPreviewController {
|
||||
private var navigationBar: UINavigationBar? {
|
||||
view.subviews.first?.subviews.first { $0 is UINavigationBar } as? UINavigationBar
|
||||
}
|
||||
|
||||
|
||||
private var bottomBarItemsContainer: UIView? {
|
||||
if #available(iOS 26, *) {
|
||||
view.subviews.first?.subviews.last?.subviews.first
|
||||
@@ -35,11 +35,11 @@ class TimelineMediaPreviewController: QLPreviewController {
|
||||
view.subviews.first?.subviews.last { $0 is UIToolbar }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var pageScrollView: UIScrollView? {
|
||||
view.firstScrollView()
|
||||
}
|
||||
|
||||
|
||||
private var captionView: UIView {
|
||||
captionHostingController.view
|
||||
}
|
||||
@@ -316,7 +316,7 @@ private struct CaptionView: View {
|
||||
private var currentItem: TimelineMediaPreviewItem {
|
||||
context.viewState.currentItem
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
if case let .media(mediaItem) = currentItem, mediaItem.hasCaption {
|
||||
CaptionScrollView(mediaItem: mediaItem)
|
||||
@@ -361,7 +361,7 @@ private struct CaptionScrollView: View {
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder
|
||||
private var captionContent: some View {
|
||||
if let formattedCaption = mediaItem.formattedCaption {
|
||||
|
||||
+3
-3
@@ -15,7 +15,7 @@ struct TimelineMediaPreviewRedactConfirmationView: View {
|
||||
let item: TimelineMediaPreviewItem.Media
|
||||
@ObservedObject var context: TimelineMediaPreviewViewModel.Context
|
||||
var preferredColorScheme: ColorScheme? = .dark
|
||||
|
||||
|
||||
@State private var sheetHeight: CGFloat = .zero
|
||||
private let topPadding: CGFloat = 19
|
||||
|
||||
@@ -74,12 +74,12 @@ struct TimelineMediaPreviewRedactConfirmationView: View {
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(item.filename ?? "")
|
||||
.font(.compound.bodyMD)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
|
||||
|
||||
if let contentType = item.contentType {
|
||||
Group {
|
||||
if let fileSize = item.fileSize {
|
||||
|
||||
@@ -18,7 +18,7 @@ class GlobalSearchScreenViewModel: GlobalSearchScreenViewModelType, GlobalSearch
|
||||
var actions: AnyPublisher<GlobalSearchScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(roomSummaryProvider: RoomSummaryProviderProtocol,
|
||||
mediaProvider: MediaProviderProtocol) {
|
||||
self.roomSummaryProvider = roomSummaryProvider
|
||||
|
||||
@@ -134,7 +134,7 @@ private struct GlobalSearchTextFieldRepresentable: UIViewRepresentable {
|
||||
@Binding var text: String
|
||||
let keyPressHandler: (UIKeyboardHIDUsage) -> Bool
|
||||
let endEditingHandler: () -> Void
|
||||
|
||||
|
||||
func makeUIView(context: Context) -> UITextField {
|
||||
let textField = GlobalSearchTextField(keyPressHandler: keyPressHandler)
|
||||
textField.delegate = context.coordinator
|
||||
@@ -142,15 +142,15 @@ private struct GlobalSearchTextFieldRepresentable: UIViewRepresentable {
|
||||
textField.placeholder = placeholder
|
||||
return textField
|
||||
}
|
||||
|
||||
|
||||
func updateUIView(_ uiView: UITextField, context: Context) {
|
||||
uiView.text = text
|
||||
}
|
||||
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(text: $text, endEditingHandler: endEditingHandler)
|
||||
}
|
||||
|
||||
|
||||
class Coordinator: NSObject, UITextFieldDelegate {
|
||||
var text: Binding<String>
|
||||
let endEditingHandler: () -> Void
|
||||
@@ -188,7 +188,7 @@ private class GlobalSearchTextField: UITextField {
|
||||
self.keyPressHandler = keyPressHandler
|
||||
super.init(frame: .zero)
|
||||
}
|
||||
|
||||
|
||||
override func pressesBegan(_ presses: Set<UIPress>, with event: UIPressesEvent?) {
|
||||
guard let key = presses.first?.key else {
|
||||
super.pressesBegan(presses, with: event)
|
||||
|
||||
@@ -98,12 +98,12 @@ struct HomeScreenViewState: BindableState {
|
||||
var shouldShowNewSoundBanner = false
|
||||
|
||||
var requiresExtraAccountSetup = false
|
||||
|
||||
|
||||
var rooms: [HomeScreenRoom] = []
|
||||
var roomListMode: HomeScreenRoomListMode = .skeletons
|
||||
|
||||
var hasPendingInvitations = false
|
||||
|
||||
|
||||
var selectedRoomID: String?
|
||||
|
||||
var hideInviteAvatars = false
|
||||
@@ -111,7 +111,7 @@ struct HomeScreenViewState: BindableState {
|
||||
var roomListActivityVisibility: RoomListActivityVisibility = .current
|
||||
|
||||
var reportRoomEnabled = false
|
||||
|
||||
|
||||
var shouldShowSpaceFilters = false
|
||||
var selectedSpaceFilter: SpaceServiceFilter?
|
||||
|
||||
@@ -122,7 +122,7 @@ struct HomeScreenViewState: BindableState {
|
||||
|
||||
return rooms
|
||||
}
|
||||
|
||||
|
||||
var bindings: HomeScreenViewStateBindings
|
||||
|
||||
var placeholderRooms: [HomeScreenRoom] {
|
||||
@@ -175,7 +175,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
||||
}
|
||||
|
||||
static let placeholderLastMessage = AttributedString("Hidden last message")
|
||||
|
||||
|
||||
/// The list item identifier is it's room identifier.
|
||||
let id: String
|
||||
|
||||
@@ -217,7 +217,7 @@ struct HomeScreenRoom: Identifiable, Equatable {
|
||||
let lastMessageState: LastMessageState?
|
||||
|
||||
let avatar: RoomAvatar
|
||||
|
||||
|
||||
let canonicalAlias: String?
|
||||
|
||||
let isTombstoned: Bool
|
||||
|
||||
@@ -354,7 +354,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func updateRooms() {
|
||||
guard let roomSummaryProvider else {
|
||||
MXLog.error("Room summary provider unavailable")
|
||||
|
||||
@@ -11,7 +11,7 @@ import SwiftUI
|
||||
struct RoomListFilterView: View {
|
||||
let filter: RoomListFilter
|
||||
@Binding var isActive: Bool
|
||||
|
||||
|
||||
var body: some View {
|
||||
Toggle(isOn: $isActive) {
|
||||
Text(filter.localizedName)
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ struct RoomListFiltersEmptyStateView: View {
|
||||
}
|
||||
return L10n.screenRoomlistFilterMixedEmptyStateSubtitle
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 24) {
|
||||
Text(emptyStateTitle)
|
||||
|
||||
@@ -49,7 +49,7 @@ struct HomeScreen: View {
|
||||
L10n.screenRoomlistMainSpaceTitle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
@@ -70,7 +70,7 @@ struct HomeScreen: View {
|
||||
if #available(iOS 26, *) {
|
||||
ToolbarSpacer(.fixed, placement: .primaryAction)
|
||||
}
|
||||
|
||||
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
SpaceFiltersButton(selected: context.viewState.selectedSpaceFilter != nil) {
|
||||
context.send(viewAction: .spaceFilters)
|
||||
@@ -221,7 +221,7 @@ struct HomeScreen_Previews: PreviewProvider, TestablePreview {
|
||||
roomSummaryProvider: RoomSummaryProviderMock(.init(state: roomSummaryProviderState))))
|
||||
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
|
||||
|
||||
return HomeScreenViewModel(userSession: userSession,
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
||||
appSettings: .volatile(),
|
||||
|
||||
@@ -79,7 +79,7 @@ struct HomeScreenEmptyStateLayout: Layout {
|
||||
|
||||
// Place the main view in the center if there is space, otherwise add it to the stack.
|
||||
guard let mainView else { return }
|
||||
|
||||
|
||||
let mainViewSize = mainView.sizeThatFits(proposal)
|
||||
if (y + mainViewSize.height / 2) < bounds.height / 2 {
|
||||
let center = CGPoint(x: bounds.midX, y: bounds.midY)
|
||||
|
||||
@@ -134,7 +134,7 @@ struct HomeScreenInviteCell: View {
|
||||
.fill(Color.compound.borderDisabled)
|
||||
.frame(height: 1 / UIScreen.main.scale)
|
||||
}
|
||||
|
||||
|
||||
private var title: String {
|
||||
room.name
|
||||
}
|
||||
@@ -199,7 +199,7 @@ struct HomeScreenInviteCell_Previews: PreviewProvider, TestablePreview {
|
||||
let clientProxy = ClientProxyMock(.init())
|
||||
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
|
||||
|
||||
return HomeScreenViewModel(userSession: userSession,
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
||||
appSettings: .volatile(),
|
||||
|
||||
@@ -46,7 +46,7 @@ struct HomeScreenKnockedCell: View {
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
|
||||
private var mainContent: some View {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
@@ -86,7 +86,7 @@ struct HomeScreenKnockedCell: View {
|
||||
.fill(Color.compound.borderDisabled)
|
||||
.frame(height: 1 / UIScreen.main.scale)
|
||||
}
|
||||
|
||||
|
||||
private var title: String {
|
||||
room.name
|
||||
}
|
||||
@@ -131,7 +131,7 @@ struct HomeScreenKnockedCell_Previews: PreviewProvider, TestablePreview {
|
||||
let clientProxy = ClientProxyMock(.init())
|
||||
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
|
||||
|
||||
return HomeScreenViewModel(userSession: userSession,
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
||||
appSettings: .volatile(),
|
||||
|
||||
+4
-4
@@ -21,21 +21,21 @@ struct HomeScreenRecoveryKeyConfirmationBanner: View {
|
||||
case .recoveryOutOfSync: L10n.confirmRecoveryKeyBannerTitle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var message: String {
|
||||
switch state {
|
||||
case .setUpRecovery: L10n.bannerSetUpRecoveryContent
|
||||
case .recoveryOutOfSync: L10n.confirmRecoveryKeyBannerMessage
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var actionTitle: String {
|
||||
switch state {
|
||||
case .setUpRecovery: L10n.bannerSetUpRecoverySubmit
|
||||
case .recoveryOutOfSync: L10n.confirmRecoveryKeyBannerPrimaryButtonTitle
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var primaryAction: HomeScreenViewAction {
|
||||
switch state {
|
||||
case .setUpRecovery: .setupRecovery
|
||||
@@ -119,7 +119,7 @@ struct HomeScreenRecoveryKeyConfirmationBanner_Previews: PreviewProvider, Testab
|
||||
roomSummaryProvider: RoomSummaryProviderMock(.init(state: .loading))))
|
||||
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
|
||||
|
||||
return HomeScreenViewModel(userSession: userSession,
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
||||
appSettings: .volatile(),
|
||||
|
||||
@@ -140,7 +140,7 @@ struct HomeScreenRoomCell: View {
|
||||
CompoundIcon(\.videoCallSolid, size: .xSmall, relativeTo: .compound.bodySM)
|
||||
.accessibilityLabel(L10n.a11yNotificationsOngoingCall)
|
||||
}
|
||||
|
||||
|
||||
if room.badges.isMuteShown {
|
||||
CompoundIcon(\.notificationsOffSolid, size: .custom(15), relativeTo: .compound.bodyMD)
|
||||
.accessibilityLabel(L10n.a11yNotificationsMuted)
|
||||
@@ -159,7 +159,7 @@ struct HomeScreenRoomCell: View {
|
||||
.foregroundColor(room.isHighlighted ? .compound.iconAccentTertiary : .compound.iconQuaternary)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var mentionIcon: some View {
|
||||
CompoundIcon(\.mention, size: .custom(15), relativeTo: .compound.bodyMD)
|
||||
.accessibilityLabel(L10n.a11yNotificationsNewMentions)
|
||||
@@ -252,7 +252,7 @@ struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
|
||||
|
||||
static func makeViewModel(roomSummaryProvider: RoomSummaryProviderProtocol) -> HomeScreenViewModel {
|
||||
let userSession = UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "John Doe", roomSummaryProvider: roomSummaryProvider))))
|
||||
|
||||
|
||||
return HomeScreenViewModel(userSession: userSession,
|
||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
||||
appSettings: .volatile(),
|
||||
|
||||
@@ -37,9 +37,9 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
self.userDiscoveryService = userDiscoveryService
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.appSettings = appSettings
|
||||
|
||||
|
||||
let mandatoryInvitees: [UserProfileProxy] = if case .draft(let invitees) = roomType { invitees } else { [] }
|
||||
|
||||
|
||||
super.init(initialViewState: InviteUsersScreenViewState(selectedUsers: mandatoryInvitees,
|
||||
mandatoryInvitees: mandatoryInvitees,
|
||||
isSkippable: isSkippable),
|
||||
@@ -92,12 +92,12 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
toggleUser(user)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func toggleUser(_ user: UserProfileProxy) {
|
||||
guard !state.isInviteeMandatory(user) else { return }
|
||||
|
||||
|
||||
if state.selectedUsers.contains(user) {
|
||||
state.selectedUsers.removeAll { $0.userID == user.userID }
|
||||
} else {
|
||||
@@ -119,10 +119,10 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
|
||||
private func createDraftRoom(mandatoryUserIDs: [String]) {
|
||||
showLoadingIndicator(title: L10n.commonCreatingRoom)
|
||||
|
||||
|
||||
Task {
|
||||
defer { hideLoadingIndicator() }
|
||||
|
||||
|
||||
switch await clientProxy.createRoom(name: nil,
|
||||
topic: nil,
|
||||
accessType: .private,
|
||||
@@ -244,7 +244,7 @@ class InviteUsersScreenViewModel: InviteUsersScreenViewModelType, InviteUsersScr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var searchQuery: String {
|
||||
context.searchQuery
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ struct InviteUsersScreen: View {
|
||||
}
|
||||
|
||||
@ScaledMetric private var selectedUserCellWidth: CGFloat = 80
|
||||
|
||||
|
||||
private var selectedUsersSection: some View {
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
HStack(spacing: 8) {
|
||||
@@ -204,10 +204,10 @@ struct InviteUsersScreen_Previews: PreviewProvider, TestablePreview {
|
||||
isSkippable: Bool = true) -> InviteUsersScreenViewModel {
|
||||
let clientProxy = ClientProxyMock(.init())
|
||||
clientProxy.recentConversationCounterpartsReturnValue = [.mockAlice, .mockBob, .mockCharlie, .mockDan, .mockVerbose]
|
||||
|
||||
|
||||
let userDiscoveryService = UserDiscoveryServiceMock()
|
||||
userDiscoveryService.searchProfilesWithReturnValue = .success([.mockAlice])
|
||||
|
||||
|
||||
let viewModel = InviteUsersScreenViewModel(userSession: UserSessionMock(.init(clientProxy: clientProxy)),
|
||||
roomType: roomType ?? .existingRoom(roomProxy: JoinedRoomProxyMock(.init(members: []))),
|
||||
isSkippable: isSkippable,
|
||||
|
||||
@@ -45,7 +45,7 @@ final class JoinRoomScreenCoordinator: CoordinatorProtocol {
|
||||
private let viewModel: JoinRoomScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<JoinRoomScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<JoinRoomScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -78,7 +78,7 @@ final class JoinRoomScreenCoordinator: CoordinatorProtocol {
|
||||
func stop() {
|
||||
viewModel.stop()
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(JoinRoomScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ struct JoinRoomScreenViewState: BindableState {
|
||||
var mode: JoinRoomScreenMode = .loading
|
||||
|
||||
var hideInviteAvatars = false
|
||||
|
||||
|
||||
var bindings = JoinRoomScreenViewStateBindings()
|
||||
|
||||
var shouldHideAvatars: Bool {
|
||||
|
||||
@@ -27,7 +27,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
|
||||
var actionsPublisher: AnyPublisher<JoinRoomScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(source: JoinRoomScreenSource,
|
||||
appSettings: AppSettings,
|
||||
userSession: UserSessionProtocol,
|
||||
@@ -377,7 +377,7 @@ class JoinRoomScreenViewModel: JoinRoomScreenViewModelType, JoinRoomScreenViewMo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func showDeclineInviteConfirmationAlert() {
|
||||
guard let roomDetails = state.roomDetails else {
|
||||
userIndicatorController.submitIndicator(.init(title: L10n.errorUnknown))
|
||||
|
||||
@@ -26,7 +26,7 @@ struct JoinRoomScreen: View {
|
||||
}
|
||||
return context.viewState.mode == .knocked ? 151 : 32
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
FullscreenDialog(topPadding: topPadding) {
|
||||
if context.viewState.mode == .loading {
|
||||
@@ -130,7 +130,7 @@ struct JoinRoomScreen: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private var knockMessage: some View {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
HStack(spacing: 0) {
|
||||
@@ -404,7 +404,7 @@ struct JoinRoomScreenPreviewWrapper: Identifiable {
|
||||
self.customPreviewName = customPreviewName
|
||||
|
||||
let appSettings = AppSettings.volatile()
|
||||
|
||||
|
||||
let clientProxy = ClientProxyMock(.init(hideInviteAvatars: hideInviteAvatars))
|
||||
clientProxy.canJoinRoomWithReturnValue = canJoinRoom
|
||||
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ final class KnockRequestsListScreenCoordinator: CoordinatorProtocol {
|
||||
private let viewModel: KnockRequestsListScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<KnockRequestsListScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<KnockRequestsListScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -36,7 +36,7 @@ final class KnockRequestsListScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
|
||||
func start() { }
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(KnockRequestsListScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ struct KnockRequestsListScreenViewState: BindableState {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// If you are in this view one of these must have been true so by default we assume all of them to be true
|
||||
var canAccept = true
|
||||
var canDecline = true
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ class KnockRequestsListScreenViewModel: KnockRequestsListScreenViewModelType, Kn
|
||||
var actionsPublisher: AnyPublisher<KnockRequestsListScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(roomProxy: JoinedRoomProxyProtocol,
|
||||
mediaProvider: MediaProviderProtocol,
|
||||
userIndicatorController: UserIndicatorControllerProtocol) {
|
||||
|
||||
@@ -44,7 +44,7 @@ final class LabsScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(LabsScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ struct LabsScreenViewState: BindableState {
|
||||
@dynamicMemberLookup
|
||||
struct LabsScreenViewStateBindings {
|
||||
private let labsOptions: LabsOptionsProtocol
|
||||
|
||||
|
||||
init(labsOptions: LabsOptionsProtocol) {
|
||||
self.labsOptions = labsOptions
|
||||
}
|
||||
|
||||
|
||||
subscript<Setting>(dynamicMember keyPath: ReferenceWritableKeyPath<LabsOptionsProtocol, Setting>) -> Setting {
|
||||
get { labsOptions[keyPath: keyPath] }
|
||||
set { labsOptions[keyPath: keyPath] = newValue }
|
||||
|
||||
@@ -15,7 +15,7 @@ class LabsScreenViewModel: LabsScreenViewModelType, LabsScreenViewModelProtocol
|
||||
var actionsPublisher: AnyPublisher<LabsScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(labsOptions: LabsOptionsProtocol) {
|
||||
let bindings = LabsScreenViewStateBindings(labsOptions: labsOptions)
|
||||
let state = LabsScreenViewState(bindings: bindings)
|
||||
|
||||
@@ -65,7 +65,7 @@ struct LabsScreen: View {
|
||||
|
||||
struct LabsScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = LabsScreenViewModel(labsOptions: AppSettings.volatile())
|
||||
|
||||
|
||||
static var previews: some View {
|
||||
ElementNavigationStack {
|
||||
LabsScreen(context: viewModel.context)
|
||||
|
||||
@@ -24,7 +24,7 @@ final class LinkNewDeviceScreenCoordinator: CoordinatorProtocol {
|
||||
private let orientationManager: OrientationManagerProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<LinkNewDeviceScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<LinkNewDeviceScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -58,7 +58,7 @@ final class LinkNewDeviceScreenCoordinator: CoordinatorProtocol {
|
||||
func stop() {
|
||||
orientationManager.lockOrientation(.all)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(LinkNewDeviceScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ class LinkNewDeviceScreenViewModel: LinkNewDeviceScreenViewModelType, LinkNewDev
|
||||
var actionsPublisher: AnyPublisher<LinkNewDeviceScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(clientProxy: ClientProxyProtocol, initialState: LinkNewDeviceScreenViewState? = nil) {
|
||||
self.clientProxy = clientProxy
|
||||
|
||||
|
||||
@@ -28,12 +28,12 @@ enum LocationSharingInteractionMode: Hashable {
|
||||
case picker(shouldShowLiveLocationOption: Bool)
|
||||
case viewStatic(StaticLocationData)
|
||||
case viewLive(sender: TimelineItemSender?, initialLiveLocationShare: LiveLocationShare?)
|
||||
|
||||
|
||||
var isPicker: Bool {
|
||||
if case .picker = self { return true }
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
var shouldShowLiveLocationOption: Bool {
|
||||
if case .picker(let shouldShowLiveLocationOption) = self {
|
||||
return shouldShowLiveLocationOption
|
||||
@@ -73,7 +73,7 @@ struct LocationSharingScreenViewState: BindableState {
|
||||
case .viewStatic, .viewLive: .show
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let interactionMode: LocationSharingInteractionMode
|
||||
let mapURLBuilder: MapTilerURLBuilderProtocol
|
||||
let ownUserID: String
|
||||
@@ -112,7 +112,7 @@ struct LocationSharingScreenViewState: BindableState {
|
||||
}
|
||||
|
||||
var bindings = LocationSharingScreenBindings(showsUserLocationMode: .hide)
|
||||
|
||||
|
||||
/// Indicates whether the user is sharing his current location
|
||||
var isSharingUserLocation: Bool {
|
||||
bindings.isLocationAuthorized == true && bindings.showsUserLocationMode == .showAndFollow
|
||||
@@ -148,7 +148,7 @@ struct LocationSharingScreenViewState: BindableState {
|
||||
!bindings.hasLoadedUserLocation && bindings.isLocationAuthorized == true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var zoomLevel: Double {
|
||||
15.0
|
||||
}
|
||||
@@ -194,7 +194,7 @@ struct LocationSharingScreenBindings {
|
||||
|
||||
/// Information describing the currently displayed alert.
|
||||
var alertInfo: AlertInfo<LocationSharingViewAlert>?
|
||||
|
||||
|
||||
var sharedAnnotation: LocationAnnotation?
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ class LocationSharingScreenViewModel: LocationSharingScreenViewModelType, Locati
|
||||
self.analytics = analytics
|
||||
self.userIndicatorController = userIndicatorController
|
||||
self.notificationCenter = notificationCenter
|
||||
|
||||
|
||||
super.init(initialViewState: .init(interactionMode: interactionMode,
|
||||
mapURLBuilder: mapURLBuilder,
|
||||
ownUserID: roomProxy.ownUserID),
|
||||
@@ -338,7 +338,7 @@ extension LocationSharingScreenViewModel {
|
||||
case viewLive
|
||||
case viewLiveEmpty
|
||||
}
|
||||
|
||||
|
||||
static func mock(type: MockType,
|
||||
senderID: String = "@dan:matrix.org") -> LocationSharingScreenViewModel {
|
||||
let interactionMode: LocationSharingInteractionMode = switch type {
|
||||
@@ -388,7 +388,7 @@ extension LocationSharingScreenViewModel {
|
||||
let liveLocationServiceMock = RoomLiveLocationServiceMock(.init(shares: liveLocationShares))
|
||||
let roomProxy = JoinedRoomProxyMock(.init(members: .allMembers, ownUserID: RoomMemberProxyMock.mockMe.userID))
|
||||
roomProxy.makeLiveLocationServiceReturnValue = liveLocationServiceMock
|
||||
|
||||
|
||||
return LocationSharingScreenViewModel(interactionMode: interactionMode,
|
||||
mapURLBuilder: AppSettings.volatile().mapTilerConfiguration,
|
||||
roomProxy: roomProxy,
|
||||
|
||||
@@ -11,14 +11,14 @@ import SwiftUI
|
||||
struct LocationPickerSheet: View {
|
||||
@Bindable var context: LocationSharingScreenViewModel.Context
|
||||
@State private var height: CGFloat = .zero
|
||||
|
||||
|
||||
/// Fixes an iOS 26 sheet issue
|
||||
/// if the content doesn't meet a certain size
|
||||
/// additional insets are added.
|
||||
private var additionalHeight: CGFloat {
|
||||
context.viewState.interactionMode.shouldShowLiveLocationOption ? 0 : 28
|
||||
}
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
Text(L10n.screenSharingLocationOptionSheetTitle)
|
||||
|
||||
@@ -44,7 +44,7 @@ struct LocationShareSheet_Previews: PreviewProvider {
|
||||
static let annotation = LocationAnnotation(id: profile.userID,
|
||||
coordinate: .init(latitude: 51.509865, longitude: -0.118092),
|
||||
kind: .liveUser(profile))
|
||||
|
||||
|
||||
static var previews: some View {
|
||||
LocationShareSheet(annotation: annotation)
|
||||
.previewDisplayName("Live location share sheet")
|
||||
|
||||
@@ -126,26 +126,26 @@ struct LocationSharingScreen: View {
|
||||
|
||||
struct LocationSharingScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = LocationSharingScreenViewModel.mock(type: .staticSenderLocation)
|
||||
|
||||
|
||||
static let withoutLiveSharingViewModel = LocationSharingScreenViewModel.mock(type: .pickerWithoutLiveLocationOption)
|
||||
|
||||
|
||||
static let pinViewModel = LocationSharingScreenViewModel.mock(type: .staticPinLocation)
|
||||
|
||||
|
||||
static let pickerViewModel = LocationSharingScreenViewModel.mock(type: .picker)
|
||||
|
||||
|
||||
static let liveLocationViewModel = LocationSharingScreenViewModel.mock(type: .viewLive)
|
||||
|
||||
|
||||
static var previews: some View {
|
||||
ElementNavigationStack {
|
||||
LocationSharingScreen(context: pickerViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Picker")
|
||||
|
||||
|
||||
ElementNavigationStack {
|
||||
LocationSharingScreen(context: withoutLiveSharingViewModel.context)
|
||||
}
|
||||
.previewDisplayName("Picker without live location sharing")
|
||||
|
||||
|
||||
ElementNavigationStack {
|
||||
LocationSharingScreen(context: viewModel.context)
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ final class LogViewerScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(LogViewerScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ class LogViewerScreenViewModel: LogViewerScreenViewModelType, LogViewerScreenVie
|
||||
var actions: AnyPublisher<LogViewerScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init() {
|
||||
super.init(initialViewState: LogViewerScreenViewState(urls: Tracing.logFiles))
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ struct LogViewerScreen: View {
|
||||
|
||||
private struct PreviewView: UIViewControllerRepresentable {
|
||||
let urls: [URL]
|
||||
|
||||
|
||||
func makeUIViewController(context: Context) -> UIViewController {
|
||||
let previewController = QLPreviewController()
|
||||
previewController.dataSource = context.coordinator
|
||||
@@ -28,16 +28,16 @@ private struct PreviewView: UIViewControllerRepresentable {
|
||||
|
||||
return UINavigationController(rootViewController: previewController)
|
||||
}
|
||||
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIViewController, context: Context) { }
|
||||
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(view: self)
|
||||
}
|
||||
|
||||
class Coordinator: NSObject, QLPreviewControllerDataSource, QLPreviewControllerDelegate {
|
||||
let view: PreviewView
|
||||
|
||||
|
||||
init(view: PreviewView) {
|
||||
self.view = view
|
||||
}
|
||||
@@ -47,7 +47,7 @@ private struct PreviewView: UIViewControllerRepresentable {
|
||||
func numberOfPreviewItems(in controller: QLPreviewController) -> Int {
|
||||
view.urls.count
|
||||
}
|
||||
|
||||
|
||||
func previewController(_ controller: QLPreviewController, previewItemAt index: Int) -> QLPreviewItem {
|
||||
let url = view.urls[index]
|
||||
|
||||
@@ -65,7 +65,7 @@ private struct PreviewView: UIViewControllerRepresentable {
|
||||
private class PreviewItem: NSObject, QLPreviewItem {
|
||||
var previewItemURL: URL?
|
||||
var previewItemTitle: String?
|
||||
|
||||
|
||||
init(previewItemURL: URL?, previewItemTitle: String?) {
|
||||
self.previewItemURL = previewItemURL
|
||||
self.previewItemTitle = previewItemTitle
|
||||
|
||||
+2
-2
@@ -23,7 +23,7 @@ final class ManageAuthorizedSpacesScreenCoordinator: CoordinatorProtocol {
|
||||
private let viewModel: ManageAuthorizedSpacesScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
|
||||
private let actionsSubject: PassthroughSubject<ManageAuthorizedSpacesScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<ManageAuthorizedSpacesScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
@@ -46,7 +46,7 @@ final class ManageAuthorizedSpacesScreenCoordinator: CoordinatorProtocol {
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(ManageAuthorizedSpacesScreen(context: viewModel.context))
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ class ManageAuthorizedSpacesScreenViewModel: ManageAuthorizedSpacesScreenViewMod
|
||||
var actionsPublisher: AnyPublisher<ManageAuthorizedSpacesScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
|
||||
init(authorizedSpacesSelection: AuthorizedSpacesSelection,
|
||||
mediaProvider: MediaProviderProtocol) {
|
||||
super.init(initialViewState: ManageAuthorizedSpacesScreenViewState(authorizedSpacesSelection: authorizedSpacesSelection),
|
||||
|
||||
+2
-2
@@ -54,7 +54,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
||||
}
|
||||
|
||||
super.init(initialViewState: .init(activeTimelineContext: activeTimelineContext, bindings: .init(screenMode: initialScreenMode)), mediaProvider: mediaProvider)
|
||||
|
||||
|
||||
mediaTimelineViewModel.context.$viewState.sink { [weak self] timelineViewState in
|
||||
guard let self, state.bindings.screenMode == .media else {
|
||||
return
|
||||
@@ -201,7 +201,7 @@ class MediaEventsTimelineScreenViewModel: MediaEventsTimelineScreenViewModelType
|
||||
items: currentItems)
|
||||
newGroups.append(group)
|
||||
}
|
||||
|
||||
|
||||
state.groups = newGroups
|
||||
|
||||
state.isBackPaginating = timelineViewState.timelineState.paginationState.backward == .paginating
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user