diff --git a/ElementX.xcodeproj/project.pbxproj b/ElementX.xcodeproj/project.pbxproj index c1a3c3f97..60cc8ea99 100644 --- a/ElementX.xcodeproj/project.pbxproj +++ b/ElementX.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 63; + objectVersion = 70; objects = { /* Begin PBXAggregateTarget section */ @@ -2971,6 +2971,10 @@ FFECCE59967018204876D0A5 /* LocationMarkerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationMarkerView.swift; sourceTree = ""; }; /* End PBXFileReference section */ +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 0F5E53BB2F7D3AA5008BB162 /* Letro */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = Letro; sourceTree = ""; }; +/* End PBXFileSystemSynchronizedRootGroup section */ + /* Begin PBXFrameworksBuildPhase section */ 60823A8E409E27661824D510 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; @@ -6616,6 +6620,7 @@ E68740F873AB18A5C26844EA /* Sources */ = { isa = PBXGroup; children = ( + 0F5E53BB2F7D3AA5008BB162 /* Letro */, A78C2592419CA4C76FBA8FD2 /* Application */, 0787F81684E503024BD0C051 /* Services */, 593C7129C5927E25AD8B688F /* FlowCoordinators */, @@ -7115,6 +7120,9 @@ 58C473A5DEA945AACFEA8E9F /* PBXTargetDependency */, 2E32BC489F482046B8B1460F /* PBXTargetDependency */, ); + fileSystemSynchronizedGroups = ( + 0F5E53BB2F7D3AA5008BB162 /* Letro */, + ); name = ElementX; packageProductDependencies = ( A678E40E917620059695F067 /* MatrixRustSDK */, diff --git a/ElementX/Resources/Assets.xcassets/images/letro-icon-chat.imageset/Contents.json b/ElementX/Resources/Assets.xcassets/images/letro-icon-chat.imageset/Contents.json new file mode 100644 index 000000000..2891e8282 --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/images/letro-icon-chat.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "letro-icon-chat.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElementX/Resources/Assets.xcassets/images/letro-icon-chat.imageset/letro-icon-chat.svg b/ElementX/Resources/Assets.xcassets/images/letro-icon-chat.imageset/letro-icon-chat.svg new file mode 100644 index 000000000..dbd85a74f --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/images/letro-icon-chat.imageset/letro-icon-chat.svg @@ -0,0 +1,3 @@ + + + diff --git a/ElementX/Resources/Assets.xcassets/images/letro-icon-chatSolid.imageset/Contents.json b/ElementX/Resources/Assets.xcassets/images/letro-icon-chatSolid.imageset/Contents.json new file mode 100644 index 000000000..297adfcf7 --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/images/letro-icon-chatSolid.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "letro-icon-chatSolid.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElementX/Resources/Assets.xcassets/images/letro-icon-chatSolid.imageset/letro-icon-chatSolid.svg b/ElementX/Resources/Assets.xcassets/images/letro-icon-chatSolid.imageset/letro-icon-chatSolid.svg new file mode 100644 index 000000000..f0a010478 --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/images/letro-icon-chatSolid.imageset/letro-icon-chatSolid.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/ElementX/Resources/Assets.xcassets/images/letro-icon-space.imageset/Contents.json b/ElementX/Resources/Assets.xcassets/images/letro-icon-space.imageset/Contents.json new file mode 100644 index 000000000..aa69cd2fb --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/images/letro-icon-space.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "letro-icon-space.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElementX/Resources/Assets.xcassets/images/letro-icon-space.imageset/letro-icon-space.svg b/ElementX/Resources/Assets.xcassets/images/letro-icon-space.imageset/letro-icon-space.svg new file mode 100644 index 000000000..8f359748b --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/images/letro-icon-space.imageset/letro-icon-space.svg @@ -0,0 +1,3 @@ + + + diff --git a/ElementX/Resources/Assets.xcassets/images/letro-icon-spaceSolid.imageset/Contents.json b/ElementX/Resources/Assets.xcassets/images/letro-icon-spaceSolid.imageset/Contents.json new file mode 100644 index 000000000..20bd30063 --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/images/letro-icon-spaceSolid.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "letro-icon-spaceSolid.svg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/ElementX/Resources/Assets.xcassets/images/letro-icon-spaceSolid.imageset/letro-icon-spaceSolid.svg b/ElementX/Resources/Assets.xcassets/images/letro-icon-spaceSolid.imageset/letro-icon-spaceSolid.svg new file mode 100644 index 000000000..f9764810b --- /dev/null +++ b/ElementX/Resources/Assets.xcassets/images/letro-icon-spaceSolid.imageset/letro-icon-spaceSolid.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ElementX/Sources/Application/Application.swift b/ElementX/Sources/Application/Application.swift index 308891af4..512f0702a 100644 --- a/ElementX/Sources/Application/Application.swift +++ b/ElementX/Sources/Application/Application.swift @@ -6,6 +6,7 @@ // Please see LICENSE files in the repository root for full details. // +import Compound import SwiftUI @main @@ -16,6 +17,9 @@ struct Application: App { private var appCoordinator: AppCoordinatorProtocol! init() { + // Letro: use custom colors and icons + CompoundExtensions.applyLetroOverrides() + if ProcessInfo.isRunningUITests { appCoordinator = UITestsAppCoordinator(appDelegate: appDelegate) } else if ProcessInfo.isRunningUnitTests { diff --git a/ElementX/Sources/Generated/Assets.swift b/ElementX/Sources/Generated/Assets.swift index 049ed8867..e7539ec9e 100644 --- a/ElementX/Sources/Generated/Assets.swift +++ b/ElementX/Sources/Generated/Assets.swift @@ -35,6 +35,10 @@ internal enum Asset { internal static let letroBg1 = ImageAsset(name: "images/letro-bg-1") internal static let letroHeader1 = ImageAsset(name: "images/letro-header-1") internal static let letroHeader2 = ImageAsset(name: "images/letro-header-2") + internal static let letroIconChat = ImageAsset(name: "images/letro-icon-chat") + internal static let letroIconChatSolid = ImageAsset(name: "images/letro-icon-chatSolid") + internal static let letroIconSpace = ImageAsset(name: "images/letro-icon-space") + internal static let letroIconSpaceSolid = ImageAsset(name: "images/letro-icon-spaceSolid") internal static let letroLogoType = ImageAsset(name: "images/letro-logo-type") internal static let locationMarkerShape = ImageAsset(name: "images/location-marker-shape") internal static let mapBlurred = ImageAsset(name: "images/mapBlurred") diff --git a/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift b/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift new file mode 100644 index 000000000..7a989a837 --- /dev/null +++ b/ElementX/Sources/Letro/Extensions/CompoundExtensions.swift @@ -0,0 +1,52 @@ +// +// Copyright 2026 Element Creations Ltd. +// +// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. +// Please see LICENSE files in the repository root for full details. +// +import Compound +import CompoundDesignTokens +import SwiftUI + +enum CompoundExtensions { + @MainActor + static func applyLetroOverrides() { + Color.compound.applyLetroOverrides() + UIColor.compound.applyLetroOverrides() + CompoundIcon.applyLetroOverrides() + } +} + +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) + } +} + +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) + } +} + +extension CompoundIcon { + @MainActor + static func applyLetroOverrides() { + override(\.chat, with: Image("images/letro-icon-chat")) + override(\.chatSolid, with: Image("images/letro-icon-chatSolid")) + override(\.space, with: Image("images/letro-icon-space")) + override(\.spaceSolid, with: Image("images/letro-icon-spaceSolid")) + } +} diff --git a/compound-ios/Sources/Compound/Icons/CompoundIcon.swift b/compound-ios/Sources/Compound/Icons/CompoundIcon.swift index 06b0f3159..17146a237 100644 --- a/compound-ios/Sources/Compound/Icons/CompoundIcon.swift +++ b/compound-ios/Sources/Compound/Icons/CompoundIcon.swift @@ -51,7 +51,9 @@ public struct CompoundIcon: View { /// - Parameters: /// - icon: The icon to show. public init(_ icon: KeyPath) { - image = .compound[keyPath: icon] + // Letro: Add a mechanism to override icons + image = Self.resolve(icon) + size = .medium font = .compound.bodyLG } @@ -63,7 +65,9 @@ public struct CompoundIcon: View { /// - size: The size of the icon. /// - font: The font that should be used for scaling with Dynamic Type. public init(_ icon: KeyPath, size: Size, relativeTo font: Font) { - image = .compound[keyPath: icon] + // Letro: Add a mechanism to override icons + image = Self.resolve(icon) + self.size = size self.font = font } @@ -102,6 +106,15 @@ public struct CompoundIcon: View { .resizable() .modifier(CompoundIconFrame(fontSize: size.value, textStyle: fontSize.style)) } + + // Letro: Add a mechanism to override icons + private static var overrides = [AnyKeyPath: Image]() + public static func override(_ keyPath: KeyPath, with image: Image?) { + overrides[keyPath] = image + } + private static func resolve(_ keyPath: KeyPath) -> Image { + overrides[keyPath] ?? .compound[keyPath: keyPath] + } } /// A simple modifier that applies a square frame of a given size that will be