Update iconography visible in launch video (#1762)

This commit is contained in:
Doug
2023-09-21 12:38:09 +01:00
committed by GitHub
parent 057c33e680
commit 4e6fe278db
198 changed files with 688 additions and 532 deletions
+17 -7
View File
@@ -33,35 +33,45 @@ internal enum Asset {
internal static let appLogo = ImageAsset(name: "images/app-logo")
internal static let serverSelectionIcon = ImageAsset(name: "images/server-selection-icon")
internal static let closeCircle = ImageAsset(name: "images/close-circle")
internal static let addLocation = ImageAsset(name: "images/add-location")
internal static let attachment = ImageAsset(name: "images/attachment")
internal static let photosLibrary = ImageAsset(name: "images/photos-library")
internal static let takePhoto = ImageAsset(name: "images/take-photo")
internal static let textFormatting = ImageAsset(name: "images/text-formatting")
internal static let bold = ImageAsset(name: "images/bold")
internal static let bulletList = ImageAsset(name: "images/bullet-list")
internal static let closeRte = ImageAsset(name: "images/close-rte")
internal static let codeBlock = ImageAsset(name: "images/code-block")
internal static let composerAttachment = ImageAsset(name: "images/composer-attachment")
internal static let editing = ImageAsset(name: "images/editing")
internal static let indent = ImageAsset(name: "images/indent")
internal static let inlineCode = ImageAsset(name: "images/inline-code")
internal static let italic = ImageAsset(name: "images/italic")
internal static let link = ImageAsset(name: "images/link")
internal static let numberedList = ImageAsset(name: "images/numbered-list")
internal static let quote = ImageAsset(name: "images/quote")
internal static let sendMessage = ImageAsset(name: "images/send-message")
internal static let strikethrough = ImageAsset(name: "images/strikethrough")
internal static let textFormat = ImageAsset(name: "images/text-format")
internal static let underline = ImageAsset(name: "images/underline")
internal static let unindent = ImageAsset(name: "images/unindent")
internal static let encryptionNormal = ImageAsset(name: "images/encryption-normal")
internal static let encryptionTrusted = ImageAsset(name: "images/encryption-trusted")
internal static let encryptionWarning = ImageAsset(name: "images/encryption-warning")
internal static let decryptionError = ImageAsset(name: "images/decryption-error")
internal static let endedPoll = ImageAsset(name: "images/ended-poll")
internal static let compose = ImageAsset(name: "images/compose")
internal static let launchBackground = ImageAsset(name: "images/launch-background")
internal static let locationMarker = ImageAsset(name: "images/location-marker")
internal static let locationPin = ImageAsset(name: "images/location-pin")
internal static let locationPointerFull = ImageAsset(name: "images/location-pointer-full")
internal static let locationPointer = ImageAsset(name: "images/location-pointer")
internal static let closeRte = ImageAsset(name: "images/close-rte")
internal static let composerAttachment = ImageAsset(name: "images/composer-attachment")
internal static let timelineComposerSendMessage = ImageAsset(name: "images/timeline-composer-send-message")
internal static let addReaction = ImageAsset(name: "images/add-reaction")
internal static let copy = ImageAsset(name: "images/copy")
internal static let editOutline = ImageAsset(name: "images/edit-outline")
internal static let forward = ImageAsset(name: "images/forward")
internal static let reply = ImageAsset(name: "images/reply")
internal static let viewSource = ImageAsset(name: "images/view-source")
internal static let timelineEndedPoll = ImageAsset(name: "images/timeline-ended-poll")
internal static let timelinePollAttachment = ImageAsset(name: "images/timeline-poll-attachment")
internal static let timelinePoll = ImageAsset(name: "images/timeline-poll")
internal static let timelineReactionAddMore = ImageAsset(name: "images/timeline-reaction-add-more")
internal static let waitingGradient = ImageAsset(name: "images/waiting-gradient")
}
}
@@ -0,0 +1,54 @@
//
// Copyright 2022 New Vector Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
import Compound
import SwiftUI
extension Label {
/// Creates a label using a local asset as a Compound icon and a title generated from a string.
/// The icon size will be 24pt, scaling relative to the `bodyLG` with Dynamic Type.
/// - Parameters:
/// - title: A string used as the label’s title.
/// - iconAsset: The asset to use as the custom Compound icon.
///
/// The supplied asset should have a square frame or it may end up distorted.
init(_ title: some StringProtocol, iconAsset: ImageAsset) where Title == Text, Icon == CompoundIcon {
self.init {
Text(title)
} icon: {
CompoundIcon(customImage: iconAsset.swiftUIImage)
}
}
/// Creates a label using a local asset as a Compound icon and a title generated from a string.
/// - Parameters:
/// - title: A string used as the label’s title.
/// - iconAsset: The asset to use as the custom Compound icon.
/// - size: The size of the icon.
/// - font: The font that the icon will scale relative to for Dynamic Type.
///
/// The supplied asset should have a square frame or it may end up distorted.
init(_ title: some StringProtocol,
iconAsset: ImageAsset,
iconSize: CompoundIcon.Size,
relativeTo font: Font) where Title == Text, Icon == CompoundIcon {
self.init {
Text(title)
} icon: {
CompoundIcon(customImage: iconAsset.swiftUIImage, size: iconSize, relativeTo: font)
}
}
}
@@ -16,21 +16,23 @@
import SwiftUI
struct FixedIconSizeLabelStyle: LabelStyle {
@ScaledMetric private var iconSize: CGFloat
extension LabelStyle where Self == MenuSheetLabelStyle {
/// A label style for labels that are within a menu that is being presented as a sheet.
static var menuSheet: Self { MenuSheetLabelStyle() }
}
init(iconSize: Double = 24.0) {
_iconSize = .init(wrappedValue: iconSize)
}
/// The style used for labels that are part of a menu that's presented as
/// a sheet as `TimelineItemMenu` and `RoomAttachmentPicker`.
struct MenuSheetLabelStyle: LabelStyle {
var spacing: CGFloat = 16
func makeBody(configuration: Configuration) -> some View {
HStack(spacing: spacing) {
configuration
.icon
.frame(width: iconSize, height: iconSize)
configuration.icon
configuration.title
}
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(16)
}
}
@@ -14,6 +14,7 @@
// limitations under the License.
//
import Compound
import SwiftUI
import WysiwygComposer
@@ -82,6 +83,7 @@ struct ComposerToolbar: View {
.frame(width: closeRTEButtonSize, height: closeRTEButtonSize)
.padding(7)
}
.accessibilityLabel(L10n.actionClose)
.accessibilityIdentifier(A11yIdentifiers.roomScreen.composerToolbar.closeFormattingOptions)
}
@@ -152,12 +154,11 @@ struct ComposerToolbar: View {
private var submitButtonImage: some View {
// ZStack with opacity so the button size is consistent.
ZStack {
Image(systemName: "checkmark")
CompoundIcon(\.check)
.opacity(context.viewState.composerMode.isEdit ? 1 : 0)
.fontWeight(.medium)
.accessibilityLabel(L10n.actionConfirm)
.accessibilityHidden(!context.viewState.composerMode.isEdit)
Image(asset: Asset.Images.timelineComposerSendMessage)
Image(asset: Asset.Images.sendMessage)
.resizable()
.frame(width: sendButtonIconSize, height: sendButtonIconSize)
.padding(EdgeInsets(top: 10, leading: 11, bottom: 10, trailing: 9))
@@ -14,6 +14,7 @@
// limitations under the License.
//
import Compound
import SwiftUI
import WysiwygComposer
@@ -132,11 +133,11 @@ private struct MessageComposerReplyHeader: View {
.cornerRadius(13.0)
.overlay(alignment: .topTrailing) {
Button(action: action) {
Image(systemName: "xmark")
.font(.compound.bodySM.weight(.medium))
CompoundIcon(\.close, size: .small, relativeTo: .compound.bodySMSemibold)
.foregroundColor(.compound.iconTertiary)
.padding(8.0)
.padding(4.0)
}
.accessibilityLabel(L10n.actionClose)
}
.padding(.vertical, 8.0)
.padding(.horizontal, -4.0)
@@ -148,26 +149,29 @@ private struct MessageComposerEditHeader: View {
var body: some View {
HStack(alignment: .center) {
Label(L10n.commonEditing, systemImage: "pencil.line")
Label(L10n.commonEditing,
iconAsset: Asset.Images.editing,
iconSize: .xSmall,
relativeTo: .compound.bodySMSemibold)
.labelStyle(MessageComposerHeaderLabelStyle())
Spacer()
Button(action: action) {
Image(systemName: "xmark")
.font(.compound.bodySM.weight(.medium))
CompoundIcon(\.close, size: .small, relativeTo: .compound.bodySMSemibold)
.foregroundColor(.compound.iconTertiary)
.padding(EdgeInsets(top: 10, leading: 12, bottom: 12, trailing: 14))
.padding([.leading, .vertical], 6.0)
}
.accessibilityLabel(L10n.actionClose)
}
}
}
private struct MessageComposerHeaderLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .firstTextBaseline, spacing: 5) {
HStack(alignment: .center, spacing: 4) {
configuration.icon
configuration.title
}
.font(.compound.bodySM)
.font(.compound.bodySMSemibold)
.foregroundColor(.compound.textSecondary)
.lineLimit(1)
}
@@ -14,6 +14,7 @@
// limitations under the License.
//
import Compound
import SwiftUI
import WysiwygComposer
@@ -35,6 +36,7 @@ struct RoomAttachmentPicker: View {
.foregroundColor(.compound.textActionPrimary)
.padding(7)
}
.accessibilityLabel(L10n.actionAddToTimeline)
.accessibilityIdentifier(A11yIdentifiers.roomScreen.composerToolbar.openComposeOptions)
.popover(isPresented: $context.showAttachmentPopover) {
VStack(alignment: .leading, spacing: 0.0) {
@@ -42,7 +44,8 @@ struct RoomAttachmentPicker: View {
context.showAttachmentPopover = false
context.send(viewAction: .displayMediaPicker)
} label: {
PickerLabel(title: L10n.screenRoomAttachmentSourceGallery, icon: Image(systemName: "photo.fill"))
Label(L10n.screenRoomAttachmentSourceGallery, iconAsset: Asset.Images.photosLibrary)
.labelStyle(.menuSheet)
}
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerPhotoLibrary)
@@ -50,7 +53,8 @@ struct RoomAttachmentPicker: View {
context.showAttachmentPopover = false
context.send(viewAction: .displayDocumentPicker)
} label: {
PickerLabel(title: L10n.screenRoomAttachmentSourceFiles, icon: Image(systemName: "paperclip"))
Label(L10n.screenRoomAttachmentSourceFiles, iconAsset: Asset.Images.attachment)
.labelStyle(.menuSheet)
}
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerDocuments)
@@ -58,7 +62,8 @@ struct RoomAttachmentPicker: View {
context.showAttachmentPopover = false
context.send(viewAction: .displayCameraPicker)
} label: {
PickerLabel(title: L10n.screenRoomAttachmentSourceCamera, icon: Image(systemName: "camera.fill"))
Label(L10n.screenRoomAttachmentSourceCamera, iconAsset: Asset.Images.takePhoto)
.labelStyle(.menuSheet)
}
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerCamera)
@@ -66,7 +71,8 @@ struct RoomAttachmentPicker: View {
context.showAttachmentPopover = false
context.send(viewAction: .displayLocationPicker)
} label: {
PickerLabel(title: L10n.screenRoomAttachmentSourceLocation, icon: Image(asset: Asset.Images.locationPin))
Label(L10n.screenRoomAttachmentSourceLocation, iconAsset: Asset.Images.addLocation)
.labelStyle(.menuSheet)
}
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerLocation)
@@ -74,7 +80,8 @@ struct RoomAttachmentPicker: View {
context.showAttachmentPopover = false
context.send(viewAction: .displayPollForm)
} label: {
PickerLabel(title: L10n.screenRoomAttachmentSourcePoll, icon: Image(asset: Asset.Images.timelinePollAttachment))
Label(L10n.screenRoomAttachmentSourcePoll, icon: \.polls)
.labelStyle(.menuSheet)
}
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerPoll)
@@ -83,7 +90,8 @@ struct RoomAttachmentPicker: View {
context.showAttachmentPopover = false
context.send(viewAction: .enableTextFormatting)
} label: {
PickerLabel(title: L10n.screenRoomAttachmentTextFormatting, icon: Image(asset: Asset.Images.textFormat))
Label(L10n.screenRoomAttachmentTextFormatting, iconAsset: Asset.Images.textFormat)
.labelStyle(.menuSheet)
}
.accessibilityIdentifier(A11yIdentifiers.roomScreen.attachmentPickerTextFormatting)
}
@@ -104,30 +112,6 @@ struct RoomAttachmentPicker: View {
.presentationDragIndicator(.visible)
}
}
private struct PickerLabel: View {
let title: String
let icon: Image
init(title: String, icon: Image) {
self.title = title
self.icon = icon
}
var body: some View {
Label {
Text(title)
} icon: {
icon
.resizable()
.scaledToFit()
}
.labelStyle(FixedIconSizeLabelStyle(iconSize: 20))
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(16)
}
}
}
struct RoomAttachmentPicker_Previews: PreviewProvider, TestablePreview {
@@ -141,11 +141,11 @@ private struct CreatePollOptionView: View {
if editMode?.wrappedValue == .active {
Button(action: deleteAction) {
CompoundIcon(\.delete)
.font(.system(size: 24))
.foregroundColor(.compound.iconCriticalPrimary)
}
.disabled(!canDeleteItem)
.buttonStyle(PlainButtonStyle())
.accessibilityLabel(L10n.actionRemove)
}
TextField(text: $text) {
Text(placeholder)
@@ -261,9 +261,9 @@ struct HomeScreen: View {
Button {
context.send(viewAction: .startChat)
} label: {
Image(systemName: "square.and.pencil")
.fontWeight(.semibold)
CompoundIcon(customImage: Asset.Images.compose.swiftUIImage)
}
.accessibilityLabel(L10n.actionStartChat)
.accessibilityIdentifier(A11yIdentifiers.homeScreen.startChat)
}
@@ -36,7 +36,7 @@ struct HomeScreenEmptyStateView: View {
.padding(.bottom, 12)
Button { context.send(viewAction: .startChat) } label: {
Label(L10n.actionStartChat, systemImage: "square.and.pencil")
Label(L10n.actionStartChat, iconAsset: Asset.Images.compose)
.font(.compound.bodyLGSemibold)
.foregroundColor(.compound.textOnSolidPrimary)
.padding(.vertical, 6)
@@ -143,12 +143,10 @@ struct HomeScreenRoomCell: View {
case .none, .allMessages:
EmptyView()
case .mentionsAndKeywordsOnly:
CompoundIcon(\.mention)
.font(.system(size: 15))
CompoundIcon(\.mention, size: .custom(15), relativeTo: .compound.bodyMD)
.accessibilityLabel(L10n.a11yNotificationsMentionsOnly)
case .mute:
CompoundIcon(\.notificationsSolidOff)
.font(.system(size: 15))
CompoundIcon(\.notificationsSolidOff, size: .custom(15), relativeTo: .compound.bodyMD)
.accessibilityLabel(L10n.a11yNotificationsMuted)
}
}
@@ -25,8 +25,7 @@ struct ThreadDecorator: View {
.foregroundColor(.compound.textPrimary)
.font(.compound.bodyXS)
} icon: {
CompoundIcon(\.threads)
.font(.system(size: 16))
CompoundIcon(\.threads, size: .xSmall, relativeTo: .compound.bodyXS)
.foregroundColor(.compound.iconSecondary)
}
.labelStyle(.custom(spacing: 4))
@@ -209,7 +209,7 @@ struct TimelineItemBubbledStylerView<Content: View>: View {
}
if timelineItem.hasFailedToSend {
Image(systemName: "exclamationmark.circle.fill")
CompoundIcon(\.error, size: .xSmall, relativeTo: .compound.bodyXS)
}
}
.font(.compound.bodyXS)
@@ -14,6 +14,7 @@
// limitations under the License.
//
import Compound
import SwiftUI
struct TimelineItemStatusView: View {
@@ -50,12 +51,10 @@ struct TimelineItemStatusView: View {
TimelineDeliveryStatusView(deliveryStatus: .sent)
}
case .sendingFailed:
// The bubbles handle the failure internally
// Bubbles handle the failure internally, but plain items don't.
if style == .plain {
Image(systemName: "exclamationmark.circle.fill")
.resizable()
CompoundIcon(\.error, size: .xSmall, relativeTo: .compound.bodyMD)
.foregroundColor(.compound.iconCriticalPrimary)
.frame(width: 16, height: 16)
.onTapGesture {
context.sendFailedConfirmationDialogInfo = .init(itemID: timelineItem.id)
}
@@ -156,7 +156,7 @@ struct TimelineReactionAddMoreButtonLabel: View {
var body: some View {
TimelineReactionButtonLabel {
Image(asset: Asset.Images.timelineReactionAddMore)
Image(asset: Asset.Images.addReaction)
.resizable()
.frame(width: addMoreButtonIconSize, height: addMoreButtonIconSize)
// Vertical sizing is done by the layout so that the add more button
@@ -14,6 +14,7 @@
// limitations under the License.
//
import Compound
import SwiftUI
/// The item shown when all previous items are un-decryptable due to
@@ -27,7 +28,7 @@ struct EncryptedHistoryRoomTimelineView: View {
.font(.compound.bodyMDSemibold)
.foregroundColor(.compound.textInfoPrimary)
} icon: {
Image(systemName: "info.circle.fill")
CompoundIcon(\.info, size: .small, relativeTo: .compound.bodyMDSemibold)
.foregroundColor(.compound.iconInfoPrimary)
}
.labelStyle(EncryptedHistoryLabelStyle())
@@ -46,7 +47,7 @@ struct EncryptedHistoryRoomTimelineView: View {
private struct EncryptedHistoryLabelStyle: LabelStyle {
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .firstTextBaseline, spacing: 16) {
HStack(alignment: .top, spacing: 16) {
configuration.icon
configuration.title
}
@@ -21,8 +21,9 @@ struct EncryptedRoomTimelineView: View {
var body: some View {
TimelineStyler(timelineItem: timelineItem) {
Label(timelineItem.body, systemImage: "lock.shield")
Label(timelineItem.body, iconAsset: Asset.Images.decryptionError, iconSize: .small, relativeTo: .compound.bodyLG)
.labelStyle(RoomTimelineViewLabelStyle())
.font(.compound.bodyLG)
}
}
}
@@ -31,11 +32,10 @@ struct RoomTimelineViewLabelStyle: LabelStyle {
@Environment(\.timelineStyle) private var timelineStyle
func makeBody(configuration: Configuration) -> some View {
HStack(alignment: .firstTextBaseline, spacing: 8) {
HStack(alignment: .center, spacing: 8) {
configuration.icon
.foregroundColor(.compound.iconSecondary)
configuration.title
.font(.body)
.foregroundColor(.compound.textPrimary)
}
.padding(.horizontal, timelineStyle == .bubbles ? 4 : 0)
@@ -63,6 +63,11 @@ struct EncryptedRoomTimelineView_Previews: PreviewProvider, TestablePreview {
timestamp: "Later",
isOutgoing: true,
senderId: "Anne"))
EncryptedRoomTimelineView(timelineItem: itemWith(text: "Some other text that is very long and will wrap onto multiple lines.",
timestamp: "Later",
isOutgoing: true,
senderId: "Anne"))
}
}
@@ -101,27 +101,27 @@ enum TimelineItemMenuAction: Identifiable, Hashable {
var label: some View {
switch self {
case .copy:
Label(L10n.actionCopy, systemImage: "doc.on.doc")
Label(L10n.actionCopy, iconAsset: Asset.Images.copy)
case .edit:
Label(L10n.actionEdit, systemImage: "pencil.line")
Label(L10n.actionEdit, iconAsset: Asset.Images.editOutline)
case .copyPermalink:
Label(L10n.actionCopyLinkToMessage, systemImage: "link")
Label(L10n.actionCopyLinkToMessage, icon: \.link)
case .reply(let isThread):
Label(isThread ? L10n.actionReplyInThread : L10n.actionReply, systemImage: "arrowshape.turn.up.left")
Label(isThread ? L10n.actionReplyInThread : L10n.actionReply, iconAsset: Asset.Images.reply)
case .forward:
Label(L10n.actionForward, systemImage: "arrowshape.turn.up.right")
Label(L10n.actionForward, iconAsset: Asset.Images.forward)
case .redact:
Label(L10n.actionRemove, systemImage: "trash")
Label(L10n.actionRemove, icon: \.delete)
case .viewSource:
Label(L10n.actionViewSource, systemImage: "doc.text.below.ecg")
Label(L10n.actionViewSource, iconAsset: Asset.Images.viewSource)
case .retryDecryption:
Label(L10n.actionRetryDecryption, systemImage: "arrow.down.message")
case .report:
Label(L10n.actionReportContent, systemImage: "exclamationmark.bubble")
Label(L10n.actionReportContent, icon: \.chatProblem)
case .react:
Label(L10n.actionReact, systemImage: "hand.thumbsup")
Label(L10n.actionReact, iconAsset: Asset.Images.addReaction)
case .endPoll:
Label { Text(L10n.actionEndPoll) } icon: { CompoundIcon(customImage: Asset.Images.endedPoll.swiftUIImage) }
Label(L10n.actionEndPoll, iconAsset: Asset.Images.endedPoll)
}
}
}
@@ -227,13 +227,14 @@ public struct TimelineItemMenu: View {
context.send(viewAction: .displayEmojiPicker(itemID: item.id))
}
} label: {
Image(asset: Asset.Images.timelineReactionAddMore)
Image(asset: Asset.Images.addReaction)
.resizable()
.frame(width: addMoreButtonIconSize, height: addMoreButtonIconSize)
.frame(maxHeight: .infinity, alignment: .center)
.foregroundColor(.compound.iconSecondary)
.padding(10)
}
.accessibilityLabel(L10n.actionReact)
}
.padding(.horizontal)
.frame(maxWidth: .infinity, alignment: .center)
@@ -269,10 +270,7 @@ public struct TimelineItemMenu: View {
send(action)
} label: {
action.label
.labelStyle(FixedIconSizeLabelStyle())
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .leading)
.padding()
.labelStyle(.menuSheet)
}
}
}
@@ -76,7 +76,6 @@ struct SettingsScreen: View {
Spacer()
CompoundIcon(\.chevronRight)
.font(.system(size: 24))
.foregroundColor(.compound.iconTertiaryAlpha)
.flipsForRightToLeftLayoutDirection(true)
}
@@ -167,15 +167,6 @@ class RoomProxy: RoomProxyProtocol {
roomListItem.avatarUrl().flatMap(URL.init(string:))
}
var encryptionBadgeImage: UIImage? {
guard isEncrypted else {
return nil
}
// return trusted image for now, should be updated after verification status known
return Asset.Images.encryptionTrusted.image
}
var invitedMembersCount: Int {
Int(room.invitedMembersCount())
}