mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-04 01:20:12 +00:00
Introduced the RoomTimelineViewProvider with different timeline items/views. Added timeline date separators (currently breaks back pagination)
This commit is contained in:
@@ -19,7 +19,7 @@
|
|||||||
1859CF5527D7A6FF00E86E4E /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 1859CF5427D7A6FF00E86E4E /* MatrixRustSDK */; };
|
1859CF5527D7A6FF00E86E4E /* MatrixRustSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 1859CF5427D7A6FF00E86E4E /* MatrixRustSDK */; };
|
||||||
1863A3FC27BA5A9100B52E4D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A3FB27BA5A9100B52E4D /* KeychainAccess */; };
|
1863A3FC27BA5A9100B52E4D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A3FB27BA5A9100B52E4D /* KeychainAccess */; };
|
||||||
1863A40627BA6DFC00B52E4D /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A40527BA6DFC00B52E4D /* SwiftyBeaver */; };
|
1863A40627BA6DFC00B52E4D /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A40527BA6DFC00B52E4D /* SwiftyBeaver */; };
|
||||||
18A318DD27DA42C9000867CD /* RoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A318DB27DA42C9000867CD /* RoomTimelineItem.swift */; };
|
18A318DD27DA42C9000867CD /* RoomTimelineViewProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18A318DB27DA42C9000867CD /* RoomTimelineViewProvider.swift */; };
|
||||||
18F2BAD727D25B4000DD1988 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7327D25B4000DD1988 /* RoomProxyProtocol.swift */; };
|
18F2BAD727D25B4000DD1988 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7327D25B4000DD1988 /* RoomProxyProtocol.swift */; };
|
||||||
18F2BAD827D25B4000DD1988 /* RoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7427D25B4000DD1988 /* RoomProxy.swift */; };
|
18F2BAD827D25B4000DD1988 /* RoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7427D25B4000DD1988 /* RoomProxy.swift */; };
|
||||||
18F2BAD927D25B4000DD1988 /* MockRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7527D25B4000DD1988 /* MockRoomProxy.swift */; };
|
18F2BAD927D25B4000DD1988 /* MockRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7527D25B4000DD1988 /* MockRoomProxy.swift */; };
|
||||||
@@ -80,6 +80,12 @@
|
|||||||
18F2BB2227D25D4600DD1988 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BACF27D25B4000DD1988 /* LoginScreenUITests.swift */; };
|
18F2BB2227D25D4600DD1988 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BACF27D25B4000DD1988 /* LoginScreenUITests.swift */; };
|
||||||
18F2BB2827D2647A00DD1988 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2727D2647A00DD1988 /* MockRoomTimelineController.swift */; };
|
18F2BB2827D2647A00DD1988 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2727D2647A00DD1988 /* MockRoomTimelineController.swift */; };
|
||||||
18F2BB2A27D2648900DD1988 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2927D2648900DD1988 /* RoomTimelineControllerProtocol.swift */; };
|
18F2BB2A27D2648900DD1988 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2927D2648900DD1988 /* RoomTimelineControllerProtocol.swift */; };
|
||||||
|
18F9889827DB7473002F48B4 /* ImageRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F9889727DB7473002F48B4 /* ImageRoomTimelineItem.swift */; };
|
||||||
|
18F9889A27DB747B002F48B4 /* TextRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F9889927DB747B002F48B4 /* TextRoomTimelineItem.swift */; };
|
||||||
|
18F9889C27DB7491002F48B4 /* SeparatorRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F9889B27DB7491002F48B4 /* SeparatorRoomTimelineItem.swift */; };
|
||||||
|
18F9889E27DB752B002F48B4 /* ImageRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F9889D27DB752B002F48B4 /* ImageRoomTimelineView.swift */; };
|
||||||
|
18F988A027DB7532002F48B4 /* TextRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F9889F27DB7532002F48B4 /* TextRoomTimelineView.swift */; };
|
||||||
|
18F988A227DB753B002F48B4 /* SeparatorRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F988A127DB753B002F48B4 /* SeparatorRoomTimelineView.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -111,7 +117,7 @@
|
|||||||
1850256727B6A135002E6B18 /* ElementX.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = ElementX.entitlements; sourceTree = "<group>"; };
|
1850256727B6A135002E6B18 /* ElementX.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = ElementX.entitlements; sourceTree = "<group>"; };
|
||||||
1850256827B6A135002E6B18 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
1850256827B6A135002E6B18 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
1850256A27B6A135002E6B18 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
1850256A27B6A135002E6B18 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
18A318DB27DA42C9000867CD /* RoomTimelineItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomTimelineItem.swift; sourceTree = "<group>"; };
|
18A318DB27DA42C9000867CD /* RoomTimelineViewProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomTimelineViewProvider.swift; sourceTree = "<group>"; };
|
||||||
18F2BA7327D25B4000DD1988 /* RoomProxyProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomProxyProtocol.swift; sourceTree = "<group>"; };
|
18F2BA7327D25B4000DD1988 /* RoomProxyProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomProxyProtocol.swift; sourceTree = "<group>"; };
|
||||||
18F2BA7427D25B4000DD1988 /* RoomProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomProxy.swift; sourceTree = "<group>"; };
|
18F2BA7427D25B4000DD1988 /* RoomProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomProxy.swift; sourceTree = "<group>"; };
|
||||||
18F2BA7527D25B4000DD1988 /* MockRoomProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRoomProxy.swift; sourceTree = "<group>"; };
|
18F2BA7527D25B4000DD1988 /* MockRoomProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRoomProxy.swift; sourceTree = "<group>"; };
|
||||||
@@ -172,6 +178,12 @@
|
|||||||
18F2BB1927D25BE800DD1988 /* RoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineController.swift; sourceTree = "<group>"; };
|
18F2BB1927D25BE800DD1988 /* RoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineController.swift; sourceTree = "<group>"; };
|
||||||
18F2BB2727D2647A00DD1988 /* MockRoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineController.swift; sourceTree = "<group>"; };
|
18F2BB2727D2647A00DD1988 /* MockRoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomTimelineController.swift; sourceTree = "<group>"; };
|
||||||
18F2BB2927D2648900DD1988 /* RoomTimelineControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerProtocol.swift; sourceTree = "<group>"; };
|
18F2BB2927D2648900DD1988 /* RoomTimelineControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineControllerProtocol.swift; sourceTree = "<group>"; };
|
||||||
|
18F9889727DB7473002F48B4 /* ImageRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
|
18F9889927DB747B002F48B4 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
|
18F9889B27DB7491002F48B4 /* SeparatorRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineItem.swift; sourceTree = "<group>"; };
|
||||||
|
18F9889D27DB752B002F48B4 /* ImageRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
|
18F9889F27DB7532002F48B4 /* TextRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
|
18F988A127DB753B002F48B4 /* SeparatorRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SeparatorRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||||
18FE279627C7B85300016375 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
18FE279627C7B85300016375 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
@@ -277,7 +289,13 @@
|
|||||||
18A318D927DA42C9000867CD /* TimelineItems */ = {
|
18A318D927DA42C9000867CD /* TimelineItems */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
18A318DB27DA42C9000867CD /* RoomTimelineItem.swift */,
|
18A318DB27DA42C9000867CD /* RoomTimelineViewProvider.swift */,
|
||||||
|
18F9889727DB7473002F48B4 /* ImageRoomTimelineItem.swift */,
|
||||||
|
18F9889D27DB752B002F48B4 /* ImageRoomTimelineView.swift */,
|
||||||
|
18F9889927DB747B002F48B4 /* TextRoomTimelineItem.swift */,
|
||||||
|
18F9889F27DB7532002F48B4 /* TextRoomTimelineView.swift */,
|
||||||
|
18F9889B27DB7491002F48B4 /* SeparatorRoomTimelineItem.swift */,
|
||||||
|
18F988A127DB753B002F48B4 /* SeparatorRoomTimelineView.swift */,
|
||||||
);
|
);
|
||||||
path = TimelineItems;
|
path = TimelineItems;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -735,6 +753,7 @@
|
|||||||
18F2BAED27D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift in Sources */,
|
18F2BAED27D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift in Sources */,
|
||||||
18F2BB0F27D25B4000DD1988 /* RoomScreen.swift in Sources */,
|
18F2BB0F27D25B4000DD1988 /* RoomScreen.swift in Sources */,
|
||||||
18F2BAFF27D25B4000DD1988 /* HomeScreenModels.swift in Sources */,
|
18F2BAFF27D25B4000DD1988 /* HomeScreenModels.swift in Sources */,
|
||||||
|
18F9889C27DB7491002F48B4 /* SeparatorRoomTimelineItem.swift in Sources */,
|
||||||
18F2BB1527D25B4000DD1988 /* LoginScreenViewModelProtocol.swift in Sources */,
|
18F2BB1527D25B4000DD1988 /* LoginScreenViewModelProtocol.swift in Sources */,
|
||||||
18F2BAEB27D25B4000DD1988 /* LabelledActivityIndicatorView.swift in Sources */,
|
18F2BAEB27D25B4000DD1988 /* LabelledActivityIndicatorView.swift in Sources */,
|
||||||
18F2BAE427D25B4000DD1988 /* Presentable.swift in Sources */,
|
18F2BAE427D25B4000DD1988 /* Presentable.swift in Sources */,
|
||||||
@@ -745,6 +764,7 @@
|
|||||||
18F2BAD927D25B4000DD1988 /* MockRoomProxy.swift in Sources */,
|
18F2BAD927D25B4000DD1988 /* MockRoomProxy.swift in Sources */,
|
||||||
18F2BAE727D25B4000DD1988 /* RoundedToastView.swift in Sources */,
|
18F2BAE727D25B4000DD1988 /* RoundedToastView.swift in Sources */,
|
||||||
18F2BAF227D25B4000DD1988 /* WeakDictionaryKeyReference.swift in Sources */,
|
18F2BAF227D25B4000DD1988 /* WeakDictionaryKeyReference.swift in Sources */,
|
||||||
|
18F988A027DB7532002F48B4 /* TextRoomTimelineView.swift in Sources */,
|
||||||
18F2BAE027D25B4000DD1988 /* NavigationRouter.swift in Sources */,
|
18F2BAE027D25B4000DD1988 /* NavigationRouter.swift in Sources */,
|
||||||
18F2BAF627D25B4000DD1988 /* Coordinator.swift in Sources */,
|
18F2BAF627D25B4000DD1988 /* Coordinator.swift in Sources */,
|
||||||
18F2BAEA27D25B4000DD1988 /* ActivityCenter.swift in Sources */,
|
18F2BAEA27D25B4000DD1988 /* ActivityCenter.swift in Sources */,
|
||||||
@@ -770,19 +790,23 @@
|
|||||||
18F2BAFE27D25B4000DD1988 /* HomeScreenViewModelProtocol.swift in Sources */,
|
18F2BAFE27D25B4000DD1988 /* HomeScreenViewModelProtocol.swift in Sources */,
|
||||||
18F2BAE827D25B4000DD1988 /* RectangleToastView.swift in Sources */,
|
18F2BAE827D25B4000DD1988 /* RectangleToastView.swift in Sources */,
|
||||||
18F2BB1627D25B4000DD1988 /* LoginScreenModels.swift in Sources */,
|
18F2BB1627D25B4000DD1988 /* LoginScreenModels.swift in Sources */,
|
||||||
|
18F9889E27DB752B002F48B4 /* ImageRoomTimelineView.swift in Sources */,
|
||||||
18F2BADA27D25B4000DD1988 /* RoomTimelineProvider.swift in Sources */,
|
18F2BADA27D25B4000DD1988 /* RoomTimelineProvider.swift in Sources */,
|
||||||
|
18F9889827DB7473002F48B4 /* ImageRoomTimelineItem.swift in Sources */,
|
||||||
18F2BB0027D25B4000DD1988 /* HomeScreen.swift in Sources */,
|
18F2BB0027D25B4000DD1988 /* HomeScreen.swift in Sources */,
|
||||||
18F2BB2827D2647A00DD1988 /* MockRoomTimelineController.swift in Sources */,
|
18F2BB2827D2647A00DD1988 /* MockRoomTimelineController.swift in Sources */,
|
||||||
18F2BB0127D25B4000DD1988 /* HomeScreenViewModel.swift in Sources */,
|
18F2BB0127D25B4000DD1988 /* HomeScreenViewModel.swift in Sources */,
|
||||||
18F2BAF027D25B4000DD1988 /* ActivityDismissal.swift in Sources */,
|
18F2BAF027D25B4000DD1988 /* ActivityDismissal.swift in Sources */,
|
||||||
18F2BADD27D25B4000DD1988 /* KeychainController.swift in Sources */,
|
18F2BADD27D25B4000DD1988 /* KeychainController.swift in Sources */,
|
||||||
18A318DD27DA42C9000867CD /* RoomTimelineItem.swift in Sources */,
|
18A318DD27DA42C9000867CD /* RoomTimelineViewProvider.swift in Sources */,
|
||||||
18F2BAFB27D25B4000DD1988 /* HomeScreenCoordinator.swift in Sources */,
|
18F2BAFB27D25B4000DD1988 /* HomeScreenCoordinator.swift in Sources */,
|
||||||
18F2BB0C27D25B4000DD1988 /* RoomScreenCoordinator.swift in Sources */,
|
18F2BB0C27D25B4000DD1988 /* RoomScreenCoordinator.swift in Sources */,
|
||||||
18F2BB0E27D25B4000DD1988 /* RoomScreenViewModelProtocol.swift in Sources */,
|
18F2BB0E27D25B4000DD1988 /* RoomScreenViewModelProtocol.swift in Sources */,
|
||||||
18F2BB0D27D25B4000DD1988 /* RoomScreenViewModel.swift in Sources */,
|
18F2BB0D27D25B4000DD1988 /* RoomScreenViewModel.swift in Sources */,
|
||||||
|
18F988A227DB753B002F48B4 /* SeparatorRoomTimelineView.swift in Sources */,
|
||||||
18F2BAE127D25B4000DD1988 /* RootRouterType.swift in Sources */,
|
18F2BAE127D25B4000DD1988 /* RootRouterType.swift in Sources */,
|
||||||
1850256C27B6A135002E6B18 /* AppCoordinator.swift in Sources */,
|
1850256C27B6A135002E6B18 /* AppCoordinator.swift in Sources */,
|
||||||
|
18F9889A27DB747B002F48B4 /* TextRoomTimelineItem.swift in Sources */,
|
||||||
18F2BADC27D25B4000DD1988 /* UserSession.swift in Sources */,
|
18F2BADC27D25B4000DD1988 /* UserSession.swift in Sources */,
|
||||||
18F2BAEF27D25B4000DD1988 /* ActivityRequest.swift in Sources */,
|
18F2BAEF27D25B4000DD1988 /* ActivityRequest.swift in Sources */,
|
||||||
18F2BAEE27D25B4000DD1988 /* Activity.swift in Sources */,
|
18F2BAEE27D25B4000DD1988 /* Activity.swift in Sources */,
|
||||||
|
|||||||
@@ -117,12 +117,16 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
|
|||||||
let parameters = RoomScreenCoordinatorParameters(roomProxy: roomProxy)
|
let parameters = RoomScreenCoordinatorParameters(roomProxy: roomProxy)
|
||||||
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
let coordinator = RoomScreenCoordinator(parameters: parameters)
|
||||||
|
|
||||||
coordinator.completion = { [weak self] result in
|
coordinator.completion = { _ in
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.add(childCoordinator: coordinator)
|
self.add(childCoordinator: coordinator)
|
||||||
self.navigationRouter.push(coordinator)
|
self.navigationRouter.push(coordinator) { [weak self] in
|
||||||
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
self.remove(childCoordinator: coordinator)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,79 +24,76 @@ struct HomeScreen: View {
|
|||||||
// MARK: Views
|
// MARK: Views
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView {
|
VStack(spacing: 16.0) {
|
||||||
VStack(spacing: 16.0) {
|
if context.viewState.isLoadingRooms {
|
||||||
if context.viewState.isLoadingRooms {
|
VStack {
|
||||||
VStack {
|
Text("Loading rooms")
|
||||||
Text("Loading rooms")
|
ProgressView()
|
||||||
ProgressView()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
List {
|
|
||||||
Section("Rooms") {
|
|
||||||
ForEach(context.viewState.unencryptedRooms) { room in
|
|
||||||
RoomCell(room: room, context: context)
|
|
||||||
}
|
|
||||||
|
|
||||||
let other = context.viewState.encryptedRooms
|
|
||||||
|
|
||||||
if other.count > 0 {
|
|
||||||
DisclosureGroup("Encrypted") {
|
|
||||||
ForEach(other) { room in
|
|
||||||
RoomCell(room: room, context: context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Section("People") {
|
|
||||||
ForEach(context.viewState.unencryptedDMs) { room in
|
|
||||||
RoomCell(room: room, context: context)
|
|
||||||
}
|
|
||||||
|
|
||||||
let other = context.viewState.encryptedDMs
|
|
||||||
|
|
||||||
if other.count > 0 {
|
|
||||||
DisclosureGroup("Encrypted") {
|
|
||||||
ForEach(other) { room in
|
|
||||||
RoomCell(room: room, context: context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.listStyle(.plain)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
List {
|
||||||
|
Section("Rooms") {
|
||||||
|
ForEach(context.viewState.unencryptedRooms) { room in
|
||||||
|
RoomCell(room: room, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
let other = context.viewState.encryptedRooms
|
||||||
|
|
||||||
|
if other.count > 0 {
|
||||||
|
DisclosureGroup("Encrypted") {
|
||||||
|
ForEach(other) { room in
|
||||||
|
RoomCell(room: room, context: context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Section("People") {
|
||||||
|
ForEach(context.viewState.unencryptedDMs) { room in
|
||||||
|
RoomCell(room: room, context: context)
|
||||||
|
}
|
||||||
|
|
||||||
|
let other = context.viewState.encryptedDMs
|
||||||
|
|
||||||
|
if other.count > 0 {
|
||||||
|
DisclosureGroup("Encrypted") {
|
||||||
|
ForEach(other) { room in
|
||||||
|
RoomCell(room: room, context: context)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.listStyle(.plain)
|
||||||
}
|
}
|
||||||
.ignoresSafeArea(.all, edges: .bottom)
|
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
Spacer()
|
||||||
.toolbar {
|
}
|
||||||
ToolbarItem(placement: .navigationBarLeading) {
|
.ignoresSafeArea(.all, edges: .bottom)
|
||||||
HStack {
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
if let avatar = context.viewState.userAvatar {
|
.toolbar {
|
||||||
Image(uiImage: avatar)
|
ToolbarItem(placement: .navigationBarLeading) {
|
||||||
.resizable()
|
HStack {
|
||||||
.scaledToFill()
|
if let avatar = context.viewState.userAvatar {
|
||||||
.frame(width: 40, height: 40, alignment: .center)
|
Image(uiImage: avatar)
|
||||||
.mask(Circle())
|
.resizable()
|
||||||
} else {
|
.scaledToFill()
|
||||||
let _ = context.send(viewAction: .loadUserAvatar)
|
.frame(width: 40, height: 40, alignment: .center)
|
||||||
}
|
.mask(Circle())
|
||||||
Text("Hello, \(context.viewState.userDisplayName)!")
|
} else {
|
||||||
.font(.subheadline)
|
let _ = context.send(viewAction: .loadUserAvatar)
|
||||||
.fontWeight(.bold)
|
|
||||||
}
|
}
|
||||||
|
Text("Hello, \(context.viewState.userDisplayName)!")
|
||||||
|
.font(.subheadline)
|
||||||
|
.fontWeight(.bold)
|
||||||
}
|
}
|
||||||
ToolbarItem(placement: .navigationBarTrailing) {
|
}
|
||||||
Button("Logout") {
|
ToolbarItem(placement: .navigationBarTrailing) {
|
||||||
context.send(viewAction: .logout)
|
Button("Logout") {
|
||||||
}
|
context.send(viewAction: .logout)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.navigationViewStyle(StackNavigationViewStyle())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ enum RoomScreenViewModelResult {
|
|||||||
|
|
||||||
enum RoomScreenViewAction {
|
enum RoomScreenViewAction {
|
||||||
case loadPreviousPage
|
case loadPreviousPage
|
||||||
|
case itemAppeared(id: String)
|
||||||
|
case itemDisappeared(id: String)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RoomScreenViewState: BindableState {
|
struct RoomScreenViewState: BindableState {
|
||||||
var roomTitle: String = ""
|
var roomTitle: String = ""
|
||||||
var messages: [RoomTimelineItem] = []
|
var timelineItems: [RoomTimelineViewProvider] = []
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,14 +41,14 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
super.init(initialViewState: RoomScreenViewState())
|
super.init(initialViewState: RoomScreenViewState())
|
||||||
|
|
||||||
state.roomTitle = roomProxy.name ?? ""
|
state.roomTitle = roomProxy.name ?? ""
|
||||||
state.messages = timelineController.timelineItems
|
state.timelineItems = timelineController.timelineItems
|
||||||
|
|
||||||
timelineController.callbacks.sink { [weak self] callback in
|
timelineController.callbacks.sink { [weak self] callback in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
switch callback {
|
switch callback {
|
||||||
case .updatedTimelineItems:
|
case .updatedTimelineItems:
|
||||||
self.state.messages = timelineController.timelineItems
|
self.state.timelineItems = timelineController.timelineItems
|
||||||
}
|
}
|
||||||
}.store(in: &cancellables)
|
}.store(in: &cancellables)
|
||||||
}
|
}
|
||||||
@@ -59,6 +59,10 @@ class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol
|
|||||||
switch viewAction {
|
switch viewAction {
|
||||||
case .loadPreviousPage:
|
case .loadPreviousPage:
|
||||||
timelineController.paginateBackwards(Constants.backPaginationPageSize)
|
timelineController.paginateBackwards(Constants.backPaginationPageSize)
|
||||||
|
case .itemAppeared:
|
||||||
|
break
|
||||||
|
case .itemDisappeared:
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import Combine
|
|||||||
struct RoomScreen: View {
|
struct RoomScreen: View {
|
||||||
|
|
||||||
@State private var scrollViewObserver: ScrollViewObserver = ScrollViewObserver()
|
@State private var scrollViewObserver: ScrollViewObserver = ScrollViewObserver()
|
||||||
@State private var messages: [RoomTimelineItem] = []
|
@State private var timelineItems: [RoomTimelineViewProvider] = []
|
||||||
|
|
||||||
@State private var didRequestBackPagination = false
|
@State private var didRequestBackPagination = false
|
||||||
@State private var hasPendingMessages = false
|
@State private var hasPendingMessages = false
|
||||||
@@ -58,8 +58,15 @@ struct RoomScreen: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ForEach(messages) { message in
|
ForEach(timelineItems) { timelineItem in
|
||||||
message.body
|
timelineItem
|
||||||
|
.listRowSeparator(.hidden)
|
||||||
|
.onAppear {
|
||||||
|
context.send(viewAction: .itemAppeared(id: timelineItem.id))
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
context.send(viewAction: .itemDisappeared(id: timelineItem.id))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Color.clear
|
Color.clear
|
||||||
@@ -85,8 +92,8 @@ struct RoomScreen: View {
|
|||||||
// When the view state changes check whether the user is interacting with the scroll view.
|
// When the view state changes check whether the user is interacting with the scroll view.
|
||||||
// Updating in that case causes undesired scrolling. Delay until the scroll view stops scrolling.
|
// Updating in that case causes undesired scrolling. Delay until the scroll view stops scrolling.
|
||||||
// Also store previous top most message identifier to have something to scroll to after the update.
|
// Also store previous top most message identifier to have something to scroll to after the update.
|
||||||
.onChange(of: context.viewState.messages) { newValue in
|
.onChange(of: context.viewState.timelineItems) { newValue in
|
||||||
previousTopMostMessageIdentifier = messages.first?.id
|
previousTopMostMessageIdentifier = timelineItems.first?.id
|
||||||
wasBottomVisible = scrollViewObserver.isBottomVisible
|
wasBottomVisible = scrollViewObserver.isBottomVisible
|
||||||
|
|
||||||
if scrollViewObserver.isTracking == true {
|
if scrollViewObserver.isTracking == true {
|
||||||
@@ -94,17 +101,17 @@ struct RoomScreen: View {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
messages = newValue
|
timelineItems = newValue
|
||||||
}
|
}
|
||||||
// Check if we have pending messages to apply and apply them when the scroll finishes scrolling
|
// Check if we have pending messages to apply and apply them when the scroll finishes scrolling
|
||||||
.onReceive(scrollViewObserver.didEndScrolling, perform: {
|
.onReceive(scrollViewObserver.didEndScrolling, perform: {
|
||||||
if hasPendingMessages {
|
if hasPendingMessages {
|
||||||
messages = context.viewState.messages
|
timelineItems = context.viewState.timelineItems
|
||||||
hasPendingMessages = false
|
hasPendingMessages = false
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Process timeline updates
|
// Process timeline updates
|
||||||
.onChange(of: messages, perform: { _ in
|
.onChange(of: timelineItems, perform: { _ in
|
||||||
if didRequestBackPagination && wasBottomVisible {
|
if didRequestBackPagination && wasBottomVisible {
|
||||||
scrollViewProxy.scrollTo(timelineBottomAnchor, anchor: .bottom)
|
scrollViewProxy.scrollTo(timelineBottomAnchor, anchor: .bottom)
|
||||||
} else if didRequestBackPagination == false {
|
} else if didRequestBackPagination == false {
|
||||||
|
|||||||
@@ -10,12 +10,16 @@ import Foundation
|
|||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
class MockRoomTimelineController: RoomTimelineControllerProtocol {
|
||||||
let timelineItems: [RoomTimelineItem] = [RoomTimelineItem.text(id: UUID().uuidString, senderDisplayName: "Anne", text: "You rock!", originServerTs: .now, shouldShowSenderDetails: true),
|
|
||||||
RoomTimelineItem.text(id: UUID().uuidString, senderDisplayName: "Anne", text: "Some other message from Anne", originServerTs: .now, shouldShowSenderDetails: false),
|
|
||||||
RoomTimelineItem.sectionTitle(id: UUID().uuidString, text: "The next day"),
|
|
||||||
RoomTimelineItem.text(id: UUID().uuidString, senderDisplayName: "Bob", text: "You rule!", originServerTs: .now, shouldShowSenderDetails: true)]
|
|
||||||
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
||||||
|
|
||||||
|
let timelineItems: [RoomTimelineViewProvider] =
|
||||||
|
[RoomTimelineViewProvider.separator(.init(id: UUID().uuidString, text: "Yesterday")),
|
||||||
|
RoomTimelineViewProvider.text(.init(id: UUID().uuidString, senderDisplayName: "Alice", text: "You rock!", timestamp: "10:10 AM", shouldShowSenderDetails: true)),
|
||||||
|
RoomTimelineViewProvider.text(.init(id: UUID().uuidString, senderDisplayName: "Alice", text: "You also rule!", timestamp: "10:11 AM", shouldShowSenderDetails: false)),
|
||||||
|
RoomTimelineViewProvider.separator(.init(id: UUID().uuidString, text: "Today")),
|
||||||
|
RoomTimelineViewProvider.text(.init(id: UUID().uuidString, senderDisplayName: "Bob", text: "You too!", timestamp: "5 PM", shouldShowSenderDetails: true))]
|
||||||
|
|
||||||
func paginateBackwards(_ count: UInt) {
|
func paginateBackwards(_ count: UInt) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,20 +14,13 @@ enum RoomTimelineControllerCallback {
|
|||||||
case updatedTimelineItems
|
case updatedTimelineItems
|
||||||
}
|
}
|
||||||
|
|
||||||
private var sectionTitleDateFormatter: DateFormatter = {
|
|
||||||
let dateFormatter = DateFormatter()
|
|
||||||
dateFormatter.dateStyle = .long
|
|
||||||
dateFormatter.timeStyle = .none
|
|
||||||
return dateFormatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
class RoomTimelineController: RoomTimelineControllerProtocol {
|
class RoomTimelineController: RoomTimelineControllerProtocol {
|
||||||
private let timelineProvider: RoomTimelineProvider
|
private let timelineProvider: RoomTimelineProvider
|
||||||
private var cancellables = Set<AnyCancellable>()
|
private var cancellables = Set<AnyCancellable>()
|
||||||
|
|
||||||
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
|
||||||
|
|
||||||
private(set) var timelineItems = [RoomTimelineItem]()
|
private(set) var timelineItems = [RoomTimelineViewProvider]()
|
||||||
|
|
||||||
init(timelineProvider: RoomTimelineProvider) {
|
init(timelineProvider: RoomTimelineProvider) {
|
||||||
self.timelineProvider = timelineProvider
|
self.timelineProvider = timelineProvider
|
||||||
@@ -37,32 +30,34 @@ class RoomTimelineController: RoomTimelineControllerProtocol {
|
|||||||
|
|
||||||
switch callback {
|
switch callback {
|
||||||
case .updatedMessages:
|
case .updatedMessages:
|
||||||
var newTimelineItems = [RoomTimelineItem]()
|
var newTimelineItems = [RoomTimelineViewProvider]()
|
||||||
|
|
||||||
var previousMessage: Message?
|
var previousMessage: Message?
|
||||||
var previousSender: String?
|
|
||||||
for message in self.timelineProvider.messages {
|
for message in self.timelineProvider.messages {
|
||||||
let timestamp = Date(timeIntervalSince1970: TimeInterval(message.originServerTs()))
|
let timestamp = Date(timeIntervalSince1970: TimeInterval(message.originServerTs()))
|
||||||
|
|
||||||
let areMessagesFromTheSameDay = self.haveSameDay(lhs: previousMessage, rhs: message)
|
let areMessagesFromTheSameDay = self.haveSameDay(lhs: previousMessage, rhs: message)
|
||||||
// let shouldAddSectionHeader = !areMessagesFromTheSameDay
|
let shouldAddSectionHeader = !areMessagesFromTheSameDay
|
||||||
//
|
|
||||||
// if shouldAddSectionHeader {
|
|
||||||
// newTimelineItems.append(RoomTimelineItem.sectionTitle(id: message.id(),
|
|
||||||
// text: sectionTitleDateFormatter.string(from: timestamp)))
|
|
||||||
// }
|
|
||||||
|
|
||||||
let areMessagesFromTheSameSender = previousSender == message.sender()
|
if shouldAddSectionHeader {
|
||||||
|
let item = SeparatorRoomTimelineItem(id: timestamp.ISO8601Format(),
|
||||||
|
text: timestamp.formatted(date: .long, time: .omitted))
|
||||||
|
|
||||||
|
newTimelineItems.append(RoomTimelineViewProvider.separator(item))
|
||||||
|
}
|
||||||
|
|
||||||
|
let areMessagesFromTheSameSender = (previousMessage?.sender() == message.sender())
|
||||||
let shouldShowSenderDetails = !areMessagesFromTheSameSender || !areMessagesFromTheSameDay
|
let shouldShowSenderDetails = !areMessagesFromTheSameSender || !areMessagesFromTheSameDay
|
||||||
|
|
||||||
newTimelineItems.append(RoomTimelineItem.text(id: message.id(),
|
let item = TextRoomTimelineItem(id: message.id(),
|
||||||
senderDisplayName: message.sender(),
|
senderDisplayName: message.sender(),
|
||||||
text: message.content(),
|
text: message.content(),
|
||||||
originServerTs: timestamp,
|
timestamp: timestamp.formatted(date: .omitted, time: .shortened),
|
||||||
shouldShowSenderDetails: shouldShowSenderDetails))
|
shouldShowSenderDetails: shouldShowSenderDetails)
|
||||||
|
|
||||||
|
newTimelineItems.append(RoomTimelineViewProvider.text(item))
|
||||||
|
|
||||||
previousMessage = message
|
previousMessage = message
|
||||||
previousSender = message.sender()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.timelineItems = newTimelineItems
|
self.timelineItems = newTimelineItems
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import Foundation
|
|||||||
import Combine
|
import Combine
|
||||||
|
|
||||||
protocol RoomTimelineControllerProtocol {
|
protocol RoomTimelineControllerProtocol {
|
||||||
var timelineItems: [RoomTimelineItem] { get }
|
var timelineItems: [RoomTimelineViewProvider] { get }
|
||||||
var callbacks: PassthroughSubject<RoomTimelineControllerCallback, Never> { get }
|
var callbacks: PassthroughSubject<RoomTimelineControllerCallback, Never> { get }
|
||||||
|
|
||||||
func paginateBackwards(_ count: UInt)
|
func paginateBackwards(_ count: UInt)
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// ImageRoomTimelineItem.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 11/03/2022.
|
||||||
|
// Copyright © 2022 Element. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct ImageRoomTimelineItem: Identifiable, Equatable {
|
||||||
|
let id: String
|
||||||
|
let text: String
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
//
|
||||||
|
// ImageRoomTimelineView.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 11/03/2022.
|
||||||
|
// Copyright © 2022 Element. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct ImageRoomTimelineView: View {
|
||||||
|
let timelineItem: ImageRoomTimelineItem
|
||||||
|
var loadedImage: UIImage?
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
if let loadedImage = loadedImage {
|
||||||
|
Image(uiImage: loadedImage)
|
||||||
|
} else {
|
||||||
|
ProgressView()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
//
|
|
||||||
// TextRoomTimelineItem.swift
|
|
||||||
// ElementX
|
|
||||||
//
|
|
||||||
// Created by Stefan Ceriu on 04.03.2022.
|
|
||||||
// Copyright © 2022 Element. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import SwiftUI
|
|
||||||
|
|
||||||
private var dateFormatter: DateFormatter = {
|
|
||||||
let dateFormatter = DateFormatter()
|
|
||||||
dateFormatter.dateStyle = .none
|
|
||||||
dateFormatter.timeStyle = .short
|
|
||||||
return dateFormatter
|
|
||||||
}()
|
|
||||||
|
|
||||||
enum RoomTimelineItem: Identifiable, Equatable {
|
|
||||||
case text(id: String, senderDisplayName: String, text: String, originServerTs: Date, shouldShowSenderDetails: Bool)
|
|
||||||
case sectionTitle(id: String, text: String)
|
|
||||||
|
|
||||||
var id: String {
|
|
||||||
switch self {
|
|
||||||
case .text(let id, _, _, _, _):
|
|
||||||
return id
|
|
||||||
case .sectionTitle(let id, _):
|
|
||||||
return id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension RoomTimelineItem: View {
|
|
||||||
var body: some View {
|
|
||||||
switch self {
|
|
||||||
case .text(let id, let senderDisplayName, let text, let originServerTs, let shouldShowSenderDetails):
|
|
||||||
VStack(alignment: .leading) {
|
|
||||||
if shouldShowSenderDetails {
|
|
||||||
HStack {
|
|
||||||
Text(senderDisplayName)
|
|
||||||
.font(.footnote)
|
|
||||||
.bold()
|
|
||||||
Spacer()
|
|
||||||
Text(dateFormatter.string(from: originServerTs))
|
|
||||||
.font(.footnote)
|
|
||||||
}
|
|
||||||
Divider()
|
|
||||||
Spacer()
|
|
||||||
}
|
|
||||||
Text(text)
|
|
||||||
}
|
|
||||||
.listRowSeparator(.hidden)
|
|
||||||
.id(id)
|
|
||||||
case .sectionTitle(let id, let text):
|
|
||||||
LabelledDivider(label: text)
|
|
||||||
.id(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LabelledDivider: View {
|
|
||||||
|
|
||||||
let label: String
|
|
||||||
let color: Color
|
|
||||||
|
|
||||||
init(label: String, color: Color = .gray) {
|
|
||||||
self.label = label
|
|
||||||
self.color = color
|
|
||||||
}
|
|
||||||
|
|
||||||
var body: some View {
|
|
||||||
HStack {
|
|
||||||
line
|
|
||||||
Text(label)
|
|
||||||
.foregroundColor(color)
|
|
||||||
.fixedSize()
|
|
||||||
line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var line: some View {
|
|
||||||
VStack { Divider().background(color) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
//
|
||||||
|
// TextRoomTimelineItem.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 04.03.2022.
|
||||||
|
// Copyright © 2022 Element. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
enum RoomTimelineViewProvider: Identifiable, Equatable {
|
||||||
|
case text(TextRoomTimelineItem)
|
||||||
|
case separator(SeparatorRoomTimelineItem)
|
||||||
|
case image(ImageRoomTimelineItem)
|
||||||
|
|
||||||
|
var id: String {
|
||||||
|
switch self {
|
||||||
|
case .text(let item):
|
||||||
|
return item.id
|
||||||
|
case .separator(let item):
|
||||||
|
return item.id
|
||||||
|
case .image(let item):
|
||||||
|
return item.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RoomTimelineViewProvider: View {
|
||||||
|
@ViewBuilder var body: some View {
|
||||||
|
switch self {
|
||||||
|
case .text(let item):
|
||||||
|
TextRoomTimelineView(timelineItem: item)
|
||||||
|
case .separator(let item):
|
||||||
|
SeparatorRoomTimelineView(timelineItem: item)
|
||||||
|
case .image(let item):
|
||||||
|
ImageRoomTimelineView(timelineItem: item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// SectionSeparatorTimelineItem.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 11/03/2022.
|
||||||
|
// Copyright © 2022 Element. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct SeparatorRoomTimelineItem: Identifiable, Equatable {
|
||||||
|
let id: String
|
||||||
|
let text: String
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// SeparatorRoomTimelineView.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 11/03/2022.
|
||||||
|
// Copyright © 2022 Element. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SeparatorRoomTimelineView: View {
|
||||||
|
let timelineItem: SeparatorRoomTimelineItem
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
LabelledDivider(label: timelineItem.text)
|
||||||
|
.id(timelineItem.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct LabelledDivider: View {
|
||||||
|
let label: String
|
||||||
|
let color: Color
|
||||||
|
|
||||||
|
init(label: String, color: Color = .gray) {
|
||||||
|
self.label = label
|
||||||
|
self.color = color
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack {
|
||||||
|
line
|
||||||
|
Text(label)
|
||||||
|
.foregroundColor(color)
|
||||||
|
.fixedSize()
|
||||||
|
line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var line: some View {
|
||||||
|
VStack { Divider().background(color) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// TextRoomTimelineItem.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 11/03/2022.
|
||||||
|
// Copyright © 2022 Element. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct TextRoomTimelineItem: Identifiable, Equatable {
|
||||||
|
let id: String
|
||||||
|
let senderDisplayName: String
|
||||||
|
let text: String
|
||||||
|
let timestamp: String
|
||||||
|
let shouldShowSenderDetails: Bool
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// TextRoomTimelineView.swift
|
||||||
|
// ElementX
|
||||||
|
//
|
||||||
|
// Created by Stefan Ceriu on 11/03/2022.
|
||||||
|
// Copyright © 2022 Element. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct TextRoomTimelineView: View {
|
||||||
|
let timelineItem: TextRoomTimelineItem
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
if timelineItem.shouldShowSenderDetails {
|
||||||
|
HStack {
|
||||||
|
Text(timelineItem.senderDisplayName)
|
||||||
|
.font(.footnote)
|
||||||
|
.bold()
|
||||||
|
Spacer()
|
||||||
|
Text(timelineItem.timestamp)
|
||||||
|
.font(.footnote)
|
||||||
|
}
|
||||||
|
Divider()
|
||||||
|
Spacer()
|
||||||
|
}
|
||||||
|
Text(timelineItem.text)
|
||||||
|
}
|
||||||
|
.id(timelineItem.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user