mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Tweaks discovered when using Compound overrides. (#4456)
- Fixes a bug where gradients wouldn't use the latest tokens after first use. - Makes the bloom aware of colour overrides (and discovers a bug with dark mode). - Decouples HomeScreenCell from the HomeScreenViewModel.Context (so it can be previewed with colour overrides).
This commit is contained in:
@@ -9406,7 +9406,7 @@
|
|||||||
repositoryURL = "https://github.com/element-hq/compound-ios";
|
repositoryURL = "https://github.com/element-hq/compound-ios";
|
||||||
requirement = {
|
requirement = {
|
||||||
kind = revision;
|
kind = revision;
|
||||||
revision = 8d7ca2e413026735b724044847ff1ae3b40563f0;
|
revision = 4ad0bd0acd825a905bb4955b32979fe34cc538eb;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */ = {
|
F76A08D0EA29A07A54F4EB4D /* XCRemoteSwiftPackageReference "swift-collections" */ = {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/element-hq/compound-ios",
|
"location" : "https://github.com/element-hq/compound-ios",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "8d7ca2e413026735b724044847ff1ae3b40563f0"
|
"revision" : "4ad0bd0acd825a905bb4955b32979fe34cc538eb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -275,8 +275,8 @@
|
|||||||
"kind" : "remoteSourceControl",
|
"kind" : "remoteSourceControl",
|
||||||
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
|
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
|
||||||
"state" : {
|
"state" : {
|
||||||
"revision" : "37230a37e83f1b7023be08e1b1a2603fcb1567fb",
|
"revision" : "1be8144023c367c5de701a6313ed29a3a10bf59b",
|
||||||
"version" : "1.18.4"
|
"version" : "1.18.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -67,17 +67,10 @@ struct ShimmerModifier: ViewModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ShimmerOverlay_Previews: PreviewProvider, TestablePreview {
|
struct ShimmerOverlay_Previews: PreviewProvider, TestablePreview {
|
||||||
static let viewModel = HomeScreenViewModel(userSession: UserSessionMock(.init(clientProxy: ClientProxyMock(.init(userID: "")))),
|
|
||||||
selectedRoomPublisher: CurrentValueSubject<String?, Never>(nil).asCurrentValuePublisher(),
|
|
||||||
appSettings: ServiceLocator.shared.settings,
|
|
||||||
analyticsService: ServiceLocator.shared.analytics,
|
|
||||||
notificationManager: NotificationManagerMock(),
|
|
||||||
userIndicatorController: ServiceLocator.shared.userIndicatorController)
|
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
ForEach(0...8, id: \.self) { _ in
|
ForEach(0...8, id: \.self) { _ in
|
||||||
HomeScreenRoomCell(room: .placeholder(), context: viewModel.context, isSelected: false)
|
HomeScreenRoomCell(room: .placeholder(), isSelected: false, mediaProvider: MediaProviderMock(configuration: .init())) { _ in }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
|
|||||||
@@ -17,10 +17,12 @@ extension View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private struct BloomModifier: ViewModifier {
|
private struct BloomModifier: ViewModifier {
|
||||||
|
@Environment(\.colorScheme) private var colorScheme
|
||||||
|
|
||||||
@State private var standardAppearance = UINavigationBarAppearance()
|
@State private var standardAppearance = UINavigationBarAppearance()
|
||||||
@State private var scrollEdgeAppearance = UINavigationBarAppearance()
|
@State private var scrollEdgeAppearance = UINavigationBarAppearance()
|
||||||
|
|
||||||
@State private var bloomGradientImage: UIImage?
|
@State private var bloom = Bloom()
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
content
|
content
|
||||||
@@ -28,33 +30,39 @@ private struct BloomModifier: ViewModifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func configureBloom(controller: UIViewController) {
|
private func configureBloom(controller: UIViewController) {
|
||||||
guard controller.navigationItem.standardAppearance != standardAppearance,
|
if controller.navigationItem.standardAppearance == standardAppearance,
|
||||||
controller.navigationItem.scrollEdgeAppearance != scrollEdgeAppearance else {
|
controller.navigationItem.scrollEdgeAppearance == scrollEdgeAppearance,
|
||||||
|
canUse(bloom) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let image = makeBloomImage()
|
let bloom = makeBloom()
|
||||||
|
|
||||||
standardAppearance.configureWithDefaultBackground()
|
standardAppearance.configureWithDefaultBackground()
|
||||||
standardAppearance.backgroundImage = image
|
standardAppearance.backgroundImage = bloom.image
|
||||||
standardAppearance.backgroundImageContentMode = .scaleToFill
|
standardAppearance.backgroundImageContentMode = .scaleToFill
|
||||||
controller.navigationItem.standardAppearance = standardAppearance
|
controller.navigationItem.standardAppearance = standardAppearance
|
||||||
|
|
||||||
scrollEdgeAppearance.configureWithTransparentBackground()
|
scrollEdgeAppearance.configureWithTransparentBackground()
|
||||||
scrollEdgeAppearance.backgroundImage = image
|
scrollEdgeAppearance.backgroundImage = bloom.image
|
||||||
scrollEdgeAppearance.backgroundImageContentMode = .scaleToFill
|
scrollEdgeAppearance.backgroundImageContentMode = .scaleToFill
|
||||||
scrollEdgeAppearance.backgroundColor = .compound.bgCanvasDefault
|
scrollEdgeAppearance.backgroundColor = .compound.bgCanvasDefault
|
||||||
controller.navigationItem.scrollEdgeAppearance = scrollEdgeAppearance
|
controller.navigationItem.scrollEdgeAppearance = scrollEdgeAppearance
|
||||||
}
|
}
|
||||||
|
|
||||||
private func makeBloomImage() -> UIImage? {
|
private func makeBloom() -> Bloom {
|
||||||
if let bloomGradientImage {
|
if bloom.image != nil, canUse(bloom) {
|
||||||
return bloomGradientImage
|
return bloom
|
||||||
}
|
}
|
||||||
|
|
||||||
let newImage = ImageRenderer(content: bloomGradient).uiImage
|
// There's a bug somewhere when rendering in dark mode (which we've mistakenly not been doing)
|
||||||
Task { bloomGradientImage = newImage }
|
// which results in the first 5 stops not having any alpha, only the last one…
|
||||||
return newImage
|
let newImage = ImageRenderer(content: bloomGradient /* .colorScheme(colorScheme) */ ).uiImage
|
||||||
|
|
||||||
|
bloom.image = newImage
|
||||||
|
bloom.colorScheme = colorScheme
|
||||||
|
bloom.baseColor = .compound.gradientSubtleStop1
|
||||||
|
return bloom
|
||||||
}
|
}
|
||||||
|
|
||||||
private var bloomGradient: some View {
|
private var bloomGradient: some View {
|
||||||
@@ -64,4 +72,18 @@ private struct BloomModifier: ViewModifier {
|
|||||||
.ignoresSafeArea(edges: .all)
|
.ignoresSafeArea(edges: .all)
|
||||||
.frame(width: 256, height: 256)
|
.frame(width: 256, height: 256)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func canUse(_ bloom: Bloom) -> Bool {
|
||||||
|
// Don't check for a nil image in here, there's no point re-rendering over and over if the render fails.
|
||||||
|
bloom.colorScheme == colorScheme && bloom.baseColor == .compound.gradientSubtleStop1
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a class to avoid a "Modifying state during view update" warning when storing
|
||||||
|
// the result on the same run-loop - we want to avoid dispatching that to the next loop as
|
||||||
|
// that can result in further (unnecessary) renders being made.
|
||||||
|
class Bloom {
|
||||||
|
var image: UIImage?
|
||||||
|
var colorScheme: ColorScheme?
|
||||||
|
var baseColor: Color?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ struct HomeScreenContent: View {
|
|||||||
case .skeletons:
|
case .skeletons:
|
||||||
LazyVStack(spacing: 0) {
|
LazyVStack(spacing: 0) {
|
||||||
ForEach(context.viewState.visibleRooms) { room in
|
ForEach(context.viewState.visibleRooms) { room in
|
||||||
HomeScreenRoomCell(room: room, context: context, isSelected: false)
|
HomeScreenRoomCell(room: room, isSelected: false, mediaProvider: context.mediaProvider, action: context.send)
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
.shimmer() // Putting this directly on the LazyVStack creates an accordion animation on iOS 16.
|
.shimmer() // Putting this directly on the LazyVStack creates an accordion animation on iOS 16.
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,9 @@ struct HomeScreenRoomCell: View {
|
|||||||
@Environment(\.redactionReasons) private var redactionReasons
|
@Environment(\.redactionReasons) private var redactionReasons
|
||||||
|
|
||||||
let room: HomeScreenRoom
|
let room: HomeScreenRoom
|
||||||
let context: HomeScreenViewModel.Context
|
|
||||||
let isSelected: Bool
|
let isSelected: Bool
|
||||||
|
let mediaProvider: MediaProviderProtocol!
|
||||||
|
let action: (HomeScreenViewAction) -> Void
|
||||||
|
|
||||||
private let verticalInsets = 12.0
|
private let verticalInsets = 12.0
|
||||||
private let horizontalInsets = 16.0
|
private let horizontalInsets = 16.0
|
||||||
@@ -23,7 +24,7 @@ struct HomeScreenRoomCell: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
Button {
|
Button {
|
||||||
if let roomID = room.roomID {
|
if let roomID = room.roomID {
|
||||||
context.send(viewAction: .selectRoom(roomIdentifier: roomID))
|
action(.selectRoom(roomIdentifier: roomID))
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack(spacing: 16.0) {
|
HStack(spacing: 16.0) {
|
||||||
@@ -51,7 +52,7 @@ struct HomeScreenRoomCell: View {
|
|||||||
if dynamicTypeSize < .accessibility3 {
|
if dynamicTypeSize < .accessibility3 {
|
||||||
RoomAvatarImage(avatar: room.avatar,
|
RoomAvatarImage(avatar: room.avatar,
|
||||||
avatarSize: .room(on: .chats),
|
avatarSize: .room(on: .chats),
|
||||||
mediaProvider: context.mediaProvider)
|
mediaProvider: mediaProvider)
|
||||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||||
.accessibilityHidden(true)
|
.accessibilityHidden(true)
|
||||||
}
|
}
|
||||||
@@ -167,27 +168,25 @@ private extension View {
|
|||||||
|
|
||||||
struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
|
struct HomeScreenRoomCell_Previews: PreviewProvider, TestablePreview {
|
||||||
static let summaryProviderGeneric = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
static let summaryProviderGeneric = RoomSummaryProviderMock(.init(state: .loaded(.mockRooms)))
|
||||||
static let viewModelGeneric = makeViewModel(roomSummaryProvider: summaryProviderGeneric)
|
|
||||||
static let genericRooms = summaryProviderGeneric.roomListPublisher.value.compactMap(mockRoom)
|
static let genericRooms = summaryProviderGeneric.roomListPublisher.value.compactMap(mockRoom)
|
||||||
|
|
||||||
static let summaryProviderForNotificationsState = RoomSummaryProviderMock(.init(state: .loaded(.mockRoomsWithNotificationsState)))
|
static let summaryProviderForNotificationsState = RoomSummaryProviderMock(.init(state: .loaded(.mockRoomsWithNotificationsState)))
|
||||||
static let viewModelForNotificationsState = makeViewModel(roomSummaryProvider: summaryProviderForNotificationsState)
|
|
||||||
static let notificationsStateRooms = summaryProviderForNotificationsState.roomListPublisher.value.compactMap(mockRoom)
|
static let notificationsStateRooms = summaryProviderForNotificationsState.roomListPublisher.value.compactMap(mockRoom)
|
||||||
|
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
ForEach(genericRooms) { room in
|
ForEach(genericRooms) { room in
|
||||||
HomeScreenRoomCell(room: room, context: viewModelGeneric.context, isSelected: false)
|
HomeScreenRoomCell(room: room, isSelected: false, mediaProvider: MediaProviderMock(configuration: .init())) { _ in }
|
||||||
}
|
}
|
||||||
|
|
||||||
HomeScreenRoomCell(room: .placeholder(), context: viewModelGeneric.context, isSelected: false)
|
HomeScreenRoomCell(room: .placeholder(), isSelected: false, mediaProvider: MediaProviderMock(configuration: .init())) { _ in }
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
}
|
}
|
||||||
.previewDisplayName("Generic")
|
.previewDisplayName("Generic")
|
||||||
|
|
||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
ForEach(notificationsStateRooms) { room in
|
ForEach(notificationsStateRooms) { room in
|
||||||
HomeScreenRoomCell(room: room, context: viewModelForNotificationsState.context, isSelected: false)
|
HomeScreenRoomCell(room: room, isSelected: false, mediaProvider: MediaProviderMock(configuration: .init())) { _ in }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.previewLayout(.sizeThatFits)
|
.previewLayout(.sizeThatFits)
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ struct HomeScreenRoomList: View {
|
|||||||
ForEach(context.viewState.visibleRooms) { room in
|
ForEach(context.viewState.visibleRooms) { room in
|
||||||
switch room.type {
|
switch room.type {
|
||||||
case .placeholder:
|
case .placeholder:
|
||||||
HomeScreenRoomCell(room: room, context: context, isSelected: false)
|
HomeScreenRoomCell(room: room, isSelected: false, mediaProvider: context.mediaProvider, action: context.send)
|
||||||
.redacted(reason: .placeholder)
|
.redacted(reason: .placeholder)
|
||||||
case .invite:
|
case .invite:
|
||||||
HomeScreenInviteCell(room: room, context: context, hideInviteAvatars: context.viewState.hideInviteAvatars)
|
HomeScreenInviteCell(room: room, context: context, hideInviteAvatars: context.viewState.hideInviteAvatars)
|
||||||
@@ -33,7 +33,7 @@ struct HomeScreenRoomList: View {
|
|||||||
case .room:
|
case .room:
|
||||||
let isSelected = context.viewState.selectedRoomID == room.id
|
let isSelected = context.viewState.selectedRoomID == room.id
|
||||||
|
|
||||||
HomeScreenRoomCell(room: room, context: context, isSelected: isSelected)
|
HomeScreenRoomCell(room: room, isSelected: isSelected, mediaProvider: context.mediaProvider, action: context.send)
|
||||||
.contextMenu {
|
.contextMenu {
|
||||||
if room.badges.isDotShown {
|
if room.badges.isDotShown {
|
||||||
Button {
|
Button {
|
||||||
|
|||||||
+1
-1
@@ -72,7 +72,7 @@ packages:
|
|||||||
# path: ../matrix-rust-sdk
|
# path: ../matrix-rust-sdk
|
||||||
Compound:
|
Compound:
|
||||||
url: https://github.com/element-hq/compound-ios
|
url: https://github.com/element-hq/compound-ios
|
||||||
revision: 8d7ca2e413026735b724044847ff1ae3b40563f0
|
revision: 4ad0bd0acd825a905bb4955b32979fe34cc538eb
|
||||||
# path: ../compound-ios
|
# path: ../compound-ios
|
||||||
AnalyticsEvents:
|
AnalyticsEvents:
|
||||||
url: https://github.com/matrix-org/matrix-analytics-events
|
url: https://github.com/matrix-org/matrix-analytics-events
|
||||||
|
|||||||
Reference in New Issue
Block a user