mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
ecced8ee48
* Bump element-call-ios to 0.1.0-rc.10 The release moves the RTC core to matrix-rust-rtc's room-first API, so the stack no longer needs starting or stopping: it subscribes to a room when a call opens it. It also adds headphones and Bluetooth audio output icons, mapped here to Compound's headphones icon. * Bump compound-design-tokens to 11.3.1 and use the Bluetooth output icon 11.3.1 adds the `bluetoothOutput` icon, replacing the `headphonesSolid` placeholder for the Bluetooth audio route in native calls.
155 lines
4.7 KiB
Swift
155 lines
4.7 KiB
Swift
//
|
|
// 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 ElementCall
|
|
import SwiftUI
|
|
|
|
// Colours, icons and avatars for the call, read from this app's Compound.
|
|
//
|
|
// Why the package takes a theme instead of linking Compound itself: `Color.compound` is a single
|
|
// shared instance that Element Pro re-brands at runtime, and a second copy inside the package would
|
|
// never see the override. Every member is computed so each read goes to the live instance.
|
|
|
|
struct NativeCallTheme: ElementCallThemeProtocol {
|
|
var bgCanvasDefault: Color {
|
|
.compound.bgCanvasDefault
|
|
}
|
|
|
|
var bgCanvasDefaultLevel: Color {
|
|
.compound.bgCanvasDefaultLevel1
|
|
}
|
|
|
|
var bgSubtleSecondary: Color {
|
|
.compound.bgSubtleSecondary
|
|
}
|
|
|
|
var bgAccentRest: Color {
|
|
.compound.bgAccentRest
|
|
}
|
|
|
|
var bgActionPrimaryRest: Color {
|
|
.compound.bgActionPrimaryRest
|
|
}
|
|
|
|
var bgCriticalPrimary: Color {
|
|
.compound.bgCriticalPrimary
|
|
}
|
|
|
|
var iconPrimary: Color {
|
|
.compound.iconPrimary
|
|
}
|
|
|
|
var iconQuaternary: Color {
|
|
.compound.iconQuaternary
|
|
}
|
|
|
|
var iconAccentPrimary: Color {
|
|
.compound.iconAccentPrimary
|
|
}
|
|
|
|
var iconCriticalPrimary: Color {
|
|
.compound.iconCriticalPrimary
|
|
}
|
|
|
|
var iconOnSolidPrimary: Color {
|
|
.compound.iconOnSolidPrimary
|
|
}
|
|
|
|
var textPrimary: Color {
|
|
.compound.textPrimary
|
|
}
|
|
|
|
var textSecondary: Color {
|
|
.compound.textSecondary
|
|
}
|
|
|
|
var textCriticalPrimary: Color {
|
|
.compound.textCriticalPrimary
|
|
}
|
|
|
|
var borderSuccessSubtle: Color {
|
|
.compound.borderSuccessSubtle
|
|
}
|
|
|
|
var borderInteractiveSecondary: Color {
|
|
.compound.borderInteractiveSecondary
|
|
}
|
|
|
|
var bodySM: Font {
|
|
.compound.bodySM
|
|
}
|
|
|
|
var bodySMSemibold: Font {
|
|
.compound.bodySMSemibold
|
|
}
|
|
|
|
var bodyLGSemibold: Font {
|
|
.compound.bodyLGSemibold
|
|
}
|
|
}
|
|
|
|
struct NativeCallIcons: ElementCallIconRenderingProtocol {
|
|
func icon(_ icon: ElementCallIcon, size: ElementCallIconSize, relativeTo textStyle: ElementCallTextStyle?) -> AnyView {
|
|
// The package asks for a text style rather than a point size so CompoundIcon can scale it.
|
|
AnyView(CompoundIcon(Self.keyPath(for: icon), size: Self.size(for: size), relativeTo: Self.font(for: textStyle)))
|
|
}
|
|
|
|
private static func keyPath(for icon: ElementCallIcon) -> KeyPath<CompoundIcons, Image> {
|
|
switch icon {
|
|
case .endCall: \.endCall
|
|
case .micOn: \.micOnSolid
|
|
case .micOff: \.micOffSolid
|
|
case .videoCall: \.videoCallSolid
|
|
case .videoCallOff: \.videoCallOffSolid
|
|
case .switchCamera: \.switchCameraSolid
|
|
case .shareScreen: \.shareScreenSolid
|
|
case .volumeOn: \.volumeOnSolid
|
|
case .volumeOff: \.volumeOffSolid
|
|
case .headphones: \.headphonesSolid
|
|
case .bluetooth: \.bluetoothOutput
|
|
case .raisedHand: \.raisedHandSolid
|
|
case .userProfile: \.userProfileSolid
|
|
case .overflow: \.overflowHorizontal
|
|
case .collapse: \.collapse
|
|
}
|
|
}
|
|
|
|
private static func size(for size: ElementCallIconSize) -> CompoundIcon.Size {
|
|
switch size {
|
|
case .xSmall: .xSmall
|
|
case .small: .small
|
|
case .medium: .medium
|
|
}
|
|
}
|
|
|
|
// Not redundant despite the target's default isolation: a static member of a type conforming to
|
|
// a nonisolated, Sendable port is nonisolated, and `Font.compound` is main actor-isolated.
|
|
@MainActor
|
|
private static func font(for textStyle: ElementCallTextStyle?) -> Font {
|
|
switch textStyle {
|
|
case .bodySM: .compound.bodySM
|
|
case .bodySMSemibold: .compound.bodySMSemibold
|
|
case .bodyLGSemibold: .compound.bodyLGSemibold
|
|
case nil: .compound.bodyMD
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Keeps avatars looking like the rest of the app, and keeps media loading out of the package.
|
|
struct NativeCallAvatars: ElementCallAvatarRenderingProtocol {
|
|
let mediaProvider: MediaProviderProtocol?
|
|
|
|
func avatar(userID: String, displayName: String?, avatarURL: URL?, size: ElementCallAvatarSize) -> AnyView {
|
|
AnyView(LoadableAvatarImage(url: avatarURL,
|
|
name: displayName,
|
|
contentID: userID,
|
|
avatarSize: size == .thumbnail ? .user(on: .roomDetails) : .user(on: .memberDetails),
|
|
mediaProvider: mediaProvider))
|
|
}
|
|
}
|