From a31475e2f5d59934418d58b996c63bfabb37d9e8 Mon Sep 17 00:00:00 2001 From: Letro Bot Date: Tue, 14 Apr 2026 16:58:33 +0400 Subject: [PATCH 1/5] Update letro colors --- .../Letro/Extensions/CompoundExtensions.swift | 30 +++++++++---------- .../Letro/Extensions/SwiftUIExtensions.swift | 17 +++++++++-- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift b/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift index 60f433d82..e70db352a 100644 --- a/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift +++ b/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift @@ -20,28 +20,26 @@ enum CompoundExtensions { extension CompoundColors { @MainActor func applyLetroOverrides() { - override(\.gradientSubtleStop1, with: CompoundCoreColorTokens.alphaOrange500) - override(\.gradientSubtleStop2, with: CompoundCoreColorTokens.alphaOrange400) - override(\.gradientSubtleStop3, with: CompoundCoreColorTokens.alphaOrange300) - override(\.gradientSubtleStop4, with: CompoundCoreColorTokens.alphaOrange200) - override(\.gradientSubtleStop5, with: CompoundCoreColorTokens.alphaOrange100) - override(\.gradientSubtleStop6, with: CompoundCoreColorTokens.transparent) - override(\.iconAccentTertiary, with: CompoundCoreColorTokens.orange800) - override(\.bgAccentRest, with: CompoundCoreColorTokens.orange900) + override(\.gradientSubtleStop1, with: .letroGradient1.opacity(0.33)) + override(\.gradientSubtleStop2, with: .letroGradient2.opacity(0.22)) + override(\.gradientSubtleStop3, with: .letroGradient3.opacity(0.11)) + override(\.gradientSubtleStop4, with: .letroGradient4.opacity(0.07)) + override(\.gradientSubtleStop5, with: .letroGradient5.opacity(0.04)) + override(\.iconAccentTertiary, with: .letroPrimary) + override(\.bgAccentRest, with: .letroPrimary) } } extension CompoundUIColors { @MainActor func applyLetroOverrides() { - override(\.gradientSubtleStop1, with: CompoundCoreUIColorTokens.alphaOrange500) - override(\.gradientSubtleStop2, with: CompoundCoreUIColorTokens.alphaOrange400) - override(\.gradientSubtleStop3, with: CompoundCoreUIColorTokens.alphaOrange300) - override(\.gradientSubtleStop4, with: CompoundCoreUIColorTokens.alphaOrange200) - override(\.gradientSubtleStop5, with: CompoundCoreUIColorTokens.alphaOrange100) - override(\.gradientSubtleStop6, with: CompoundCoreUIColorTokens.transparent) - override(\.iconAccentTertiary, with: CompoundCoreUIColorTokens.orange800) - override(\.bgAccentRest, with: CompoundCoreUIColorTokens.orange900) + override(\.gradientSubtleStop1, with: .letroGradient1.withAlphaComponent(0.33)) + override(\.gradientSubtleStop2, with: .letroGradient2.withAlphaComponent(0.22)) + override(\.gradientSubtleStop3, with: .letroGradient3.withAlphaComponent(0.11)) + override(\.gradientSubtleStop4, with: .letroGradient4.withAlphaComponent(0.07)) + override(\.gradientSubtleStop5, with: .letroGradient5.withAlphaComponent(0.04)) + override(\.iconAccentTertiary, with: .letroPrimary) + override(\.bgAccentRest, with: .letroPrimary) } } diff --git a/ElementX/Sources/Letro/Extensions/SwiftUIExtensions.swift b/ElementX/Sources/Letro/Extensions/SwiftUIExtensions.swift index 462545d75..8d9c6534d 100644 --- a/ElementX/Sources/Letro/Extensions/SwiftUIExtensions.swift +++ b/ElementX/Sources/Letro/Extensions/SwiftUIExtensions.swift @@ -9,8 +9,15 @@ import SwiftUI import UIKit public extension Color { - static let letroPrimary = Color(hex: 0xC20000) + static let letroPrimary = Color(hex: 0xF32D1B) + static let letroSecondary = Color(hex: 0xF9BC15) static let letroGray = Color(hex: 0xB1B3B9) + static let letroGradient1 = Color(hex: 0xCB2000) + static let letroGradient2 = Color(hex: 0xD33900) + static let letroGradient3 = Color(hex: 0xDD5A00) + static let letroGradient4 = Color(hex: 0xE98200) + static let letroGradient5 = Color(hex: 0xF7B000) + init(hex: UInt32) { self.init(.sRGB, red: Double((hex >> 16) & 0xFF) / 255.0, green: Double((hex >> 8) & 0xFF) / 255.0, blue: Double(hex & 0xFF) / 255.0, opacity: 1) @@ -18,8 +25,14 @@ public extension Color { } public extension UIColor { - static let letroPrimary = UIColor(hex: 0xC20000) + static let letroPrimary = UIColor(hex: 0xF32D1B) + static let letroSecondary = UIColor(hex: 0xF9BC15) static let letroGray = UIColor(hex: 0xB1B3B9) + static let letroGradient1 = UIColor(hex: 0xCB2000) + static let letroGradient2 = UIColor(hex: 0xD33900) + static let letroGradient3 = UIColor(hex: 0xDD5A00) + static let letroGradient4 = UIColor(hex: 0xE98200) + static let letroGradient5 = UIColor(hex: 0xF7B000) convenience init(hex: UInt32) { self.init(red: CGFloat((hex >> 16) & 0xFF) / 255.0, green: CGFloat((hex >> 8) & 0xFF) / 255.0, blue: CGFloat(hex & 0xFF) / 255.0, alpha: 1) From 6774697c1744bdba43e5d8ab339a1005f45ebac4 Mon Sep 17 00:00:00 2001 From: Letro Bot Date: Tue, 14 Apr 2026 17:40:16 +0400 Subject: [PATCH 2/5] Change a few links in AppSettings --- .../Sources/Application/Settings/AppSettings.swift | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/ElementX/Sources/Application/Settings/AppSettings.swift b/ElementX/Sources/Application/Settings/AppSettings.swift index ba61f7900..9f852356e 100644 --- a/ElementX/Sources/Application/Settings/AppSettings.swift +++ b/ElementX/Sources/Application/Settings/AppSettings.swift @@ -224,15 +224,15 @@ final class AppSettings { /// A URL where users can go read more about identity pinning violations private(set) var identityPinningViolationDetailsURL: URL = "https://letro.com/help#encryption18" /// A URL describing how history sharing works - private(set) var historySharingDetailsURL: URL = "https://letro.com/en/help#e2ee-history-sharing" + private(set) var historySharingDetailsURL: URL = "https://letro.com/help#e2ee-history-sharing" /// Any domains that Element web may be hosted on - used for handling links. - private(set) var elementWebHosts = ["app.element.io", "staging.element.io", "develop.element.io"] + private(set) var elementWebHosts = ["app.letro.com", "staging.letro.com", "develop.letro.com"] /// The domain that account provisioning links will be hosted on - used for handling the links. - private(set) var accountProvisioningHost = "mobile.element.io" + private(set) var accountProvisioningHost = "mobile.letro.com" /// The App Store URL for Element Pro, shown to the user when a homeserver requires that app. /// **Note:** This property isn't overridable as it in unexpected for forks to come across the error (or to even have a "Pro" app). - let elementProAppStoreURL: URL = "https://apps.apple.com/app/element-pro-for-work/id6502951615" + let elementProAppStoreURL: URL = "https://apps.apple.com/app/letro-pro/id6758909629" @UserPreference(key: UserDefaultsKeys.appAppearance, defaultValue: .system, storageType: .userDefaults(store)) var appAppearance: AppAppearance @@ -279,7 +279,7 @@ final class AppSettings { #endif } - private(set) var pushGatewayBaseURL: URL = "https://matrix.org" + private(set) var pushGatewayBaseURL: URL = "https://letro.com" var pushGatewayNotifyEndpoint: URL { pushGatewayBaseURL.appending(path: "_matrix/push/v1/notify") } @@ -318,14 +318,14 @@ final class AppSettings { let bugReportSentryURL: URL? = Secrets.sentryDSN.map { URL(string: $0)! } // swiftlint:disable:this force_unwrapping let bugReportSentryRustURL: URL? = Secrets.sentryRustDSN.map { URL(string: $0)! } // swiftlint:disable:this force_unwrapping /// The name allocated by the bug report server - private(set) var bugReportApplicationID = "element-x-ios" + private(set) var bugReportApplicationID = "letro" // MARK: - Analytics /// The configuration to use for analytics. Set to `nil` to disable analytics. let analyticsConfiguration: AnalyticsConfiguration? = AppSettings.makeAnalyticsConfiguration() /// The URL to open with more information about analytics terms. When this is `nil` the "Learn more" link will be hidden. - private(set) var analyticsTermsURL: URL? = "https://element.io/cookie-policy" + private(set) var analyticsTermsURL: URL? = "https://letro.com/cookie-policy" /// Whether or not there the app is able ask for user consent to enable analytics or sentry reporting. var canPromptForAnalytics: Bool { analyticsConfiguration != nil || bugReportSentryURL != nil From 1f944c7e840d91cb3da13b0fbadfd1cd8e53a6c6 Mon Sep 17 00:00:00 2001 From: Letro Bot Date: Tue, 14 Apr 2026 17:52:08 +0400 Subject: [PATCH 3/5] Customize a few more strings, Fix an issue with stringdict customations --- .../Localizations/en.lproj/Letro.strings | 3 +- .../Localizations/en.lproj/Letro.stringsdict | 400 +++++------------- 2 files changed, 118 insertions(+), 285 deletions(-) diff --git a/ElementX/Resources/Localizations/en.lproj/Letro.strings b/ElementX/Resources/Localizations/en.lproj/Letro.strings index 3e16ab116..12c0f18c8 100644 --- a/ElementX/Resources/Localizations/en.lproj/Letro.strings +++ b/ElementX/Resources/Localizations/en.lproj/Letro.strings @@ -154,6 +154,7 @@ "screen_room_timeline_tombstoned_room_message" = "This silo has been replaced and is no longer active"; "screen_room_timeline_upgraded_room_message" = "This silo is a continuation of another silo"; "screen_roomlist_a11y_create_message" = "Create a new conversation or silo"; +"screen_roomlist_empty_title" = ""; "screen_roomlist_filter_people" = "Directs"; "screen_roomlist_filter_rooms" = "Silos"; "screen_roomlist_filter_rooms_empty_state_title" = "You’re not in any silo yet"; @@ -183,7 +184,7 @@ "screen_space_announcement_title" = "Introducing Workspaces"; "screen_space_empty_state_title" = "Add your first silo"; "screen_space_list_description" = "Workspaces you have created or joined."; -"screen_space_list_empty_state_title" = "Create spaces to organize silos"; +"screen_space_list_empty_state_title" = "Create workspaces to organize your desk"; "screen_space_list_parent_space" = "%1$@ workspace"; "screen_space_list_title" = "Workspaces"; "screen_space_remove_rooms_confirmation_content" = "Removing a silo will not affect the silo access. To change the access go to Silo info > Privacy & security."; diff --git a/ElementX/Resources/Localizations/en.lproj/Letro.stringsdict b/ElementX/Resources/Localizations/en.lproj/Letro.stringsdict index 52d6c493f..56f065596 100644 --- a/ElementX/Resources/Localizations/en.lproj/Letro.stringsdict +++ b/ElementX/Resources/Localizations/en.lproj/Letro.stringsdict @@ -1,286 +1,118 @@ - + - - - - common_spaces - - - - NSStringLocalizedFormatKey - - - %#@COUNT@ - - - COUNT - - - - NSStringFormatSpecTypeKey - - - NSStringPluralRuleType - - - NSStringFormatValueTypeKey - - - d - - - one - - - %1$d Workspace - - - other - - - %1$d Workspaces - - - - - screen_leave_space_submit - - - - NSStringLocalizedFormatKey - - - %#@COUNT@ - - - COUNT - - - - NSStringFormatSpecTypeKey - - - NSStringPluralRuleType - - - NSStringFormatValueTypeKey - - - d - - - one - - - Leave %1$d silo and space - - - other - - - Leave %1$d silos and space - - - - - common_rooms - - - - NSStringLocalizedFormatKey - - - %#@COUNT@ - - - COUNT - - - - NSStringFormatSpecTypeKey - - - NSStringPluralRuleType - - - NSStringFormatValueTypeKey - - - d - - - one - - - %1$d Silo - - - other - - - %1$d Silos - - - - - notification_unread_notified_messages_in_room_rooms - - - - NSStringLocalizedFormatKey - - - %#@COUNT@ - - - COUNT - - - - NSStringFormatSpecTypeKey - - - NSStringPluralRuleType - - - NSStringFormatValueTypeKey - - - d - - - one - - - %d silo - - - other - - - %d silos - - - - - screen_room_multiple_knock_requests_title - - - - NSStringLocalizedFormatKey - - - %#@COUNT@ - - - COUNT - - - - NSStringFormatSpecTypeKey - - - NSStringPluralRuleType - - - NSStringFormatValueTypeKey - - - d - - - one - - - %1$@ +%2$d other want to join this silo - - - other - - - %1$@ +%2$d others want to join this silo - - - - - screen_room_timeline_state_changes - - - - NSStringLocalizedFormatKey - - - %#@COUNT@ - - - COUNT - - - - NSStringFormatSpecTypeKey - - - NSStringPluralRuleType - - - NSStringFormatValueTypeKey - - - d - - - one - - - %1$d change - - - other - - - %1$d changes - - - - - screen_space_remove_rooms_confirmation_title - - - - NSStringLocalizedFormatKey - - - %#@COUNT@ - - - COUNT - - - - NSStringFormatSpecTypeKey - - - NSStringPluralRuleType - - - NSStringFormatValueTypeKey - - - d - - - one - - - Remove %1$d silo from %2$@ - - - other - - - Remove %1$d silos from %2$@ - - - - + + + common_rooms + + NSStringLocalizedFormatKey + %#@COUNT@ + COUNT + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %1$d Silo + other + %1$d Silos + + + common_spaces + + NSStringLocalizedFormatKey + %#@COUNT@ + COUNT + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %1$d Workspace + other + %1$d Workspaces + + + notification_unread_notified_messages_in_room_rooms + + NSStringLocalizedFormatKey + %#@COUNT@ + COUNT + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %d silo + other + %d silos + + + screen_leave_space_submit + + NSStringLocalizedFormatKey + %#@COUNT@ + COUNT + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Leave %1$d silo and space + other + Leave %1$d silos and space + + + screen_room_multiple_knock_requests_title + + NSStringLocalizedFormatKey + %#@COUNT@ + COUNT + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %1$@ +%2$d other want to join this silo + other + %1$@ +%2$d others want to join this silo + + + screen_room_timeline_state_changes + + NSStringLocalizedFormatKey + %#@COUNT@ + COUNT + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + %1$d change + other + %1$d changes + + + screen_space_remove_rooms_confirmation_title + + NSStringLocalizedFormatKey + %#@COUNT@ + COUNT + + NSStringFormatSpecTypeKey + NSStringPluralRuleType + NSStringFormatValueTypeKey + d + one + Remove %1$d silo from %2$@ + other + Remove %1$d silos from %2$@ + + + From d3083e7ed59d7ab07ffebe7f68009d13d994364b Mon Sep 17 00:00:00 2001 From: Letro Bot Date: Tue, 14 Apr 2026 19:58:47 +0400 Subject: [PATCH 4/5] Add the missing NSLocationAlwaysAndWhenInUseUsageDescription in target.yml since latest upstream rebase --- ElementX/SupportingFiles/Info.plist | 2 ++ ElementX/SupportingFiles/target.yml | 1 + 2 files changed, 3 insertions(+) diff --git a/ElementX/SupportingFiles/Info.plist b/ElementX/SupportingFiles/Info.plist index d0a8ac9a7..714655422 100644 --- a/ElementX/SupportingFiles/Info.plist +++ b/ElementX/SupportingFiles/Info.plist @@ -69,6 +69,8 @@ To take pictures or videos and send them as a message $(APP_DISPLAY_NAME) needs access to the camera. NSFaceIDUsageDescription Face ID is used to access your app. + NSLocationAlwaysAndWhenInUseUsageDescription + To share your live location, $(APP_DISPLAY_NAME) needs location access when the app is in the background. NSLocationWhenInUseUsageDescription Grant location access so that $(APP_DISPLAY_NAME) can share your location. NSMicrophoneUsageDescription diff --git a/ElementX/SupportingFiles/target.yml b/ElementX/SupportingFiles/target.yml index ded680685..a8f99e15c 100644 --- a/ElementX/SupportingFiles/target.yml +++ b/ElementX/SupportingFiles/target.yml @@ -88,6 +88,7 @@ targets: NSCameraUsageDescription: To take pictures or videos and send them as a message $(APP_DISPLAY_NAME) needs access to the camera. NSMicrophoneUsageDescription: To record and send messages with audio, $(APP_DISPLAY_NAME) needs to access the microphone. NSPhotoLibraryAddUsageDescription: This lets you save images and videos to your photo library. + NSLocationAlwaysAndWhenInUseUsageDescription: To share your live location, $(APP_DISPLAY_NAME) needs location access when the app is in the background. NSLocationWhenInUseUsageDescription: Grant location access so that $(APP_DISPLAY_NAME) can share your location. NSFaceIDUsageDescription: Face ID is used to access your app. UIBackgroundModes: [ From 303f58f94df32af9ac76f584ce76b9bae908aac0 Mon Sep 17 00:00:00 2001 From: Letro Bot Date: Thu, 16 Apr 2026 21:57:56 +0400 Subject: [PATCH 5/5] Customize action buttons to use letro gradient --- .../Letro/Extensions/CompoundExtensions.swift | 16 ++++++++++++++++ .../BaseStyles/CompoundButtonStyle.swift | 16 +++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift b/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift index e70db352a..45c788768 100644 --- a/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift +++ b/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift @@ -20,26 +20,42 @@ enum CompoundExtensions { extension CompoundColors { @MainActor func applyLetroOverrides() { + override(\.gradientActionStop1, with: .letroGradient1) + override(\.gradientActionStop2, with: .letroGradient2) + override(\.gradientActionStop3, with: .letroGradient3) + override(\.gradientActionStop4, with: .letroGradient5) override(\.gradientSubtleStop1, with: .letroGradient1.opacity(0.33)) override(\.gradientSubtleStop2, with: .letroGradient2.opacity(0.22)) override(\.gradientSubtleStop3, with: .letroGradient3.opacity(0.11)) override(\.gradientSubtleStop4, with: .letroGradient4.opacity(0.07)) override(\.gradientSubtleStop5, with: .letroGradient5.opacity(0.04)) + override(\.bgActionPrimaryRest, with: .letroPrimary) + override(\.bgActionPrimaryPressed, with: .letroGradient1) + override(\.bgActionPrimaryDisabled, with: .letroGray.opacity(0.4)) override(\.iconAccentTertiary, with: .letroPrimary) override(\.bgAccentRest, with: .letroPrimary) + override(\.textActionPrimary, with: .letroPrimary) } } extension CompoundUIColors { @MainActor func applyLetroOverrides() { + override(\.gradientActionStop1, with: .letroGradient1) + override(\.gradientActionStop2, with: .letroGradient2) + override(\.gradientActionStop3, with: .letroGradient3) + override(\.gradientActionStop4, with: .letroGradient5) override(\.gradientSubtleStop1, with: .letroGradient1.withAlphaComponent(0.33)) override(\.gradientSubtleStop2, with: .letroGradient2.withAlphaComponent(0.22)) override(\.gradientSubtleStop3, with: .letroGradient3.withAlphaComponent(0.11)) override(\.gradientSubtleStop4, with: .letroGradient4.withAlphaComponent(0.07)) override(\.gradientSubtleStop5, with: .letroGradient5.withAlphaComponent(0.04)) + override(\.bgActionPrimaryRest, with: .letroPrimary) + override(\.bgActionPrimaryPressed, with: .letroGradient1) + override(\.bgActionPrimaryDisabled, with: .letroGray.withAlphaComponent(0.4)) override(\.iconAccentTertiary, with: .letroPrimary) override(\.bgAccentRest, with: .letroPrimary) + override(\.textActionPrimary, with: .letroPrimary) } } diff --git a/compound-ios/Sources/Compound/BaseStyles/CompoundButtonStyle.swift b/compound-ios/Sources/Compound/BaseStyles/CompoundButtonStyle.swift index 680cb3645..d3dd229c5 100644 --- a/compound-ios/Sources/Compound/BaseStyles/CompoundButtonStyle.swift +++ b/compound-ios/Sources/Compound/BaseStyles/CompoundButtonStyle.swift @@ -138,7 +138,7 @@ public struct CompoundButtonStyle: ButtonStyle { Capsule().strokeBorder(strokeColor(configuration: configuration)) } case .primary: - Capsule().fill(fillColor(configuration: configuration)) + makePrimaryBackground(configuration: configuration) case .secondary: Capsule().strokeBorder(strokeColor(configuration: configuration)) case .tertiary: @@ -148,6 +148,20 @@ public struct CompoundButtonStyle: ButtonStyle { } } + // Letro: custom action buttons + @ViewBuilder + private func makePrimaryBackground(configuration: Self.Configuration) -> some View { + if !isEnabled || configuration.role == .destructive { + Capsule().fill(fillColor(configuration: configuration)) + } else { + Capsule() + .fill(LinearGradient(gradient: .compound.action, + startPoint: .bottomLeading, + endPoint: .topTrailing)) + .opacity(configuration.isPressed ? pressedOpacity : 1) + } + } + private var contentShape: AnyShape { switch kind { case .super, .primary, .secondary, .tertiary: