Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Room screen: timeline message bubbles #91

Merged
merged 23 commits into from
Jun 23, 2022
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
a8a93ec
#34 Create `TimelineItemStylerView`
ismailgulek Jun 22, 2022
d959b9b
#34 Add styler view into different type of cells
ismailgulek Jun 22, 2022
0050766
#34 Use placeholder avatar image on room list
ismailgulek Jun 22, 2022
1d1324b
#34 Add `isOutgoing` param to event based timeline item
ismailgulek Jun 22, 2022
9b89841
#34 Compute `isOutgoing` for timeline items
ismailgulek Jun 22, 2022
f8097bc
#34 Update sender info view in timeline
ismailgulek Jun 22, 2022
8c05a63
#34 Update mock timeline items
ismailgulek Jun 22, 2022
1782f64
#34 Rename `EventBasedTimelineView` to `EventBasedTimelineSenderView`
ismailgulek Jun 22, 2022
f444d5e
#34 Change padding on timeline cells
ismailgulek Jun 22, 2022
133ed25
#34 Create `TimelineItemStylerView` to move content into a bubble if …
ismailgulek Jun 22, 2022
19a8630
#34 Use styler view in all of the timeline item views
ismailgulek Jun 22, 2022
9d7608c
#34 Make timestamp more readable on images
ismailgulek Jun 22, 2022
b4f82c1
#34 Little layout tweaks
ismailgulek Jun 22, 2022
a3ac72d
#34 Add changelog
ismailgulek Jun 22, 2022
c3e87b3
#34 Fix code smells
ismailgulek Jun 22, 2022
cf986a3
#34 Set text colors on timeline items
ismailgulek Jun 23, 2022
9992adc
#34 Fix background color of the timeline
ismailgulek Jun 23, 2022
1c30dcf
#34 Fix PR remarks
ismailgulek Jun 23, 2022
9c6680d
#34 Set background colors explicitly on remaining screens
ismailgulek Jun 23, 2022
25fb6ce
#34 Reduce min bubble width and make it a scaled metric
ismailgulek Jun 23, 2022
f742baa
#34 Refactor `PlaceholderAvatarImage` to accept a text only
ismailgulek Jun 23, 2022
b8da33b
#34 Fix code smell
ismailgulek Jun 23, 2022
26900ba
#34 Fix further comments
ismailgulek Jun 23, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E6D88E8AFFBF2C1D589C0FA /* UIConstants.swift */; };
3772354754450F2B54107E17 /* TemplateSimpleScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FF4EDB32B97910AAAFE632B2 /* TemplateSimpleScreenViewModelProtocol.swift */; };
38546A6010A2CF240EC9AF73 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6EA1D2CBAEA5D0BD00B90D1B /* BindableState.swift */; };
39AE84C8E5F2FE9D2DC7775C /* EventBasedTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56008790A9C4479A6B31FDF4 /* EventBasedTimelineView.swift */; };
39AE84C8E5F2FE9D2DC7775C /* EventBasedTimelineSenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 56008790A9C4479A6B31FDF4 /* EventBasedTimelineSenderView.swift */; };
3B770CB4DED51CC362C66D47 /* SettingsModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4990FDBDA96B88E214F92F48 /* SettingsModels.swift */; };
3C549A0BF39F8A854D45D9FD /* GZIP in Frameworks */ = {isa = PBXBuildFile; productRef = 997C7385E1A07E061D7E2100 /* GZIP */; };
3D325A1147F6281C57BFCDF6 /* EventBrief.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4411C0DA0087A1CB143E96FA /* EventBrief.swift */; };
Expand Down Expand Up @@ -222,6 +222,7 @@
EA31DD9043B91ECB8E45A9A6 /* ScreenshotDetectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F03C9D319676F3C0DC6B0203 /* ScreenshotDetectorTests.swift */; };
EA65360A0EC026DD83AC0CF5 /* AuthenticationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6CA5F386C7701C129398945 /* AuthenticationCoordinator.swift */; };
EBD6C79705B3DDB2F7E5F554 /* UserSessionStoreProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF1B52D0ABBA7091A991CAFE /* UserSessionStoreProtocol.swift */; };
EC8128A028620A970012F05B /* TimelineItemStylerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EC81289F28620A970012F05B /* TimelineItemStylerView.swift */; };
ED4F663C783E9A8C0E80B983 /* TemplateSimpleScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 47543EB19F3DCF308751F53C /* TemplateSimpleScreenViewModel.swift */; };
EE8491AD81F47DF3C192497B /* DecorationTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */; };
EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; };
Expand Down Expand Up @@ -376,7 +377,7 @@
534A5C8FCDE2CBC50266B9F2 /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = gl; path = gl.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
55BC11560C8A2598964FFA4C /* bs */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bs; path = bs.lproj/Localizable.strings; sourceTree = "<group>"; };
55D7187F6B0C0A651AC3DFFA /* in */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = in; path = in.lproj/Localizable.strings; sourceTree = "<group>"; };
56008790A9C4479A6B31FDF4 /* EventBasedTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineView.swift; sourceTree = "<group>"; };
56008790A9C4479A6B31FDF4 /* EventBasedTimelineSenderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedTimelineSenderView.swift; sourceTree = "<group>"; };
56F01DD1BBD4450E18115916 /* LabelledActivityIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LabelledActivityIndicatorView.swift; sourceTree = "<group>"; };
5773C86AF04AEF26515AD00C /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = sl.lproj/Localizable.strings; sourceTree = "<group>"; };
5872785B9C7934940146BFBA /* MXLogger.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MXLogger.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -576,6 +577,7 @@
E8FD25EB4DF66625B74E4505 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = "<group>"; };
E9D059BFE329BE09B6D96A9F /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ro; path = ro.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
EBE5502760CF6CA2D7201883 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.stringsdict; name = ja; path = ja.lproj/Localizable.stringsdict; sourceTree = "<group>"; };
EC81289F28620A970012F05B /* TimelineItemStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemStylerView.swift; sourceTree = "<group>"; };
ED1D792EB82506A19A72C8DE /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = "<group>"; };
EDB3E99D445CFCB3AA3F34FB /* FramePreferenceKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FramePreferenceKey.swift; sourceTree = "<group>"; };
EE8BCD14EFED23459A43FDFF /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = ja.lproj/Localizable.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1031,6 +1033,7 @@
B43AF03660F5FD4FFFA7F1CE /* TimelineItemContextMenu.swift */,
804F9B0FABE093C7284CD09B /* TimelineItemList.swift */,
874A1842477895F199567BD7 /* TimelineView.swift */,
EC81289F28620A970012F05B /* TimelineItemStylerView.swift */,
B7D3886505ECC85A06DA8258 /* Timeline */,
);
path = View;
Expand Down Expand Up @@ -1250,7 +1253,7 @@
isa = PBXGroup;
children = (
471EB7D96AFEA8D787659686 /* EmoteRoomTimelineView.swift */,
56008790A9C4479A6B31FDF4 /* EventBasedTimelineView.swift */,
56008790A9C4479A6B31FDF4 /* EventBasedTimelineSenderView.swift */,
F73FF1A33198F5FAE9D34B1F /* FormattedBodyText.swift */,
D0A45283CF1DB96E583BECA6 /* ImageRoomTimelineView.swift */,
B5B243E7818E5E9F6A4EDC7A /* NoticeRoomTimelineView.swift */,
Expand Down Expand Up @@ -1733,7 +1736,7 @@
6647430A45B4A8E692909A8F /* EmoteRoomTimelineItem.swift in Sources */,
68AC3C84E2B438036B174E30 /* EmoteRoomTimelineView.swift in Sources */,
02D8DF8EB7537EB4E9019DDB /* EventBasedTimelineItemProtocol.swift in Sources */,
39AE84C8E5F2FE9D2DC7775C /* EventBasedTimelineView.swift in Sources */,
39AE84C8E5F2FE9D2DC7775C /* EventBasedTimelineSenderView.swift in Sources */,
3D325A1147F6281C57BFCDF6 /* EventBrief.swift in Sources */,
418B4AEFD03DC7A6D2C9D5C8 /* EventBriefFactory.swift in Sources */,
F78C57B197DA74735FEBB42C /* EventBriefFactoryProtocol.swift in Sources */,
Expand Down Expand Up @@ -1843,6 +1846,7 @@
01CB8ACFA5E143E89C168CA8 /* TimelineItemContextMenu.swift in Sources */,
4D970CB606276717B43E2332 /* TimelineItemList.swift in Sources */,
500CB65ED116B81DA52FDAEE /* TimelineView.swift in Sources */,
EC8128A028620A970012F05B /* TimelineItemStylerView.swift in Sources */,
4669804D0369FBED4E8625D1 /* ToastViewPresenter.swift in Sources */,
9CB5129C83F75921E5E28028 /* ToastViewState.swift in Sources */,
36AC963F2F04069B7FF1AA0C /* UIConstants.swift in Sources */,
Expand Down
7 changes: 5 additions & 2 deletions ElementX/Sources/AppCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,14 +238,17 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
MXLog.error("Invalid room identifier: \(roomIdentifier)")
return
}
let userId = userSession.clientProxy.userIdentifier

let memberDetailProvider = memberDetailProviderManager.memberDetailProviderForRoomProxy(roomProxy)

let timelineItemFactory = RoomTimelineItemFactory(mediaProvider: userSession.mediaProvider,
let timelineItemFactory = RoomTimelineItemFactory(userId: userId,
mediaProvider: userSession.mediaProvider,
memberDetailProvider: memberDetailProvider,
attributedStringBuilder: AttributedStringBuilder())

let timelineController = RoomTimelineController(timelineProvider: RoomTimelineProvider(roomProxy: roomProxy),
let timelineController = RoomTimelineController(userId: userId,
timelineProvider: RoomTimelineProvider(roomProxy: roomProxy),
timelineItemFactory: timelineItemFactory,
mediaProvider: userSession.mediaProvider,
memberDetailProvider: memberDetailProvider)
Expand Down
19 changes: 12 additions & 7 deletions ElementX/Sources/Screens/BugReport/View/BugReport.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ struct BugReport: View {
}
.navigationTitle(ElementL10n.titleActivityBugReport)
}
.background(Color.element.background, ignoresSafeAreaEdges: .all)
}

/// The main content of the view to be shown in a scroll view.
Expand All @@ -59,11 +60,11 @@ struct BugReport: View {
.accessibilityIdentifier("reportBugDescription")
ZStack(alignment: .topLeading) {
RoundedRectangle(cornerRadius: 8, style: .continuous)
.fill(Color(UIColor.secondarySystemBackground))
.fill(Color.element.system)

if context.reportText.isEmpty {
Text(ElementL10n.sendBugReportPlaceholder)
.foregroundColor(Color(UIColor.placeholderText))
.foregroundColor(Color.element.secondaryContent)
.padding(.horizontal, 8)
.padding(.vertical, 12)
}
Expand Down Expand Up @@ -131,10 +132,14 @@ struct BugReport: View {

struct BugReport_Previews: PreviewProvider {
static var previews: some View {
Group {
let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), screenshot: Asset.Images.appLogo.image)
BugReport(context: viewModel.context)
.previewInterfaceOrientation(.portrait)
}
body.preferredColorScheme(.light)
body.preferredColorScheme(.dark)
}

@ViewBuilder
static var body: some View {
let viewModel = BugReportViewModel(bugReportService: MockBugReportService(), screenshot: Asset.Images.appLogo.image)
BugReport(context: viewModel.context)
.previewInterfaceOrientation(.portrait)
}
}
12 changes: 11 additions & 1 deletion ElementX/Sources/Screens/HomeScreen/View/HomeScreen.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct HomeScreen: View {
Section("Rooms") {
ForEach(context.viewState.unencryptedRooms) { room in
RoomCell(room: room, context: context)
.listRowBackground(Color.clear)
}

let other = context.viewState.encryptedRooms
Expand All @@ -44,6 +45,7 @@ struct HomeScreen: View {
RoomCell(room: room, context: context)
}
}
.listRowBackground(Color.clear)
}
}

Expand All @@ -60,6 +62,7 @@ struct HomeScreen: View {
RoomCell(room: room, context: context)
}
}
.listRowBackground(Color.clear)
}
}
}
Expand All @@ -69,6 +72,7 @@ struct HomeScreen: View {

Spacer()
}
.background(Color.element.background)
.ignoresSafeArea(.all, edges: .bottom)
.navigationBarTitleDisplayMode(.inline)
.toolbar {
Expand Down Expand Up @@ -127,7 +131,8 @@ struct RoomCell: View {
.frame(width: 40, height: 40)
.mask(Circle())
} else {
Image(systemName: "person.3")
PlaceholderAvatarImage(text: room.displayName ?? room.id)
.clipShape(Circle())
.frame(width: 40, height: 40)
}

Expand Down Expand Up @@ -168,6 +173,11 @@ struct RoomCell: View {

struct HomeScreen_Previews: PreviewProvider {
static var previews: some View {
body.preferredColorScheme(.light)
body.preferredColorScheme(.dark)
}

static var body: some View {
let viewModel = HomeScreenViewModel(attributedStringBuilder: AttributedStringBuilder())

let eventBrief = EventBrief(eventId: "id",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ struct RoomHeaderView: View {
.scaledToFill()
.accessibilityIdentifier("roomAvatarImage")
} else {
PlaceholderAvatarImage(firstCharacter: String(context.viewState.roomTitle.first ?? Character("")))
PlaceholderAvatarImage(text: context.viewState.roomTitle)
.accessibilityIdentifier("roomAvatarPlaceholderImage")
}
}
Expand Down Expand Up @@ -78,7 +78,7 @@ struct RoomHeaderView_Previews: PreviewProvider {
let viewModel = RoomScreenViewModel(timelineController: MockRoomTimelineController(),
timelineViewFactory: RoomTimelineViewFactory(),
roomName: "Some Room name",
roomAvatar: Asset.Images.appLogo.image,
roomAvatar: nil,
roomEncryptionBadge: Asset.Images.encryptionTrusted.image
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ struct RoomScreen: View {
RoomHeaderView(context: context)
}
}
.background(Color.element.background, ignoresSafeAreaEdges: .all)
}

private func sendMessage() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,17 @@ struct EmoteRoomTimelineView: View {

var body: some View {
VStack(alignment: .leading) {
EventBasedTimelineView(timelineItem: timelineItem)
HStack(alignment: .top) {
Image(systemName: "face.dashed").padding(.top, 1.0)
if let attributedComponents = timelineItem.attributedComponents {
FormattedBodyText(attributedComponents: attributedComponents)
} else {
Text(timelineItem.text)
TimelineItemStylerView(timelineItem: timelineItem) {
EventBasedTimelineSenderView(timelineItem: timelineItem)
} content: {
HStack(alignment: .top) {
Image(systemName: "face.dashed").padding(.top, 1.0)
if let attributedComponents = timelineItem.attributedComponents {
FormattedBodyText(attributedComponents: attributedComponents)
} else {
Text(timelineItem.text)
.foregroundColor(.element.primaryContent)
}
}
}
}
Expand Down Expand Up @@ -52,6 +56,7 @@ struct EmoteRoomTimelineView_Previews: PreviewProvider {
text: text,
timestamp: timestamp,
shouldShowSenderDetails: true,
isOutgoing: false,
senderId: senderId)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//
// EventBasedTimelineSenderView.swift
// ElementX
//
// Created by Stefan Ceriu on 18/03/2022.
// Copyright © 2022 Element. All rights reserved.
//

import Foundation
import SwiftUI

struct EventBasedTimelineSenderView: View {
let timelineItem: EventBasedTimelineItemProtocol

@ScaledMetric private var avatarSize = 26

var body: some View {
if timelineItem.shouldShowSenderDetails {
VStack {
Spacer()
HStack(alignment: .top, spacing: 4) {
avatar
Text(timelineItem.senderDisplayName ?? timelineItem.senderId)
.font(.body)
.foregroundColor(.element.primaryContent)
.fontWeight(.semibold)
.lineLimit(1)
}
}
}
}

@ViewBuilder private var avatar: some View {
ZStack(alignment: .center) {
if let avatar = timelineItem.senderAvatar {
Image(uiImage: avatar)
.resizable()
.scaledToFill()
.overlay(Circle().stroke(Color.element.accent))
} else {
PlaceholderAvatarImage(text: timelineItem.senderDisplayName ?? timelineItem.senderId)
}
}
.clipShape(Circle())
.frame(width: avatarSize, height: avatarSize)
.overlay(
Circle()
.stroke(Color.element.background, lineWidth: 2)
)

.animation(.default, value: timelineItem.senderAvatar)
}
}

struct EventBasedTimelineSenderView_Previews: PreviewProvider {
static var previews: some View {
body.preferredColorScheme(.light)
body.preferredColorScheme(.dark)
}

@ViewBuilder
static var body: some View {
VStack(alignment: .leading, spacing: 20.0) {
EventBasedTimelineSenderView(timelineItem: item1)

EventBasedTimelineSenderView(timelineItem: item2)
}
.frame(maxHeight: 160)
.previewLayout(.sizeThatFits)
}

private static var item1: EventBasedTimelineItemProtocol {
TextRoomTimelineItem(id: UUID().uuidString,
text: "Some text",
timestamp: "",
shouldShowSenderDetails: true,
isOutgoing: false,
senderId: "",
senderDisplayName: "Bob")
}

private static var item2: EventBasedTimelineItemProtocol {
TextRoomTimelineItem(id: UUID().uuidString,
text: "Some text",
timestamp: "",
shouldShowSenderDetails: true,
isOutgoing: false,
senderId: "",
senderDisplayName: "Some long display name for a user")
}

}
Loading