mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Update HeroImage to match the BigIcon component from Compound. (#3439)
We can move it across in a following PR.
This commit is contained in:
+45
-38
@@ -10,39 +10,36 @@ import SwiftUI
|
||||
|
||||
/// An image that is styled for use as the main/top/hero screen icon. This component
|
||||
/// takes a compound icon. If you would like to apply it to an SFSymbol, you can call
|
||||
/// the `heroImage()` modifier directly on the Image.
|
||||
struct HeroImage: View {
|
||||
/// the `bigIcon()` modifier directly on the Image.
|
||||
struct BigIcon: View {
|
||||
enum Style {
|
||||
case normal
|
||||
case subtle
|
||||
case defaultSolid
|
||||
case `default`
|
||||
case alertSolid
|
||||
case alert
|
||||
case successSolid
|
||||
case success
|
||||
case critical
|
||||
case criticalOnSecondary
|
||||
|
||||
var foregroundColor: Color {
|
||||
switch self {
|
||||
case .normal:
|
||||
.compound.iconPrimary
|
||||
case .subtle:
|
||||
case .defaultSolid, .default:
|
||||
.compound.iconSecondary
|
||||
case .success:
|
||||
.compound.iconSuccessPrimary
|
||||
case .critical, .criticalOnSecondary:
|
||||
case .alertSolid, .alert:
|
||||
.compound.iconCriticalPrimary
|
||||
case .successSolid, .success:
|
||||
.compound.iconSuccessPrimary
|
||||
}
|
||||
}
|
||||
|
||||
var backgroundFillColor: Color {
|
||||
switch self {
|
||||
case .normal:
|
||||
case .defaultSolid:
|
||||
.compound.bgSubtleSecondary
|
||||
case .subtle:
|
||||
.compound.bgSubtlePrimary
|
||||
case .success:
|
||||
.compound.bgSuccessSubtle
|
||||
case .critical:
|
||||
case .alertSolid:
|
||||
.compound.bgCriticalSubtle
|
||||
case .criticalOnSecondary:
|
||||
case .successSolid:
|
||||
.compound.bgSuccessSubtle
|
||||
case .default, .alert, .success:
|
||||
.compound.bgCanvasDefault
|
||||
}
|
||||
}
|
||||
@@ -50,32 +47,32 @@ struct HeroImage: View {
|
||||
|
||||
/// The icon that is shown.
|
||||
let icon: KeyPath<CompoundIcons, Image>
|
||||
var style: Style = .normal
|
||||
var style: Style = .defaultSolid
|
||||
|
||||
var body: some View {
|
||||
CompoundIcon(icon, size: .custom(42), relativeTo: .title)
|
||||
.modifier(HeroImageModifier(style: style))
|
||||
CompoundIcon(icon, size: .custom(32), relativeTo: .title)
|
||||
.modifier(BigIconModifier(style: style))
|
||||
}
|
||||
}
|
||||
|
||||
extension Image {
|
||||
/// Styles the image for use as the main/top/hero screen icon. You should prefer
|
||||
/// the HeroImage component when possible, by using an icon from Compound.
|
||||
func heroImage(insets: CGFloat = 16, style: HeroImage.Style = .normal) -> some View {
|
||||
/// the BigIcon component when possible, by using an icon from Compound.
|
||||
func bigIcon(insets: CGFloat = 16, style: BigIcon.Style = .defaultSolid) -> some View {
|
||||
resizable()
|
||||
.renderingMode(.template)
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.scaledPadding(insets, relativeTo: .title)
|
||||
.modifier(HeroImageModifier(style: style))
|
||||
.modifier(BigIconModifier(style: style))
|
||||
}
|
||||
}
|
||||
|
||||
private struct HeroImageModifier: ViewModifier {
|
||||
let style: HeroImage.Style
|
||||
private struct BigIconModifier: ViewModifier {
|
||||
let style: BigIcon.Style
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.scaledFrame(size: 70, relativeTo: .title)
|
||||
.scaledFrame(size: 64, relativeTo: .title)
|
||||
.foregroundColor(style.foregroundColor)
|
||||
.background {
|
||||
RoundedRectangle(cornerRadius: 14)
|
||||
@@ -87,22 +84,32 @@ private struct HeroImageModifier: ViewModifier {
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct HeroImage_Previews: PreviewProvider, TestablePreview {
|
||||
struct BigIcon_Previews: PreviewProvider, TestablePreview {
|
||||
static var previews: some View {
|
||||
VStack(spacing: 20) {
|
||||
VStack(spacing: 40) {
|
||||
HStack(spacing: 20) {
|
||||
HeroImage(icon: \.lockSolid)
|
||||
BigIcon(icon: \.lockSolid)
|
||||
Image(systemName: "hourglass")
|
||||
.heroImage()
|
||||
.bigIcon()
|
||||
Image(asset: Asset.Images.serverSelectionIcon)
|
||||
.heroImage(insets: 19)
|
||||
.bigIcon(insets: 19)
|
||||
}
|
||||
|
||||
HStack(spacing: 20) {
|
||||
HeroImage(icon: \.helpSolid, style: .subtle)
|
||||
HeroImage(icon: \.checkCircleSolid, style: .success)
|
||||
HeroImage(icon: \.error, style: .critical)
|
||||
HeroImage(icon: \.error, style: .criticalOnSecondary)
|
||||
VStack(spacing: 20) {
|
||||
HStack(spacing: 20) {
|
||||
BigIcon(icon: \.helpSolid)
|
||||
BigIcon(icon: \.helpSolid, style: .default)
|
||||
}
|
||||
|
||||
HStack(spacing: 20) {
|
||||
BigIcon(icon: \.error, style: .alertSolid)
|
||||
BigIcon(icon: \.error, style: .alert)
|
||||
}
|
||||
|
||||
HStack(spacing: 20) {
|
||||
BigIcon(icon: \.checkCircleSolid, style: .successSolid)
|
||||
BigIcon(icon: \.checkCircleSolid, style: .success)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -58,7 +58,7 @@ struct AppLockSetupPINScreen: View {
|
||||
|
||||
var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
HeroImage(icon: \.lockSolid)
|
||||
BigIcon(icon: \.lockSolid)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(context.viewState.title)
|
||||
|
||||
@@ -45,7 +45,7 @@ struct LoginScreen: View {
|
||||
/// The header containing the title and icon.
|
||||
var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
HeroImage(icon: \.lockSolid)
|
||||
BigIcon(icon: \.lockSolid)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.screenLoginTitleWithHomeserver(context.viewState.homeserver.address))
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ struct ServerConfirmationScreen: View {
|
||||
var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
Image(systemSymbol: .personCropCircleFill)
|
||||
.heroImage()
|
||||
.bigIcon()
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(context.viewState.title)
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ struct ServerSelectionScreen: View {
|
||||
var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
Image(asset: Asset.Images.serverSelectionIcon)
|
||||
.heroImage(insets: 19)
|
||||
.bigIcon(insets: 19)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.screenChangeServerTitle)
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ struct EncryptionResetPasswordScreen: View {
|
||||
var body: some View {
|
||||
FullscreenDialog {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.lockSolid)
|
||||
BigIcon(icon: \.lockSolid)
|
||||
|
||||
Text(L10n.screenResetEncryptionPasswordTitle)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ struct EncryptionResetScreen: View {
|
||||
|
||||
private var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
HeroImage(icon: \.error, style: .criticalOnSecondary)
|
||||
BigIcon(icon: \.error, style: .alert)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.screenEncryptionResetTitle)
|
||||
|
||||
@@ -88,7 +88,7 @@ struct JoinRoomScreen: View {
|
||||
@ViewBuilder
|
||||
private var knockedView: some View {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.checkCircleSolid, style: .success)
|
||||
BigIcon(icon: \.checkCircleSolid, style: .successSolid)
|
||||
VStack(spacing: 8) {
|
||||
Text(L10n.screenJoinRoomKnockSentTitle)
|
||||
.font(.compound.headingMDBold)
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ struct AnalyticsPromptScreen: View {
|
||||
|
||||
private var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
HeroImage(icon: \.chart)
|
||||
BigIcon(icon: \.chart)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.screenAnalyticsPromptTitle(InfoPlistReader.main.bundleDisplayName))
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ struct IdentityConfirmationScreen: View {
|
||||
@ViewBuilder
|
||||
private var screenHeader: some View {
|
||||
VStack(spacing: 0) {
|
||||
HeroImage(icon: \.lockSolid)
|
||||
BigIcon(icon: \.lockSolid)
|
||||
.padding(.bottom, 16)
|
||||
|
||||
Text(L10n.screenIdentityConfirmationTitle)
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ struct IdentityConfirmedScreen: View {
|
||||
@ViewBuilder
|
||||
private var screenHeader: some View {
|
||||
VStack(spacing: 0) {
|
||||
HeroImage(icon: \.checkCircle, style: .success)
|
||||
BigIcon(icon: \.checkCircle, style: .successSolid)
|
||||
.padding(.bottom, 16)
|
||||
|
||||
Text(L10n.screenIdentityConfirmedTitle)
|
||||
|
||||
+1
-1
@@ -27,7 +27,7 @@ struct NotificationPermissionsScreen: View {
|
||||
/// The main content of the screen that is shown inside the scroll view.
|
||||
private var mainContent: some View {
|
||||
VStack(spacing: 8) {
|
||||
HeroImage(icon: \.notificationsSolid)
|
||||
BigIcon(icon: \.notificationsSolid)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(L10n.screenNotificationOptinTitle)
|
||||
|
||||
+2
-2
@@ -58,11 +58,11 @@ struct SessionVerificationScreen: View {
|
||||
private var screenHeader: some View {
|
||||
VStack(spacing: 0) {
|
||||
if context.viewState.verificationState == .initial {
|
||||
HeroImage(icon: \.lockSolid)
|
||||
BigIcon(icon: \.lockSolid)
|
||||
.padding(.bottom, 16)
|
||||
} else {
|
||||
Image(systemName: headerImageName)
|
||||
.heroImage()
|
||||
.bigIcon()
|
||||
.padding(.bottom, 16)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ struct PinnedEventsTimelineScreen: View {
|
||||
private var content: some View {
|
||||
if timelineContext.viewState.pinnedEventIDs.isEmpty {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.pin, style: .normal)
|
||||
BigIcon(icon: \.pin)
|
||||
Text(L10n.screenPinnedTimelineEmptyStateHeadline)
|
||||
.font(.compound.headingSMSemibold)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
|
||||
@@ -41,7 +41,7 @@ struct QRCodeLoginScreen: View {
|
||||
FullscreenDialog {
|
||||
VStack(alignment: .leading, spacing: 40) {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.computer, style: .subtle)
|
||||
BigIcon(icon: \.computer, style: .default)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text(L10n.screenQrCodeLoginInitialStateTitle(InfoPlistReader.main.productionAppName))
|
||||
@@ -101,7 +101,7 @@ struct QRCodeLoginScreen: View {
|
||||
VStack(spacing: 16) {
|
||||
switch state {
|
||||
case .deviceCode:
|
||||
HeroImage(icon: \.computer, style: .subtle)
|
||||
BigIcon(icon: \.computer, style: .default)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text(L10n.screenQrCodeLoginDeviceCodeTitle)
|
||||
@@ -115,7 +115,7 @@ struct QRCodeLoginScreen: View {
|
||||
.multilineTextAlignment(.center)
|
||||
}
|
||||
case .verificationCode:
|
||||
HeroImage(icon: \.lock, style: .subtle)
|
||||
BigIcon(icon: \.lock, style: .default)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text(L10n.screenQrCodeLoginVerifyCodeTitle)
|
||||
@@ -136,7 +136,7 @@ struct QRCodeLoginScreen: View {
|
||||
FullscreenDialog {
|
||||
VStack(spacing: 40) {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.takePhotoSolid, style: .subtle)
|
||||
BigIcon(icon: \.takePhotoSolid, style: .default)
|
||||
|
||||
Text(L10n.screenQrCodeLoginScanningStateTitle)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
@@ -256,7 +256,7 @@ struct QRCodeLoginScreen: View {
|
||||
switch errorState {
|
||||
case .noCameraPermission:
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.takePhotoSolid, style: .subtle)
|
||||
BigIcon(icon: \.takePhotoSolid, style: .default)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text(L10n.screenQrCodeLoginNoCameraPermissionStateTitle)
|
||||
@@ -273,7 +273,7 @@ struct QRCodeLoginScreen: View {
|
||||
case .connectionNotSecure:
|
||||
VStack(spacing: 40) {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.error, style: .criticalOnSecondary)
|
||||
BigIcon(icon: \.error, style: .alert)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text(L10n.screenQrCodeLoginConnectionNoteSecureStateTitle)
|
||||
@@ -339,7 +339,7 @@ struct QRCodeLoginScreen: View {
|
||||
}
|
||||
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.error, style: .criticalOnSecondary)
|
||||
BigIcon(icon: \.error, style: .alert)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text(title)
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ struct ResolveVerifiedUserSendFailureScreen: View {
|
||||
|
||||
var header: some View {
|
||||
VStack(spacing: 8) {
|
||||
HeroImage(icon: \.error, style: .critical)
|
||||
BigIcon(icon: \.error, style: .alertSolid)
|
||||
.padding(.bottom, 8)
|
||||
|
||||
Text(context.viewState.title)
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ struct SecureBackupKeyBackupScreen: View {
|
||||
|
||||
private var disableBackupSection: some View {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.keyOffSolid)
|
||||
BigIcon(icon: \.keyOffSolid)
|
||||
|
||||
Text(L10n.screenKeyBackupDisableTitle)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ struct SecureBackupLogoutConfirmationScreen: View {
|
||||
var body: some View {
|
||||
FullscreenDialog {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.keyOffSolid)
|
||||
BigIcon(icon: \.keyOffSolid)
|
||||
content
|
||||
}
|
||||
.padding()
|
||||
|
||||
+1
-1
@@ -53,7 +53,7 @@ struct SecureBackupRecoveryKeyScreen: View {
|
||||
|
||||
private var header: some View {
|
||||
VStack(spacing: 16) {
|
||||
HeroImage(icon: \.keySolid)
|
||||
BigIcon(icon: \.keySolid)
|
||||
|
||||
Text(context.viewState.title)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
|
||||
Reference in New Issue
Block a user