Added room timeline screen with live events listening and back pagination. Rearranged project files.

This commit is contained in:
Stefan Ceriu
2022-03-08 14:24:33 +02:00
parent f1e367f382
commit 9eec77e228
77 changed files with 1269 additions and 491 deletions

View File

@@ -2,6 +2,7 @@
disabled_rules:
- trailing_whitespace
- unused_setter_value
- redundant_discardable_let
# some rules are only opt-in
opt_in_rules:

View File

@@ -7,18 +7,6 @@
objects = {
/* Begin PBXBuildFile section */
182BC42027BE667200A30C33 /* RoomModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC41F27BE667200A30C33 /* RoomModelProtocol.swift */; };
182BC42227BE6C6900A30C33 /* MockRoomModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC42127BE6C6900A30C33 /* MockRoomModel.swift */; };
182BC46F27C4CD6D00A30C33 /* RoundedToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC46527C4CD6D00A30C33 /* RoundedToastView.swift */; };
182BC47027C4CD6D00A30C33 /* RectangleToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC46627C4CD6D00A30C33 /* RectangleToastView.swift */; };
182BC47127C4CD6D00A30C33 /* ActivityPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC46727C4CD6D00A30C33 /* ActivityPresentable.swift */; };
182BC47227C4CD6D00A30C33 /* ActivityCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC46827C4CD6D00A30C33 /* ActivityCenter.swift */; };
182BC47327C4CD6D00A30C33 /* ToastActivityPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC46A27C4CD6D00A30C33 /* ToastActivityPresenter.swift */; };
182BC47527C4CD6D00A30C33 /* Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC46C27C4CD6D00A30C33 /* Activity.swift */; };
182BC47627C4CD6D00A30C33 /* ActivityRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC46D27C4CD6D00A30C33 /* ActivityRequest.swift */; };
182BC47727C4CD6D00A30C33 /* ActivityDismissal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC46E27C4CD6D00A30C33 /* ActivityDismissal.swift */; };
182BC47927C4CE2200A30C33 /* LabelledActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC47827C4CE2200A30C33 /* LabelledActivityIndicatorView.swift */; };
182BC47B27C4D05200A30C33 /* FullscreenLoadingActivityPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 182BC47A27C4D05200A30C33 /* FullscreenLoadingActivityPresenter.swift */; };
182BC48127C4EBBB00A30C33 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 182BC48027C4EBBB00A30C33 /* Kingfisher */; };
1850253F27B6918D002E6B18 /* ElementXTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1850253E27B6918D002E6B18 /* ElementXTests.swift */; };
1850254927B6918D002E6B18 /* ElementXUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1850254827B6918D002E6B18 /* ElementXUITests.swift */; };
@@ -30,43 +18,68 @@
1850257127B6A135002E6B18 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 1850256927B6A135002E6B18 /* LaunchScreen.storyboard */; };
1863A3FC27BA5A9100B52E4D /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A3FB27BA5A9100B52E4D /* KeychainAccess */; };
1863A40627BA6DFC00B52E4D /* SwiftyBeaver in Frameworks */ = {isa = PBXBuildFile; productRef = 1863A40527BA6DFC00B52E4D /* SwiftyBeaver */; };
1863A41427BA716A00B52E4D /* AuthenticationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A40E27BA716A00B52E4D /* AuthenticationCoordinator.swift */; };
1863A41527BA716A00B52E4D /* UserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A40F27BA716A00B52E4D /* UserSession.swift */; };
1863A41627BA716A00B52E4D /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A41027BA716A00B52E4D /* KeychainController.swift */; };
1863A41727BA716A00B52E4D /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A41127BA716A00B52E4D /* KeychainControllerProtocol.swift */; };
1863A41C27BA76B900B52E4D /* MXLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A41B27BA76B900B52E4D /* MXLog.swift */; };
1863A42C27BA784300B52E4D /* LoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A41F27BA784300B52E4D /* LoginScreenViewModel.swift */; };
1863A43027BA784300B52E4D /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A42727BA784300B52E4D /* LoginScreenViewModelProtocol.swift */; };
1863A43127BA784300B52E4D /* LoginScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A42827BA784300B52E4D /* LoginScreenModels.swift */; };
1863A43227BA784300B52E4D /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A42A27BA784300B52E4D /* LoginScreen.swift */; };
1863A43427BA786400B52E4D /* LoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A42627BA784300B52E4D /* LoginScreenViewModelTests.swift */; };
1863A43527BA788500B52E4D /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A42427BA784300B52E4D /* LoginScreenUITests.swift */; };
1863A43A27BA789800B52E4D /* StateStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A43827BA789800B52E4D /* StateStoreViewModel.swift */; };
1863A43B27BA789800B52E4D /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A43927BA789800B52E4D /* BindableState.swift */; };
1863A43F27BA790000B52E4D /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A43D27BA790000B52E4D /* Coordinator.swift */; };
1863A44927BA79FF00B52E4D /* NavigationRouterStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A44127BA79FF00B52E4D /* NavigationRouterStore.swift */; };
1863A44A27BA79FF00B52E4D /* NavigationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A44227BA79FF00B52E4D /* NavigationRouter.swift */; };
1863A44B27BA79FF00B52E4D /* RootRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A44327BA79FF00B52E4D /* RootRouterType.swift */; };
1863A44C27BA79FF00B52E4D /* NavigationRouterStoreProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A44427BA79FF00B52E4D /* NavigationRouterStoreProtocol.swift */; };
1863A44D27BA79FF00B52E4D /* RootRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A44527BA79FF00B52E4D /* RootRouter.swift */; };
1863A44E27BA79FF00B52E4D /* Presentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A44627BA79FF00B52E4D /* Presentable.swift */; };
1863A44F27BA79FF00B52E4D /* NavigationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A44727BA79FF00B52E4D /* NavigationModule.swift */; };
1863A45027BA79FF00B52E4D /* NavigationRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A44827BA79FF00B52E4D /* NavigationRouterType.swift */; };
1863A45627BA7A7800B52E4D /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A45227BA7A7800B52E4D /* WeakDictionaryKeyReference.swift */; };
1863A45727BA7A7800B52E4D /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A45327BA7A7800B52E4D /* WeakDictionaryReference.swift */; };
1863A45827BA7A7800B52E4D /* WeakDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A45427BA7A7800B52E4D /* WeakDictionary.swift */; };
1863A45927BA7A7800B52E4D /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A45527BA7A7800B52E4D /* WeakKeyDictionary.swift */; };
1863A45B27BA7B4700B52E4D /* LoginScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A45A27BA7B4700B52E4D /* LoginScreenCoordinator.swift */; };
1863A45F27BAA60300B52E4D /* SplashViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A45D27BAA60300B52E4D /* SplashViewController.swift */; };
1863A46027BAA60300B52E4D /* SplashViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1863A45E27BAA60300B52E4D /* SplashViewController.xib */; };
1863A48527BAA8A900B52E4D /* HomeScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A47927BAA8A900B52E4D /* HomeScreenCoordinator.swift */; };
1863A48827BAA8A900B52E4D /* HomeScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A47F27BAA8A900B52E4D /* HomeScreenViewModelProtocol.swift */; };
1863A48927BAA8A900B52E4D /* HomeScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A48027BAA8A900B52E4D /* HomeScreenModels.swift */; };
1863A48A27BAA8A900B52E4D /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A48227BAA8A900B52E4D /* HomeScreen.swift */; };
1863A48C27BAA8A900B52E4D /* HomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A48427BAA8A900B52E4D /* HomeScreenViewModel.swift */; };
1863A48E27BAA8C800B52E4D /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A47C27BAA8A900B52E4D /* HomeScreenUITests.swift */; };
1863A48F27BAA8CC00B52E4D /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A47E27BAA8A900B52E4D /* HomeScreenViewModelTests.swift */; };
1863A49427BAAA6700B52E4D /* RoomModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1863A49327BAAA6700B52E4D /* RoomModel.swift */; };
18F2BAD727D25B4000DD1988 /* RoomProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7327D25B4000DD1988 /* RoomProxyProtocol.swift */; };
18F2BAD827D25B4000DD1988 /* RoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7427D25B4000DD1988 /* RoomProxy.swift */; };
18F2BAD927D25B4000DD1988 /* MockRoomProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7527D25B4000DD1988 /* MockRoomProxy.swift */; };
18F2BADA27D25B4000DD1988 /* RoomTimelineProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7727D25B4000DD1988 /* RoomTimelineProvider.swift */; };
18F2BADB27D25B4000DD1988 /* AuthenticationCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7927D25B4000DD1988 /* AuthenticationCoordinator.swift */; };
18F2BADC27D25B4000DD1988 /* UserSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7A27D25B4000DD1988 /* UserSession.swift */; };
18F2BADD27D25B4000DD1988 /* KeychainController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7B27D25B4000DD1988 /* KeychainController.swift */; };
18F2BADE27D25B4000DD1988 /* KeychainControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7C27D25B4000DD1988 /* KeychainControllerProtocol.swift */; };
18F2BADF27D25B4000DD1988 /* NavigationRouterStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA7F27D25B4000DD1988 /* NavigationRouterStore.swift */; };
18F2BAE027D25B4000DD1988 /* NavigationRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8027D25B4000DD1988 /* NavigationRouter.swift */; };
18F2BAE127D25B4000DD1988 /* RootRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8127D25B4000DD1988 /* RootRouterType.swift */; };
18F2BAE227D25B4000DD1988 /* NavigationRouterStoreProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8227D25B4000DD1988 /* NavigationRouterStoreProtocol.swift */; };
18F2BAE327D25B4000DD1988 /* RootRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8327D25B4000DD1988 /* RootRouter.swift */; };
18F2BAE427D25B4000DD1988 /* Presentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8427D25B4000DD1988 /* Presentable.swift */; };
18F2BAE527D25B4000DD1988 /* NavigationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8527D25B4000DD1988 /* NavigationModule.swift */; };
18F2BAE627D25B4000DD1988 /* NavigationRouterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8627D25B4000DD1988 /* NavigationRouterType.swift */; };
18F2BAE727D25B4000DD1988 /* RoundedToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8927D25B4000DD1988 /* RoundedToastView.swift */; };
18F2BAE827D25B4000DD1988 /* RectangleToastView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8A27D25B4000DD1988 /* RectangleToastView.swift */; };
18F2BAE927D25B4000DD1988 /* ActivityPresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8B27D25B4000DD1988 /* ActivityPresentable.swift */; };
18F2BAEA27D25B4000DD1988 /* ActivityCenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8C27D25B4000DD1988 /* ActivityCenter.swift */; };
18F2BAEB27D25B4000DD1988 /* LabelledActivityIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8D27D25B4000DD1988 /* LabelledActivityIndicatorView.swift */; };
18F2BAEC27D25B4000DD1988 /* ToastActivityPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA8F27D25B4000DD1988 /* ToastActivityPresenter.swift */; };
18F2BAED27D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9027D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift */; };
18F2BAEE27D25B4000DD1988 /* Activity.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9127D25B4000DD1988 /* Activity.swift */; };
18F2BAEF27D25B4000DD1988 /* ActivityRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9227D25B4000DD1988 /* ActivityRequest.swift */; };
18F2BAF027D25B4000DD1988 /* ActivityDismissal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9327D25B4000DD1988 /* ActivityDismissal.swift */; };
18F2BAF127D25B4000DD1988 /* MXLog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9427D25B4000DD1988 /* MXLog.swift */; };
18F2BAF227D25B4000DD1988 /* WeakDictionaryKeyReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9627D25B4000DD1988 /* WeakDictionaryKeyReference.swift */; };
18F2BAF327D25B4000DD1988 /* WeakDictionaryReference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9727D25B4000DD1988 /* WeakDictionaryReference.swift */; };
18F2BAF427D25B4000DD1988 /* WeakDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9827D25B4000DD1988 /* WeakDictionary.swift */; };
18F2BAF527D25B4000DD1988 /* WeakKeyDictionary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9927D25B4000DD1988 /* WeakKeyDictionary.swift */; };
18F2BAF627D25B4000DD1988 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9A27D25B4000DD1988 /* Coordinator.swift */; };
18F2BAF727D25B4000DD1988 /* StateStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9D27D25B4000DD1988 /* StateStoreViewModel.swift */; };
18F2BAF827D25B4000DD1988 /* BindableState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BA9E27D25B4000DD1988 /* BindableState.swift */; };
18F2BAF927D25B4000DD1988 /* SplashViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAA127D25B4000DD1988 /* SplashViewController.swift */; };
18F2BAFA27D25B4000DD1988 /* SplashViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 18F2BAA227D25B4000DD1988 /* SplashViewController.xib */; };
18F2BAFB27D25B4000DD1988 /* HomeScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAA427D25B4000DD1988 /* HomeScreenCoordinator.swift */; };
18F2BAFE27D25B4000DD1988 /* HomeScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAAA27D25B4000DD1988 /* HomeScreenViewModelProtocol.swift */; };
18F2BAFF27D25B4000DD1988 /* HomeScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAAB27D25B4000DD1988 /* HomeScreenModels.swift */; };
18F2BB0027D25B4000DD1988 /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAAD27D25B4000DD1988 /* HomeScreen.swift */; };
18F2BB0127D25B4000DD1988 /* HomeScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAAE27D25B4000DD1988 /* HomeScreenViewModel.swift */; };
18F2BB0C27D25B4000DD1988 /* RoomScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC427D25B4000DD1988 /* RoomScreenCoordinator.swift */; };
18F2BB0D27D25B4000DD1988 /* RoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC527D25B4000DD1988 /* RoomScreenViewModel.swift */; };
18F2BB0E27D25B4000DD1988 /* RoomScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC627D25B4000DD1988 /* RoomScreenViewModelProtocol.swift */; };
18F2BB0F27D25B4000DD1988 /* RoomScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC827D25B4000DD1988 /* RoomScreen.swift */; };
18F2BB1127D25B4000DD1988 /* RoomScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BACA27D25B4000DD1988 /* RoomScreenModels.swift */; };
18F2BB1227D25B4000DD1988 /* LoginScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BACC27D25B4000DD1988 /* LoginScreenViewModel.swift */; };
18F2BB1527D25B4000DD1988 /* LoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAD227D25B4000DD1988 /* LoginScreenViewModelProtocol.swift */; };
18F2BB1627D25B4000DD1988 /* LoginScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAD327D25B4000DD1988 /* LoginScreenModels.swift */; };
18F2BB1727D25B4000DD1988 /* LoginScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAD427D25B4000DD1988 /* LoginScreenCoordinator.swift */; };
18F2BB1827D25B4000DD1988 /* LoginScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAD627D25B4000DD1988 /* LoginScreen.swift */; };
18F2BB1A27D25BE800DD1988 /* RoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB1927D25BE800DD1988 /* RoomTimelineController.swift */; };
18F2BB1D27D25D1C00DD1988 /* LoginScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAD127D25B4000DD1988 /* LoginScreenViewModelTests.swift */; };
18F2BB1E27D25D2200DD1988 /* HomeScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAA927D25B4000DD1988 /* HomeScreenViewModelTests.swift */; };
18F2BB1F27D25D3100DD1988 /* RoomScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC327D25B4000DD1988 /* RoomScreenViewModelTests.swift */; };
18F2BB2027D25D3400DD1988 /* RoomScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAC127D25B4000DD1988 /* RoomScreenUITests.swift */; };
18F2BB2127D25D3C00DD1988 /* HomeScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BAA727D25B4000DD1988 /* HomeScreenUITests.swift */; };
18F2BB2227D25D4600DD1988 /* LoginScreenUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BACF27D25B4000DD1988 /* LoginScreenUITests.swift */; };
18F2BB2427D262A900DD1988 /* RoomTimelineItemProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2327D262A900DD1988 /* RoomTimelineItemProtocol.swift */; };
18F2BB2627D262FA00DD1988 /* TextRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2527D262FA00DD1988 /* TextRoomTimelineItem.swift */; };
18F2BB2827D2647A00DD1988 /* MockRoomTimelineController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2727D2647A00DD1988 /* MockRoomTimelineController.swift */; };
18F2BB2A27D2648900DD1988 /* RoomTimelineControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18F2BB2927D2648900DD1988 /* RoomTimelineControllerProtocol.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -87,18 +100,6 @@
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
182BC41F27BE667200A30C33 /* RoomModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomModelProtocol.swift; sourceTree = "<group>"; };
182BC42127BE6C6900A30C33 /* MockRoomModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRoomModel.swift; sourceTree = "<group>"; };
182BC46527C4CD6D00A30C33 /* RoundedToastView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedToastView.swift; sourceTree = "<group>"; };
182BC46627C4CD6D00A30C33 /* RectangleToastView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RectangleToastView.swift; sourceTree = "<group>"; };
182BC46727C4CD6D00A30C33 /* ActivityPresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityPresentable.swift; sourceTree = "<group>"; };
182BC46827C4CD6D00A30C33 /* ActivityCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityCenter.swift; sourceTree = "<group>"; };
182BC46A27C4CD6D00A30C33 /* ToastActivityPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToastActivityPresenter.swift; sourceTree = "<group>"; };
182BC46C27C4CD6D00A30C33 /* Activity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Activity.swift; sourceTree = "<group>"; };
182BC46D27C4CD6D00A30C33 /* ActivityRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityRequest.swift; sourceTree = "<group>"; };
182BC46E27C4CD6D00A30C33 /* ActivityDismissal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityDismissal.swift; sourceTree = "<group>"; };
182BC47827C4CE2200A30C33 /* LabelledActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelledActivityIndicatorView.swift; sourceTree = "<group>"; };
182BC47A27C4D05200A30C33 /* FullscreenLoadingActivityPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullscreenLoadingActivityPresenter.swift; sourceTree = "<group>"; };
1850252427B6918C002E6B18 /* ElementX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ElementX.app; sourceTree = BUILT_PRODUCTS_DIR; };
1850253A27B6918D002E6B18 /* ElementXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ElementXTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
1850253E27B6918D002E6B18 /* ElementXTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ElementXTests.swift; sourceTree = "<group>"; };
@@ -110,43 +111,68 @@
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>"; };
1850256A27B6A135002E6B18 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
1863A40E27BA716A00B52E4D /* AuthenticationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinator.swift; sourceTree = "<group>"; };
1863A40F27BA716A00B52E4D /* UserSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserSession.swift; sourceTree = "<group>"; };
1863A41027BA716A00B52E4D /* KeychainController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = "<group>"; };
1863A41127BA716A00B52E4D /* KeychainControllerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainControllerProtocol.swift; sourceTree = "<group>"; };
1863A41B27BA76B900B52E4D /* MXLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXLog.swift; sourceTree = "<group>"; };
1863A41F27BA784300B52E4D /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = "<group>"; };
1863A42427BA784300B52E4D /* LoginScreenUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenUITests.swift; sourceTree = "<group>"; };
1863A42627BA784300B52E4D /* LoginScreenViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModelTests.swift; sourceTree = "<group>"; };
1863A42727BA784300B52E4D /* LoginScreenViewModelProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModelProtocol.swift; sourceTree = "<group>"; };
1863A42827BA784300B52E4D /* LoginScreenModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenModels.swift; sourceTree = "<group>"; };
1863A42A27BA784300B52E4D /* LoginScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = "<group>"; };
1863A43827BA789800B52E4D /* StateStoreViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateStoreViewModel.swift; sourceTree = "<group>"; };
1863A43927BA789800B52E4D /* BindableState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindableState.swift; sourceTree = "<group>"; };
1863A43D27BA790000B52E4D /* Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = "<group>"; };
1863A44127BA79FF00B52E4D /* NavigationRouterStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouterStore.swift; sourceTree = "<group>"; };
1863A44227BA79FF00B52E4D /* NavigationRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = "<group>"; };
1863A44327BA79FF00B52E4D /* RootRouterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootRouterType.swift; sourceTree = "<group>"; };
1863A44427BA79FF00B52E4D /* NavigationRouterStoreProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouterStoreProtocol.swift; sourceTree = "<group>"; };
1863A44527BA79FF00B52E4D /* RootRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootRouter.swift; sourceTree = "<group>"; };
1863A44627BA79FF00B52E4D /* Presentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presentable.swift; sourceTree = "<group>"; };
1863A44727BA79FF00B52E4D /* NavigationModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
1863A44827BA79FF00B52E4D /* NavigationRouterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouterType.swift; sourceTree = "<group>"; };
1863A45227BA7A7800B52E4D /* WeakDictionaryKeyReference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakDictionaryKeyReference.swift; sourceTree = "<group>"; };
1863A45327BA7A7800B52E4D /* WeakDictionaryReference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakDictionaryReference.swift; sourceTree = "<group>"; };
1863A45427BA7A7800B52E4D /* WeakDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakDictionary.swift; sourceTree = "<group>"; };
1863A45527BA7A7800B52E4D /* WeakKeyDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakKeyDictionary.swift; sourceTree = "<group>"; };
1863A45A27BA7B4700B52E4D /* LoginScreenCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenCoordinator.swift; sourceTree = "<group>"; };
1863A45D27BAA60300B52E4D /* SplashViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashViewController.swift; sourceTree = "<group>"; };
1863A45E27BAA60300B52E4D /* SplashViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SplashViewController.xib; sourceTree = "<group>"; };
1863A47927BAA8A900B52E4D /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = "<group>"; };
1863A47C27BAA8A900B52E4D /* HomeScreenUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenUITests.swift; sourceTree = "<group>"; };
1863A47E27BAA8A900B52E4D /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; };
1863A47F27BAA8A900B52E4D /* HomeScreenViewModelProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelProtocol.swift; sourceTree = "<group>"; };
1863A48027BAA8A900B52E4D /* HomeScreenModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenModels.swift; sourceTree = "<group>"; };
1863A48227BAA8A900B52E4D /* HomeScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; };
1863A48427BAA8A900B52E4D /* HomeScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModel.swift; sourceTree = "<group>"; };
1863A49327BAAA6700B52E4D /* RoomModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomModel.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>"; };
18F2BA7527D25B4000DD1988 /* MockRoomProxy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRoomProxy.swift; sourceTree = "<group>"; };
18F2BA7727D25B4000DD1988 /* RoomTimelineProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomTimelineProvider.swift; sourceTree = "<group>"; };
18F2BA7927D25B4000DD1988 /* AuthenticationCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AuthenticationCoordinator.swift; sourceTree = "<group>"; };
18F2BA7A27D25B4000DD1988 /* UserSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserSession.swift; sourceTree = "<group>"; };
18F2BA7B27D25B4000DD1988 /* KeychainController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainController.swift; sourceTree = "<group>"; };
18F2BA7C27D25B4000DD1988 /* KeychainControllerProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = KeychainControllerProtocol.swift; sourceTree = "<group>"; };
18F2BA7F27D25B4000DD1988 /* NavigationRouterStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouterStore.swift; sourceTree = "<group>"; };
18F2BA8027D25B4000DD1988 /* NavigationRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouter.swift; sourceTree = "<group>"; };
18F2BA8127D25B4000DD1988 /* RootRouterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootRouterType.swift; sourceTree = "<group>"; };
18F2BA8227D25B4000DD1988 /* NavigationRouterStoreProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouterStoreProtocol.swift; sourceTree = "<group>"; };
18F2BA8327D25B4000DD1988 /* RootRouter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RootRouter.swift; sourceTree = "<group>"; };
18F2BA8427D25B4000DD1988 /* Presentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Presentable.swift; sourceTree = "<group>"; };
18F2BA8527D25B4000DD1988 /* NavigationModule.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationModule.swift; sourceTree = "<group>"; };
18F2BA8627D25B4000DD1988 /* NavigationRouterType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationRouterType.swift; sourceTree = "<group>"; };
18F2BA8927D25B4000DD1988 /* RoundedToastView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoundedToastView.swift; sourceTree = "<group>"; };
18F2BA8A27D25B4000DD1988 /* RectangleToastView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RectangleToastView.swift; sourceTree = "<group>"; };
18F2BA8B27D25B4000DD1988 /* ActivityPresentable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityPresentable.swift; sourceTree = "<group>"; };
18F2BA8C27D25B4000DD1988 /* ActivityCenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityCenter.swift; sourceTree = "<group>"; };
18F2BA8D27D25B4000DD1988 /* LabelledActivityIndicatorView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LabelledActivityIndicatorView.swift; sourceTree = "<group>"; };
18F2BA8F27D25B4000DD1988 /* ToastActivityPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ToastActivityPresenter.swift; sourceTree = "<group>"; };
18F2BA9027D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FullscreenLoadingActivityPresenter.swift; sourceTree = "<group>"; };
18F2BA9127D25B4000DD1988 /* Activity.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Activity.swift; sourceTree = "<group>"; };
18F2BA9227D25B4000DD1988 /* ActivityRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityRequest.swift; sourceTree = "<group>"; };
18F2BA9327D25B4000DD1988 /* ActivityDismissal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ActivityDismissal.swift; sourceTree = "<group>"; };
18F2BA9427D25B4000DD1988 /* MXLog.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MXLog.swift; sourceTree = "<group>"; };
18F2BA9627D25B4000DD1988 /* WeakDictionaryKeyReference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakDictionaryKeyReference.swift; sourceTree = "<group>"; };
18F2BA9727D25B4000DD1988 /* WeakDictionaryReference.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakDictionaryReference.swift; sourceTree = "<group>"; };
18F2BA9827D25B4000DD1988 /* WeakDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakDictionary.swift; sourceTree = "<group>"; };
18F2BA9927D25B4000DD1988 /* WeakKeyDictionary.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WeakKeyDictionary.swift; sourceTree = "<group>"; };
18F2BA9A27D25B4000DD1988 /* Coordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = "<group>"; };
18F2BA9D27D25B4000DD1988 /* StateStoreViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StateStoreViewModel.swift; sourceTree = "<group>"; };
18F2BA9E27D25B4000DD1988 /* BindableState.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BindableState.swift; sourceTree = "<group>"; };
18F2BAA127D25B4000DD1988 /* SplashViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SplashViewController.swift; sourceTree = "<group>"; };
18F2BAA227D25B4000DD1988 /* SplashViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = SplashViewController.xib; sourceTree = "<group>"; };
18F2BAA427D25B4000DD1988 /* HomeScreenCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenCoordinator.swift; sourceTree = "<group>"; };
18F2BAA727D25B4000DD1988 /* HomeScreenUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenUITests.swift; sourceTree = "<group>"; };
18F2BAA927D25B4000DD1988 /* HomeScreenViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelTests.swift; sourceTree = "<group>"; };
18F2BAAA27D25B4000DD1988 /* HomeScreenViewModelProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModelProtocol.swift; sourceTree = "<group>"; };
18F2BAAB27D25B4000DD1988 /* HomeScreenModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenModels.swift; sourceTree = "<group>"; };
18F2BAAD27D25B4000DD1988 /* HomeScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = "<group>"; };
18F2BAAE27D25B4000DD1988 /* HomeScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeScreenViewModel.swift; sourceTree = "<group>"; };
18F2BAC127D25B4000DD1988 /* RoomScreenUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomScreenUITests.swift; sourceTree = "<group>"; };
18F2BAC327D25B4000DD1988 /* RoomScreenViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModelTests.swift; sourceTree = "<group>"; };
18F2BAC427D25B4000DD1988 /* RoomScreenCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomScreenCoordinator.swift; sourceTree = "<group>"; };
18F2BAC527D25B4000DD1988 /* RoomScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModel.swift; sourceTree = "<group>"; };
18F2BAC627D25B4000DD1988 /* RoomScreenViewModelProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomScreenViewModelProtocol.swift; sourceTree = "<group>"; };
18F2BAC827D25B4000DD1988 /* RoomScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomScreen.swift; sourceTree = "<group>"; };
18F2BACA27D25B4000DD1988 /* RoomScreenModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RoomScreenModels.swift; sourceTree = "<group>"; };
18F2BACC27D25B4000DD1988 /* LoginScreenViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModel.swift; sourceTree = "<group>"; };
18F2BACF27D25B4000DD1988 /* LoginScreenUITests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenUITests.swift; sourceTree = "<group>"; };
18F2BAD127D25B4000DD1988 /* LoginScreenViewModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModelTests.swift; sourceTree = "<group>"; };
18F2BAD227D25B4000DD1988 /* LoginScreenViewModelProtocol.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenViewModelProtocol.swift; sourceTree = "<group>"; };
18F2BAD327D25B4000DD1988 /* LoginScreenModels.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenModels.swift; sourceTree = "<group>"; };
18F2BAD427D25B4000DD1988 /* LoginScreenCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreenCoordinator.swift; sourceTree = "<group>"; };
18F2BAD627D25B4000DD1988 /* LoginScreen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoginScreen.swift; sourceTree = "<group>"; };
18F2BB1927D25BE800DD1988 /* RoomTimelineController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineController.swift; sourceTree = "<group>"; };
18F2BB2327D262A900DD1988 /* RoomTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomTimelineItemProtocol.swift; sourceTree = "<group>"; };
18F2BB2527D262FA00DD1988 /* TextRoomTimelineItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextRoomTimelineItem.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>"; };
18FE279627C7B85300016375 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -179,39 +205,6 @@
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
182BC46327C4CD6D00A30C33 /* Activity */ = {
isa = PBXGroup;
children = (
182BC47827C4CE2200A30C33 /* LabelledActivityIndicatorView.swift */,
182BC46427C4CD6D00A30C33 /* Toasts */,
182BC46727C4CD6D00A30C33 /* ActivityPresentable.swift */,
182BC46827C4CD6D00A30C33 /* ActivityCenter.swift */,
182BC46927C4CD6D00A30C33 /* ActivityPresenters */,
182BC46C27C4CD6D00A30C33 /* Activity.swift */,
182BC46D27C4CD6D00A30C33 /* ActivityRequest.swift */,
182BC46E27C4CD6D00A30C33 /* ActivityDismissal.swift */,
);
path = Activity;
sourceTree = "<group>";
};
182BC46427C4CD6D00A30C33 /* Toasts */ = {
isa = PBXGroup;
children = (
182BC46527C4CD6D00A30C33 /* RoundedToastView.swift */,
182BC46627C4CD6D00A30C33 /* RectangleToastView.swift */,
);
path = Toasts;
sourceTree = "<group>";
};
182BC46927C4CD6D00A30C33 /* ActivityPresenters */ = {
isa = PBXGroup;
children = (
182BC47A27C4D05200A30C33 /* FullscreenLoadingActivityPresenter.swift */,
182BC46A27C4CD6D00A30C33 /* ToastActivityPresenter.swift */,
);
path = ActivityPresenters;
sourceTree = "<group>";
};
1850251B27B6918C002E6B18 = {
isa = PBXGroup;
children = (
@@ -263,7 +256,9 @@
children = (
1850256527B6A135002E6B18 /* AppDelegate.swift */,
1850256227B6A135002E6B18 /* AppCoordinator.swift */,
1863A40927BA716A00B52E4D /* Modules */,
18F2BA7227D25B4000DD1988 /* Services */,
18F2BA9F27D25B4000DD1988 /* Screens */,
18F2BA7D27D25B4000DD1988 /* Other */,
);
path = Sources;
sourceTree = "<group>";
@@ -279,195 +274,287 @@
path = "Supporting Files";
sourceTree = "<group>";
};
1863A40927BA716A00B52E4D /* Modules */ = {
18F2BA7227D25B4000DD1988 /* Services */ = {
isa = PBXGroup;
children = (
1863A45C27BAA5F100B52E4D /* Splash */,
1863A40D27BA716A00B52E4D /* Authentication */,
1863A47827BAA8A900B52E4D /* HomeScreen */,
1863A49227BAAA6700B52E4D /* Models */,
1863A44027BA79FF00B52E4D /* Routers */,
1863A41A27BA76B900B52E4D /* Other */,
18F2BA7827D25B4000DD1988 /* Authentication */,
18F2BA7627D25B4000DD1988 /* Timeline */,
18F2BA7327D25B4000DD1988 /* RoomProxyProtocol.swift */,
18F2BA7427D25B4000DD1988 /* RoomProxy.swift */,
18F2BA7527D25B4000DD1988 /* MockRoomProxy.swift */,
);
path = Modules;
path = Services;
sourceTree = "<group>";
};
1863A40D27BA716A00B52E4D /* Authentication */ = {
18F2BA7627D25B4000DD1988 /* Timeline */ = {
isa = PBXGroup;
children = (
1863A40E27BA716A00B52E4D /* AuthenticationCoordinator.swift */,
1863A40F27BA716A00B52E4D /* UserSession.swift */,
1863A41027BA716A00B52E4D /* KeychainController.swift */,
1863A41127BA716A00B52E4D /* KeychainControllerProtocol.swift */,
1863A41D27BA784300B52E4D /* LoginScreen */,
18F2BB2927D2648900DD1988 /* RoomTimelineControllerProtocol.swift */,
18F2BB1927D25BE800DD1988 /* RoomTimelineController.swift */,
18F2BB2727D2647A00DD1988 /* MockRoomTimelineController.swift */,
18F2BA7727D25B4000DD1988 /* RoomTimelineProvider.swift */,
18F2BB2327D262A900DD1988 /* RoomTimelineItemProtocol.swift */,
18F2BB2527D262FA00DD1988 /* TextRoomTimelineItem.swift */,
);
path = Timeline;
sourceTree = "<group>";
};
18F2BA7827D25B4000DD1988 /* Authentication */ = {
isa = PBXGroup;
children = (
18F2BA7927D25B4000DD1988 /* AuthenticationCoordinator.swift */,
18F2BA7A27D25B4000DD1988 /* UserSession.swift */,
18F2BA7B27D25B4000DD1988 /* KeychainController.swift */,
18F2BA7C27D25B4000DD1988 /* KeychainControllerProtocol.swift */,
);
path = Authentication;
sourceTree = "<group>";
};
1863A41A27BA76B900B52E4D /* Other */ = {
18F2BA7D27D25B4000DD1988 /* Other */ = {
isa = PBXGroup;
children = (
182BC46327C4CD6D00A30C33 /* Activity */,
1863A43D27BA790000B52E4D /* Coordinator.swift */,
1863A41B27BA76B900B52E4D /* MXLog.swift */,
1863A43627BA789800B52E4D /* SwiftUI */,
1863A45127BA7A7800B52E4D /* WeakDictionary */,
18F2BA7E27D25B4000DD1988 /* Routers */,
18F2BA8727D25B4000DD1988 /* Activity */,
18F2BA9427D25B4000DD1988 /* MXLog.swift */,
18F2BA9527D25B4000DD1988 /* WeakDictionary */,
18F2BA9A27D25B4000DD1988 /* Coordinator.swift */,
18F2BA9B27D25B4000DD1988 /* SwiftUI */,
);
path = Other;
sourceTree = "<group>";
};
1863A41D27BA784300B52E4D /* LoginScreen */ = {
18F2BA7E27D25B4000DD1988 /* Routers */ = {
isa = PBXGroup;
children = (
1863A45A27BA7B4700B52E4D /* LoginScreenCoordinator.swift */,
1863A42827BA784300B52E4D /* LoginScreenModels.swift */,
1863A41F27BA784300B52E4D /* LoginScreenViewModel.swift */,
1863A42727BA784300B52E4D /* LoginScreenViewModelProtocol.swift */,
1863A42227BA784300B52E4D /* Test */,
1863A42927BA784300B52E4D /* View */,
);
path = LoginScreen;
sourceTree = "<group>";
};
1863A42227BA784300B52E4D /* Test */ = {
isa = PBXGroup;
children = (
1863A42327BA784300B52E4D /* UI */,
1863A42527BA784300B52E4D /* Unit */,
);
path = Test;
sourceTree = "<group>";
};
1863A42327BA784300B52E4D /* UI */ = {
isa = PBXGroup;
children = (
1863A42427BA784300B52E4D /* LoginScreenUITests.swift */,
);
path = UI;
sourceTree = "<group>";
};
1863A42527BA784300B52E4D /* Unit */ = {
isa = PBXGroup;
children = (
1863A42627BA784300B52E4D /* LoginScreenViewModelTests.swift */,
);
path = Unit;
sourceTree = "<group>";
};
1863A42927BA784300B52E4D /* View */ = {
isa = PBXGroup;
children = (
1863A42A27BA784300B52E4D /* LoginScreen.swift */,
);
path = View;
sourceTree = "<group>";
};
1863A43627BA789800B52E4D /* SwiftUI */ = {
isa = PBXGroup;
children = (
1863A43727BA789800B52E4D /* ViewModel */,
);
path = SwiftUI;
sourceTree = "<group>";
};
1863A43727BA789800B52E4D /* ViewModel */ = {
isa = PBXGroup;
children = (
1863A43827BA789800B52E4D /* StateStoreViewModel.swift */,
1863A43927BA789800B52E4D /* BindableState.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
1863A44027BA79FF00B52E4D /* Routers */ = {
isa = PBXGroup;
children = (
1863A44127BA79FF00B52E4D /* NavigationRouterStore.swift */,
1863A44227BA79FF00B52E4D /* NavigationRouter.swift */,
1863A44327BA79FF00B52E4D /* RootRouterType.swift */,
1863A44427BA79FF00B52E4D /* NavigationRouterStoreProtocol.swift */,
1863A44527BA79FF00B52E4D /* RootRouter.swift */,
1863A44627BA79FF00B52E4D /* Presentable.swift */,
1863A44727BA79FF00B52E4D /* NavigationModule.swift */,
1863A44827BA79FF00B52E4D /* NavigationRouterType.swift */,
18F2BA7F27D25B4000DD1988 /* NavigationRouterStore.swift */,
18F2BA8027D25B4000DD1988 /* NavigationRouter.swift */,
18F2BA8127D25B4000DD1988 /* RootRouterType.swift */,
18F2BA8227D25B4000DD1988 /* NavigationRouterStoreProtocol.swift */,
18F2BA8327D25B4000DD1988 /* RootRouter.swift */,
18F2BA8427D25B4000DD1988 /* Presentable.swift */,
18F2BA8527D25B4000DD1988 /* NavigationModule.swift */,
18F2BA8627D25B4000DD1988 /* NavigationRouterType.swift */,
);
path = Routers;
sourceTree = "<group>";
};
1863A45127BA7A7800B52E4D /* WeakDictionary */ = {
18F2BA8727D25B4000DD1988 /* Activity */ = {
isa = PBXGroup;
children = (
1863A45227BA7A7800B52E4D /* WeakDictionaryKeyReference.swift */,
1863A45327BA7A7800B52E4D /* WeakDictionaryReference.swift */,
1863A45427BA7A7800B52E4D /* WeakDictionary.swift */,
1863A45527BA7A7800B52E4D /* WeakKeyDictionary.swift */,
18F2BA8827D25B4000DD1988 /* Toasts */,
18F2BA8B27D25B4000DD1988 /* ActivityPresentable.swift */,
18F2BA8C27D25B4000DD1988 /* ActivityCenter.swift */,
18F2BA8D27D25B4000DD1988 /* LabelledActivityIndicatorView.swift */,
18F2BA8E27D25B4000DD1988 /* ActivityPresenters */,
18F2BA9127D25B4000DD1988 /* Activity.swift */,
18F2BA9227D25B4000DD1988 /* ActivityRequest.swift */,
18F2BA9327D25B4000DD1988 /* ActivityDismissal.swift */,
);
path = Activity;
sourceTree = "<group>";
};
18F2BA8827D25B4000DD1988 /* Toasts */ = {
isa = PBXGroup;
children = (
18F2BA8927D25B4000DD1988 /* RoundedToastView.swift */,
18F2BA8A27D25B4000DD1988 /* RectangleToastView.swift */,
);
path = Toasts;
sourceTree = "<group>";
};
18F2BA8E27D25B4000DD1988 /* ActivityPresenters */ = {
isa = PBXGroup;
children = (
18F2BA8F27D25B4000DD1988 /* ToastActivityPresenter.swift */,
18F2BA9027D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift */,
);
path = ActivityPresenters;
sourceTree = "<group>";
};
18F2BA9527D25B4000DD1988 /* WeakDictionary */ = {
isa = PBXGroup;
children = (
18F2BA9627D25B4000DD1988 /* WeakDictionaryKeyReference.swift */,
18F2BA9727D25B4000DD1988 /* WeakDictionaryReference.swift */,
18F2BA9827D25B4000DD1988 /* WeakDictionary.swift */,
18F2BA9927D25B4000DD1988 /* WeakKeyDictionary.swift */,
);
path = WeakDictionary;
sourceTree = "<group>";
};
1863A45C27BAA5F100B52E4D /* Splash */ = {
18F2BA9B27D25B4000DD1988 /* SwiftUI */ = {
isa = PBXGroup;
children = (
1863A45D27BAA60300B52E4D /* SplashViewController.swift */,
1863A45E27BAA60300B52E4D /* SplashViewController.xib */,
18F2BA9C27D25B4000DD1988 /* ViewModel */,
);
path = SwiftUI;
sourceTree = "<group>";
};
18F2BA9C27D25B4000DD1988 /* ViewModel */ = {
isa = PBXGroup;
children = (
18F2BA9D27D25B4000DD1988 /* StateStoreViewModel.swift */,
18F2BA9E27D25B4000DD1988 /* BindableState.swift */,
);
path = ViewModel;
sourceTree = "<group>";
};
18F2BA9F27D25B4000DD1988 /* Screens */ = {
isa = PBXGroup;
children = (
18F2BAA027D25B4000DD1988 /* Splash */,
18F2BACB27D25B4000DD1988 /* LoginScreen */,
18F2BAA327D25B4000DD1988 /* HomeScreen */,
18F2BABE27D25B4000DD1988 /* RoomScreen */,
);
path = Screens;
sourceTree = "<group>";
};
18F2BAA027D25B4000DD1988 /* Splash */ = {
isa = PBXGroup;
children = (
18F2BAA127D25B4000DD1988 /* SplashViewController.swift */,
18F2BAA227D25B4000DD1988 /* SplashViewController.xib */,
);
path = Splash;
sourceTree = "<group>";
};
1863A47827BAA8A900B52E4D /* HomeScreen */ = {
18F2BAA327D25B4000DD1988 /* HomeScreen */ = {
isa = PBXGroup;
children = (
1863A47927BAA8A900B52E4D /* HomeScreenCoordinator.swift */,
1863A48027BAA8A900B52E4D /* HomeScreenModels.swift */,
1863A48427BAA8A900B52E4D /* HomeScreenViewModel.swift */,
1863A47F27BAA8A900B52E4D /* HomeScreenViewModelProtocol.swift */,
1863A47A27BAA8A900B52E4D /* Test */,
1863A48127BAA8A900B52E4D /* View */,
18F2BAAB27D25B4000DD1988 /* HomeScreenModels.swift */,
18F2BAA427D25B4000DD1988 /* HomeScreenCoordinator.swift */,
18F2BAAA27D25B4000DD1988 /* HomeScreenViewModelProtocol.swift */,
18F2BAAE27D25B4000DD1988 /* HomeScreenViewModel.swift */,
18F2BAAC27D25B4000DD1988 /* View */,
18F2BAA527D25B4000DD1988 /* Test */,
);
path = HomeScreen;
sourceTree = "<group>";
};
1863A47A27BAA8A900B52E4D /* Test */ = {
18F2BAA527D25B4000DD1988 /* Test */ = {
isa = PBXGroup;
children = (
1863A47B27BAA8A900B52E4D /* UI */,
1863A47D27BAA8A900B52E4D /* Unit */,
18F2BAA627D25B4000DD1988 /* UI */,
18F2BAA827D25B4000DD1988 /* Unit */,
);
path = Test;
sourceTree = "<group>";
};
1863A47B27BAA8A900B52E4D /* UI */ = {
18F2BAA627D25B4000DD1988 /* UI */ = {
isa = PBXGroup;
children = (
1863A47C27BAA8A900B52E4D /* HomeScreenUITests.swift */,
18F2BAA727D25B4000DD1988 /* HomeScreenUITests.swift */,
);
path = UI;
sourceTree = "<group>";
};
1863A47D27BAA8A900B52E4D /* Unit */ = {
18F2BAA827D25B4000DD1988 /* Unit */ = {
isa = PBXGroup;
children = (
1863A47E27BAA8A900B52E4D /* HomeScreenViewModelTests.swift */,
18F2BAA927D25B4000DD1988 /* HomeScreenViewModelTests.swift */,
);
path = Unit;
sourceTree = "<group>";
};
1863A48127BAA8A900B52E4D /* View */ = {
18F2BAAC27D25B4000DD1988 /* View */ = {
isa = PBXGroup;
children = (
1863A48227BAA8A900B52E4D /* HomeScreen.swift */,
18F2BAAD27D25B4000DD1988 /* HomeScreen.swift */,
);
path = View;
sourceTree = "<group>";
};
1863A49227BAAA6700B52E4D /* Models */ = {
18F2BABE27D25B4000DD1988 /* RoomScreen */ = {
isa = PBXGroup;
children = (
1863A49327BAAA6700B52E4D /* RoomModel.swift */,
182BC41F27BE667200A30C33 /* RoomModelProtocol.swift */,
182BC42127BE6C6900A30C33 /* MockRoomModel.swift */,
18F2BACA27D25B4000DD1988 /* RoomScreenModels.swift */,
18F2BAC427D25B4000DD1988 /* RoomScreenCoordinator.swift */,
18F2BAC627D25B4000DD1988 /* RoomScreenViewModelProtocol.swift */,
18F2BAC527D25B4000DD1988 /* RoomScreenViewModel.swift */,
18F2BAC727D25B4000DD1988 /* View */,
18F2BABF27D25B4000DD1988 /* Test */,
);
path = Models;
path = RoomScreen;
sourceTree = "<group>";
};
18F2BABF27D25B4000DD1988 /* Test */ = {
isa = PBXGroup;
children = (
18F2BAC027D25B4000DD1988 /* UI */,
18F2BAC227D25B4000DD1988 /* Unit */,
);
path = Test;
sourceTree = "<group>";
};
18F2BAC027D25B4000DD1988 /* UI */ = {
isa = PBXGroup;
children = (
18F2BAC127D25B4000DD1988 /* RoomScreenUITests.swift */,
);
path = UI;
sourceTree = "<group>";
};
18F2BAC227D25B4000DD1988 /* Unit */ = {
isa = PBXGroup;
children = (
18F2BAC327D25B4000DD1988 /* RoomScreenViewModelTests.swift */,
);
path = Unit;
sourceTree = "<group>";
};
18F2BAC727D25B4000DD1988 /* View */ = {
isa = PBXGroup;
children = (
18F2BAC827D25B4000DD1988 /* RoomScreen.swift */,
);
path = View;
sourceTree = "<group>";
};
18F2BACB27D25B4000DD1988 /* LoginScreen */ = {
isa = PBXGroup;
children = (
18F2BAD327D25B4000DD1988 /* LoginScreenModels.swift */,
18F2BAD427D25B4000DD1988 /* LoginScreenCoordinator.swift */,
18F2BAD227D25B4000DD1988 /* LoginScreenViewModelProtocol.swift */,
18F2BACC27D25B4000DD1988 /* LoginScreenViewModel.swift */,
18F2BACD27D25B4000DD1988 /* Test */,
18F2BAD527D25B4000DD1988 /* View */,
);
path = LoginScreen;
sourceTree = "<group>";
};
18F2BACD27D25B4000DD1988 /* Test */ = {
isa = PBXGroup;
children = (
18F2BACE27D25B4000DD1988 /* UI */,
18F2BAD027D25B4000DD1988 /* Unit */,
);
path = Test;
sourceTree = "<group>";
};
18F2BACE27D25B4000DD1988 /* UI */ = {
isa = PBXGroup;
children = (
18F2BACF27D25B4000DD1988 /* LoginScreenUITests.swift */,
);
path = UI;
sourceTree = "<group>";
};
18F2BAD027D25B4000DD1988 /* Unit */ = {
isa = PBXGroup;
children = (
18F2BAD127D25B4000DD1988 /* LoginScreenViewModelTests.swift */,
);
path = Unit;
sourceTree = "<group>";
};
18F2BAD527D25B4000DD1988 /* View */ = {
isa = PBXGroup;
children = (
18F2BAD627D25B4000DD1988 /* LoginScreen.swift */,
);
path = View;
sourceTree = "<group>";
};
/* End PBXGroup section */
@@ -589,7 +676,7 @@
buildActionMask = 2147483647;
files = (
1850257127B6A135002E6B18 /* LaunchScreen.storyboard in Resources */,
1863A46027BAA60300B52E4D /* SplashViewController.xib in Resources */,
18F2BAFA27D25B4000DD1988 /* SplashViewController.xib in Resources */,
1850257027B6A135002E6B18 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -635,52 +722,63 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1863A44B27BA79FF00B52E4D /* RootRouterType.swift in Sources */,
182BC47327C4CD6D00A30C33 /* ToastActivityPresenter.swift in Sources */,
1863A48C27BAA8A900B52E4D /* HomeScreenViewModel.swift in Sources */,
1863A41727BA716A00B52E4D /* KeychainControllerProtocol.swift in Sources */,
1863A45B27BA7B4700B52E4D /* LoginScreenCoordinator.swift in Sources */,
182BC47127C4CD6D00A30C33 /* ActivityPresentable.swift in Sources */,
1863A41427BA716A00B52E4D /* AuthenticationCoordinator.swift in Sources */,
1863A43B27BA789800B52E4D /* BindableState.swift in Sources */,
182BC47027C4CD6D00A30C33 /* RectangleToastView.swift in Sources */,
1863A42C27BA784300B52E4D /* LoginScreenViewModel.swift in Sources */,
1863A44F27BA79FF00B52E4D /* NavigationModule.swift in Sources */,
1863A43027BA784300B52E4D /* LoginScreenViewModelProtocol.swift in Sources */,
1863A48827BAA8A900B52E4D /* HomeScreenViewModelProtocol.swift in Sources */,
1863A44D27BA79FF00B52E4D /* RootRouter.swift in Sources */,
182BC47627C4CD6D00A30C33 /* ActivityRequest.swift in Sources */,
1863A45F27BAA60300B52E4D /* SplashViewController.swift in Sources */,
1863A48A27BAA8A900B52E4D /* HomeScreen.swift in Sources */,
1863A44927BA79FF00B52E4D /* NavigationRouterStore.swift in Sources */,
1863A45727BA7A7800B52E4D /* WeakDictionaryReference.swift in Sources */,
1863A48927BAA8A900B52E4D /* HomeScreenModels.swift in Sources */,
1863A45027BA79FF00B52E4D /* NavigationRouterType.swift in Sources */,
182BC42027BE667200A30C33 /* RoomModelProtocol.swift in Sources */,
1863A45627BA7A7800B52E4D /* WeakDictionaryKeyReference.swift in Sources */,
182BC47927C4CE2200A30C33 /* LabelledActivityIndicatorView.swift in Sources */,
1863A49427BAAA6700B52E4D /* RoomModel.swift in Sources */,
18F2BADE27D25B4000DD1988 /* KeychainControllerProtocol.swift in Sources */,
18F2BAED27D25B4000DD1988 /* FullscreenLoadingActivityPresenter.swift in Sources */,
18F2BB0F27D25B4000DD1988 /* RoomScreen.swift in Sources */,
18F2BAFF27D25B4000DD1988 /* HomeScreenModels.swift in Sources */,
18F2BB1527D25B4000DD1988 /* LoginScreenViewModelProtocol.swift in Sources */,
18F2BAEB27D25B4000DD1988 /* LabelledActivityIndicatorView.swift in Sources */,
18F2BAE427D25B4000DD1988 /* Presentable.swift in Sources */,
18F2BAF927D25B4000DD1988 /* SplashViewController.swift in Sources */,
18F2BAE327D25B4000DD1988 /* RootRouter.swift in Sources */,
18F2BAE527D25B4000DD1988 /* NavigationModule.swift in Sources */,
18F2BB1227D25B4000DD1988 /* LoginScreenViewModel.swift in Sources */,
18F2BAD927D25B4000DD1988 /* MockRoomProxy.swift in Sources */,
18F2BAE727D25B4000DD1988 /* RoundedToastView.swift in Sources */,
18F2BAF227D25B4000DD1988 /* WeakDictionaryKeyReference.swift in Sources */,
18F2BB2427D262A900DD1988 /* RoomTimelineItemProtocol.swift in Sources */,
18F2BAE027D25B4000DD1988 /* NavigationRouter.swift in Sources */,
18F2BAF627D25B4000DD1988 /* Coordinator.swift in Sources */,
18F2BAEA27D25B4000DD1988 /* ActivityCenter.swift in Sources */,
18F2BB1A27D25BE800DD1988 /* RoomTimelineController.swift in Sources */,
18F2BAF327D25B4000DD1988 /* WeakDictionaryReference.swift in Sources */,
18F2BB2A27D2648900DD1988 /* RoomTimelineControllerProtocol.swift in Sources */,
18F2BAF127D25B4000DD1988 /* MXLog.swift in Sources */,
18F2BAF727D25B4000DD1988 /* StateStoreViewModel.swift in Sources */,
18F2BAF427D25B4000DD1988 /* WeakDictionary.swift in Sources */,
18F2BB1127D25B4000DD1988 /* RoomScreenModels.swift in Sources */,
18F2BADB27D25B4000DD1988 /* AuthenticationCoordinator.swift in Sources */,
1850256F27B6A135002E6B18 /* AppDelegate.swift in Sources */,
182BC47727C4CD6D00A30C33 /* ActivityDismissal.swift in Sources */,
1863A41627BA716A00B52E4D /* KeychainController.swift in Sources */,
1863A41527BA716A00B52E4D /* UserSession.swift in Sources */,
1863A43A27BA789800B52E4D /* StateStoreViewModel.swift in Sources */,
1863A45827BA7A7800B52E4D /* WeakDictionary.swift in Sources */,
1863A43227BA784300B52E4D /* LoginScreen.swift in Sources */,
1863A48527BAA8A900B52E4D /* HomeScreenCoordinator.swift in Sources */,
1863A43F27BA790000B52E4D /* Coordinator.swift in Sources */,
1863A41C27BA76B900B52E4D /* MXLog.swift in Sources */,
182BC46F27C4CD6D00A30C33 /* RoundedToastView.swift in Sources */,
182BC47527C4CD6D00A30C33 /* Activity.swift in Sources */,
182BC47B27C4D05200A30C33 /* FullscreenLoadingActivityPresenter.swift in Sources */,
1863A44E27BA79FF00B52E4D /* Presentable.swift in Sources */,
182BC42227BE6C6900A30C33 /* MockRoomModel.swift in Sources */,
18F2BAE627D25B4000DD1988 /* NavigationRouterType.swift in Sources */,
18F2BAE927D25B4000DD1988 /* ActivityPresentable.swift in Sources */,
18F2BAF827D25B4000DD1988 /* BindableState.swift in Sources */,
18F2BAD827D25B4000DD1988 /* RoomProxy.swift in Sources */,
18F2BB1827D25B4000DD1988 /* LoginScreen.swift in Sources */,
18F2BAE227D25B4000DD1988 /* NavigationRouterStoreProtocol.swift in Sources */,
18F2BAD727D25B4000DD1988 /* RoomProxyProtocol.swift in Sources */,
18F2BB1727D25B4000DD1988 /* LoginScreenCoordinator.swift in Sources */,
18F2BAF527D25B4000DD1988 /* WeakKeyDictionary.swift in Sources */,
18F2BADF27D25B4000DD1988 /* NavigationRouterStore.swift in Sources */,
18F2BAFE27D25B4000DD1988 /* HomeScreenViewModelProtocol.swift in Sources */,
18F2BAE827D25B4000DD1988 /* RectangleToastView.swift in Sources */,
18F2BB2627D262FA00DD1988 /* TextRoomTimelineItem.swift in Sources */,
18F2BB1627D25B4000DD1988 /* LoginScreenModels.swift in Sources */,
18F2BADA27D25B4000DD1988 /* RoomTimelineProvider.swift in Sources */,
18F2BB0027D25B4000DD1988 /* HomeScreen.swift in Sources */,
18F2BB2827D2647A00DD1988 /* MockRoomTimelineController.swift in Sources */,
18F2BB0127D25B4000DD1988 /* HomeScreenViewModel.swift in Sources */,
18F2BAF027D25B4000DD1988 /* ActivityDismissal.swift in Sources */,
18F2BADD27D25B4000DD1988 /* KeychainController.swift in Sources */,
18F2BAFB27D25B4000DD1988 /* HomeScreenCoordinator.swift in Sources */,
18F2BB0C27D25B4000DD1988 /* RoomScreenCoordinator.swift in Sources */,
18F2BB0E27D25B4000DD1988 /* RoomScreenViewModelProtocol.swift in Sources */,
18F2BB0D27D25B4000DD1988 /* RoomScreenViewModel.swift in Sources */,
18F2BAE127D25B4000DD1988 /* RootRouterType.swift in Sources */,
1850256C27B6A135002E6B18 /* AppCoordinator.swift in Sources */,
1863A43127BA784300B52E4D /* LoginScreenModels.swift in Sources */,
1863A44A27BA79FF00B52E4D /* NavigationRouter.swift in Sources */,
182BC47227C4CD6D00A30C33 /* ActivityCenter.swift in Sources */,
1863A45927BA7A7800B52E4D /* WeakKeyDictionary.swift in Sources */,
1863A44C27BA79FF00B52E4D /* NavigationRouterStoreProtocol.swift in Sources */,
18F2BADC27D25B4000DD1988 /* UserSession.swift in Sources */,
18F2BAEF27D25B4000DD1988 /* ActivityRequest.swift in Sources */,
18F2BAEE27D25B4000DD1988 /* Activity.swift in Sources */,
18F2BAEC27D25B4000DD1988 /* ToastActivityPresenter.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -688,8 +786,9 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1863A48F27BAA8CC00B52E4D /* HomeScreenViewModelTests.swift in Sources */,
1863A43427BA786400B52E4D /* LoginScreenViewModelTests.swift in Sources */,
18F2BB2027D25D3400DD1988 /* RoomScreenUITests.swift in Sources */,
18F2BB1E27D25D2200DD1988 /* HomeScreenViewModelTests.swift in Sources */,
18F2BB1D27D25D1C00DD1988 /* LoginScreenViewModelTests.swift in Sources */,
1850253F27B6918D002E6B18 /* ElementXTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -698,10 +797,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
18F2BB2127D25D3C00DD1988 /* HomeScreenUITests.swift in Sources */,
1850254B27B6918D002E6B18 /* ElementXUITestsLaunchTests.swift in Sources */,
1863A48E27BAA8C800B52E4D /* HomeScreenUITests.swift in Sources */,
1863A43527BA788500B52E4D /* LoginScreenUITests.swift in Sources */,
1850254927B6918D002E6B18 /* ElementXUITests.swift in Sources */,
18F2BB2227D25D4600DD1988 /* LoginScreenUITests.swift in Sources */,
18F2BB1F27D25D3100DD1988 /* RoomScreenViewModelTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -24,7 +24,7 @@
"repositoryURL": "https://github.com/matrix-org/matrix-rust-components-swift.git",
"state": {
"branch": "main",
"revision": "34aa07d11d0e29145b92f42b9d86aa140d598e19",
"revision": "497122432c79488e370df2164ae5637f32f82ca3",
"version": null
}
},

View File

@@ -27,7 +27,6 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
init() {
splashViewController = SplashViewController()
mainNavigationController = UINavigationController(rootViewController: splashViewController)
mainNavigationController.navigationBar.isHidden = true
window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = mainNavigationController
@@ -89,6 +88,8 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
switch result {
case .logout:
self?.authenticationCoordinator.logout()
case .selectRoom(let roomIdentifier):
self?.presentRoomWithIdentifier(roomIdentifier)
}
}
@@ -96,6 +97,35 @@ class AppCoordinator: AuthenticationCoordinatorDelegate, Coordinator {
navigationRouter.setRootModule(coordinator)
}
private func presentRoomWithIdentifier(_ roomIdentifier: String) {
guard let userSession = authenticationCoordinator.userSession else {
fatalError("User session should be already setup at this point")
}
showLoadingIndicator()
userSession.getRoomList { [weak self] rooms in
guard let self = self else { return }
self.hideLoadingIndicator()
guard let roomProxy = rooms.filter({ $0.id == roomIdentifier}).first else {
MXLog.error("Invalid room identifier: \(roomIdentifier)")
return
}
let parameters = RoomScreenCoordinatorParameters(roomProxy: roomProxy)
let coordinator = RoomScreenCoordinator(parameters: parameters)
coordinator.completion = { [weak self] result in
}
self.add(childCoordinator: coordinator)
self.navigationRouter.push(coordinator)
}
}
private func showLoadingIndicator() {
let presenter = FullscreenLoadingActivityPresenter(label: "Loading",
on: mainNavigationController)

View File

@@ -1,109 +0,0 @@
//
// RoomModel.swift
// ElementX
//
// Created by Stefan Ceriu on 14.02.2022.
//
import Foundation
import UIKit
import MatrixRustSDK
enum RoomModelError: Error {
case failedRetrievingDisplayName
case failedRetrievingAvatar
}
struct RoomModel: RoomModelProtocol {
private let room: Room
init(room: Room) {
self.room = room
}
var identifier: String {
return room.identifier()
}
var isDirect: Bool {
return room.isDirect()
}
var isPublic: Bool {
return room.isPublic()
}
var isSpace: Bool {
return room.isSpace()
}
var isEncrypted: Bool {
return room.isEncrypted()
}
var name: String? {
return room.name()
}
var displayName: String {
do {
return try room.displayName()
} catch {
MXLog.error("Failed retrieving room name with error: \(error)")
return "Error"
}
}
var topic: String? {
return room.topic()
}
var lastMessage: String? {
guard let lastMessage = try? room.messages().last else {
return "Last message unknown"
}
return "\(lastMessage.sender()): \(lastMessage.content())"
}
var avatarURL: URL? {
guard let urlString = room.avatarUrl() else {
return nil
}
return URL(string: urlString)
}
func loadDisplayName(_ completion: @escaping (Result<String, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let displayName = try room.displayName()
DispatchQueue.main.async {
completion(.success(displayName))
}
} catch {
DispatchQueue.main.async {
completion(.failure(RoomModelError.failedRetrievingDisplayName))
}
}
}
}
func loadAvatar(_ completion: @escaping (Result<UIImage?, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let avatarData = try room.avatar()
DispatchQueue.main.async {
completion(.success(UIImage(data: Data(bytes: avatarData, count: avatarData.count))))
}
} catch {
DispatchQueue.main.async {
completion(.failure(RoomModelError.failedRetrievingAvatar))
}
}
}
}
}

View File

@@ -24,6 +24,7 @@ struct HomeScreenCoordinatorParameters {
enum HomeScreenCoordinatorResult {
case logout
case selectRoom(roomIdentifier: String)
}
final class HomeScreenCoordinator: Coordinator, Presentable {
@@ -70,12 +71,14 @@ final class HomeScreenCoordinator: Coordinator, Presentable {
break
}
})
case .selectRoom(let roomIdentifier):
self.completion?(.selectRoom(roomIdentifier: roomIdentifier))
}
}
parameters.userSession.callbacks.sink { [weak self] result in
switch result {
case .updatedData:
case .updatedRoomsList:
self?.updateRoomsList()
}
}.store(in: &cancellables)

View File

@@ -20,12 +20,14 @@ import UIKit
enum HomeScreenViewModelResult {
case logout
case loadUserAvatar
case selectRoom(roomIdentifier: String)
}
enum HomeScreenViewAction {
case logout
case loadUserAvatar
case loadRoomData(roomId: String)
case loadRoomData(roomIdentifier: String)
case selectRoom(roomIdentifier: String)
}
struct HomeScreenViewState: BindableState {
@@ -65,3 +67,11 @@ struct HomeScreenRoom: Identifiable {
let isDirect: Bool
let isEncrypted: Bool
}
extension MutableCollection where Element == HomeScreenRoom {
mutating func updateEach(_ update: (inout Element) -> Void) {
for index in indices {
update(&self[index])
}
}
}

View File

@@ -15,6 +15,7 @@
//
import SwiftUI
import Combine
import Kingfisher
@available(iOS 14, *)
@@ -24,19 +25,15 @@ typealias HomeScreenViewModelType = StateStoreViewModel<HomeScreenViewState,
@available(iOS 14, *)
class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol {
// MARK: - Properties
// MARK: Private
private var roomList: [RoomModelProtocol]? {
private var roomUpdateListeners = Set<AnyCancellable>()
private var roomList: [RoomProxyProtocol]? {
didSet {
self.state.isLoadingRooms = (roomList == nil)
}
}
private let imageCache: ImageCache
// MARK: Public
var completion: ((HomeScreenViewModelResult) -> Void)?
// MARK: - Setup
@@ -52,23 +49,38 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
switch viewAction {
case .logout:
self.completion?(.logout)
case .loadRoomData(let roomId):
self.loadAvatarForRoomWithIdentifier(roomId)
self.loadRoomDisplayNameForRoomWithIdentifier(roomId)
case .loadRoomData(let roomIdentifier):
self.loadAvatarForRoomWithIdentifier(roomIdentifier)
self.loadRoomDisplayNameForRoomWithIdentifier(roomIdentifier)
case .loadUserAvatar:
self.completion?(.loadUserAvatar)
case .selectRoom(let roomIdentifier):
self.completion?(.selectRoom(roomIdentifier: roomIdentifier))
}
}
func updateWithRoomList(_ roomList: [RoomModelProtocol]) {
func updateWithRoomList(_ roomList: [RoomProxyProtocol]) {
roomUpdateListeners.removeAll()
self.roomList = roomList
state.rooms = roomList.map { roomModel in
HomeScreenRoom(id: roomModel.identifier,
displayName: roomModel.name,
topic: roomModel.topic,
lastMessage: roomModel.lastMessage,
isDirect: roomModel.isDirect,
isEncrypted: roomModel.isEncrypted)
roomList.forEach({ roomProxy in
roomProxy.paginateBackwards(start: 0, finish: 1)
roomProxy.startLiveEventListener()
roomProxy.callbacks.sink { [weak self] callback in
switch callback {
case .updatedLastMessage:
self?.updateRoomForProxy(roomProxy)
default:
break
}
}
.store(in: &roomUpdateListeners)
})
state.rooms = roomList.map { roomProxy in
roomFromProxy(roomProxy)
}
}
@@ -79,7 +91,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
// MARK: - Private
private func loadAvatarForRoomWithIdentifier(_ roomIdentifier: String) {
guard let room = roomList?.filter({ $0.identifier == roomIdentifier }).first,
guard let room = roomList?.filter({ $0.id == roomIdentifier }).first,
let cacheKey = room.avatarURL?.path else {
return
}
@@ -116,7 +128,7 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
}
private func loadRoomDisplayNameForRoomWithIdentifier(_ roomIdentifier: String) {
guard let room = roomList?.filter({ $0.identifier == roomIdentifier }).first else {
guard let room = roomList?.filter({ $0.id == roomIdentifier }).first else {
return
}
@@ -147,4 +159,23 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
self.state.rooms[index].avatar = avatar
}
private func updateRoomForProxy(_ roomProxy: RoomProxyProtocol) {
state.rooms.updateEach { room in
if room.id != roomProxy.id {
return
}
room = roomFromProxy(roomProxy)
}
}
private func roomFromProxy(_ roomProxy: RoomProxyProtocol) -> HomeScreenRoom {
HomeScreenRoom(id: roomProxy.id,
displayName: roomProxy.name,
topic: roomProxy.topic,
lastMessage: roomProxy.lastMessage,
isDirect: roomProxy.isDirect,
isEncrypted: roomProxy.isEncrypted)
}
}

View File

@@ -22,7 +22,7 @@ protocol HomeScreenViewModelProtocol {
var context: HomeScreenViewModelType.Context { get }
func updateWithRoomList(_ roomList: [RoomModelProtocol])
func updateWithRoomList(_ roomList: [RoomProxyProtocol])
func updateWithUserAvatar(_ avatar: UIImage?)
}

View File

@@ -106,40 +106,44 @@ struct RoomCell: View {
let context: HomeScreenViewModel.Context
var body: some View {
HStack(spacing: 16.0) {
if let avatar = room.avatar {
Image(uiImage: avatar)
.resizable()
.scaledToFill()
.frame(width: 40, height: 40)
.mask(Circle())
} else {
let _ = context.send(viewAction: .loadRoomData(roomId: room.id))
Image(systemName: "person.3")
.frame(width: 40, height: 40)
}
VStack(alignment: .leading, spacing: 4.0) {
Text(roomName(room))
.font(.headline)
.fontWeight(.regular)
if let roomTopic = room.topic, roomTopic.count > 0 {
Text(roomTopic)
.font(.footnote)
.fontWeight(.bold)
.lineLimit(1)
Button {
context.send(viewAction: .selectRoom(roomIdentifier: room.id))
} label: {
HStack(spacing: 16.0) {
if let avatar = room.avatar {
Image(uiImage: avatar)
.resizable()
.scaledToFill()
.frame(width: 40, height: 40)
.mask(Circle())
} else {
let _ = context.send(viewAction: .loadRoomData(roomIdentifier: room.id))
Image(systemName: "person.3")
.frame(width: 40, height: 40)
}
if let lastMessage = room.lastMessage {
Text(lastMessage)
.font(.footnote)
.fontWeight(.medium)
.lineLimit(1)
VStack(alignment: .leading, spacing: 4.0) {
Text(roomName(room))
.font(.headline)
.fontWeight(.regular)
if let roomTopic = room.topic, roomTopic.count > 0 {
Text(roomTopic)
.font(.footnote)
.fontWeight(.bold)
.lineLimit(1)
}
if let lastMessage = room.lastMessage {
Text(lastMessage)
.font(.footnote)
.fontWeight(.medium)
.lineLimit(1)
}
}
}
.frame(minHeight: 60.0)
}
.frame(minHeight: 60.0)
}
private func roomName(_ room: HomeScreenRoom) -> String {
@@ -153,9 +157,9 @@ struct HomeScreen_Previews: PreviewProvider {
static var previews: some View {
let viewModel = HomeScreenViewModel(userDisplayName: "Johnny Appleseed", imageCache: ImageCache.default)
let rooms = [MockRoomModel(displayName: "Alfa"),
MockRoomModel(displayName: "Beta"),
MockRoomModel(displayName: "Omega")]
let rooms = [MockRoomProxy(displayName: "Alfa"),
MockRoomProxy(displayName: "Beta"),
MockRoomProxy(displayName: "Omega")]
viewModel.updateWithRoomList(rooms)

View File

@@ -0,0 +1,67 @@
//
// Copyright 2021 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 SwiftUI
struct RoomScreenCoordinatorParameters {
let roomProxy: RoomProxyProtocol
}
final class RoomScreenCoordinator: Coordinator, Presentable {
// MARK: - Properties
// MARK: Private
private let parameters: RoomScreenCoordinatorParameters
private let roomScreenHostingController: UIViewController
private var roomScreenViewModel: RoomScreenViewModelProtocol
// MARK: Public
// Must be used only internally
var childCoordinators: [Coordinator] = []
var completion: ((RoomScreenViewModelResult) -> Void)?
// MARK: - Setup
@available(iOS 14.0, *)
init(parameters: RoomScreenCoordinatorParameters) {
self.parameters = parameters
let timelineProvider = RoomTimelineProvider(roomProxy: parameters.roomProxy)
let timelineController = RoomTimelineController(timelineProvider: timelineProvider)
let viewModel = RoomScreenViewModel(roomProxy: parameters.roomProxy, timelineController: timelineController)
let view = RoomScreen(context: viewModel.context)
roomScreenViewModel = viewModel
roomScreenHostingController = UIHostingController(rootView: view)
}
// MARK: - Public
func start() {
MXLog.debug("[RoomScreenCoordinator] did start.")
roomScreenViewModel.completion = { [weak self] result in
MXLog.debug("[RoomScreenCoordinator] RoomScreenViewModel did complete with result: \(result).")
guard let self = self else { return }
self.completion?(result)
}
}
func toPresentable() -> UIViewController {
return self.roomScreenHostingController
}
}

View File

@@ -0,0 +1,50 @@
//
// Copyright 2021 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 Foundation
enum RoomScreenViewModelResult {
}
enum RoomScreenViewAction {
case loadPreviousPage
}
private var dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
return dateFormatter
}()
struct RoomScreenMessage: Identifiable, Equatable {
let id: String
let sender: String
let text: String
let originServerTs: Date
var timestamp: String {
dateFormatter.string(from: originServerTs)
}
}
struct RoomScreenViewState: BindableState {
var roomTitle: String?
var isLoading: Bool = false
var messages: [RoomScreenMessage] = []
}

View File

@@ -0,0 +1,74 @@
//
// Copyright 2021 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 SwiftUI
@available(iOS 14, *)
typealias RoomScreenViewModelType = StateStoreViewModel<RoomScreenViewState,
Never,
RoomScreenViewAction>
@available(iOS 14, *)
class RoomScreenViewModel: RoomScreenViewModelType, RoomScreenViewModelProtocol {
private struct Constants {
static let backPaginationPageSize: UInt = 20
}
private let roomProxy: RoomProxyProtocol
private let timelineController: RoomTimelineControllerProtocol
var completion: ((RoomScreenViewModelResult) -> Void)?
// MARK: - Setup
init(roomProxy: RoomProxyProtocol, timelineController: RoomTimelineControllerProtocol) {
self.roomProxy = roomProxy
self.timelineController = timelineController
super.init(initialViewState: RoomScreenViewState())
state.messages = buildRoomScreenMessages(timelineController.timelineItems)
timelineController.callbacks.sink { [weak self] callback in
guard let self = self else { return }
switch callback {
case .updatedTimelineItems:
self.state.messages = self.buildRoomScreenMessages(timelineController.timelineItems)
}
}.store(in: &cancellables)
timelineController.paginateBackwards(Constants.backPaginationPageSize)
}
// MARK: - Public
override func process(viewAction: RoomScreenViewAction) {
switch viewAction {
case .loadPreviousPage:
timelineController.paginateBackwards(Constants.backPaginationPageSize)
}
}
// MARK: - Private
private func buildRoomScreenMessages(_ timelineItems: [RoomTimelineItemProtocol]) -> [RoomScreenMessage] {
timelineItems.map { RoomScreenMessage(id: $0.id,
sender: $0.senderDisplayName,
text: $0.text,
originServerTs: $0.originServerTs) }
}
}

View File

@@ -0,0 +1,24 @@
//
// Copyright 2021 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 Foundation
protocol RoomScreenViewModelProtocol {
var completion: ((RoomScreenViewModelResult) -> Void)? { get set }
@available(iOS 14, *)
var context: RoomScreenViewModelType.Context { get }
}

View File

@@ -0,0 +1,17 @@
//
// Copyright 2021 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 XCTest

View File

@@ -0,0 +1,21 @@
//
// Copyright 2021 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 XCTest
class RoomScreenViewModelTests: XCTestCase {
}

View File

@@ -0,0 +1,85 @@
//
// Copyright 2021 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 SwiftUI
struct RoomScreen: View {
let timelineBottomDividerIdentifier = "TimelineBottomDividerIdentifier"
@ObservedObject var context: RoomScreenViewModel.Context
@State var backPaginationMessageIdentifier: String?
var body: some View {
ScrollViewReader { reader in
List {
if backPaginationMessageIdentifier != nil {
HStack {
Spacer()
ProgressView()
Spacer()
}
} else {
Rectangle()
.onAppear {
let _ = MXLog.debug("Request load previous page")
backPaginationMessageIdentifier = context.viewState.messages.first?.id
context.send(viewAction: .loadPreviousPage)
}
.frame(height: 0.0)
}
ForEach(context.viewState.messages) { message in
VStack(alignment: .leading) {
HStack {
Text(message.sender)
Spacer()
Text(message.timestamp)
}
.font(.footnote)
Text(message.text)
}
.listRowSeparator(.hidden)
}
Divider()
.id(timelineBottomDividerIdentifier)
.listRowSeparator(.hidden)
}
.listStyle(.plain)
.onAppear {
reader.scrollTo(timelineBottomDividerIdentifier, anchor: .bottom)
}
.onChange(of: context.viewState.messages) { _ in
if backPaginationMessageIdentifier != nil {
self.backPaginationMessageIdentifier = nil
return
}
reader.scrollTo(timelineBottomDividerIdentifier, anchor: .bottom)
}
}
}
}
// MARK: - Previews
struct RoomScreen_Previews: PreviewProvider {
static var previews: some View {
let viewModel = RoomScreenViewModel(roomProxy: MockRoomProxy(displayName: "Test"),
timelineController: MockRoomTimelineController())
RoomScreen(context: viewModel.context)
}
}

View File

@@ -11,7 +11,7 @@ import Combine
import UIKit
enum UserSessionCallback {
case updatedData
case updatedRoomsList
}
enum UserSessionError: Error {
@@ -19,20 +19,27 @@ enum UserSessionError: Error {
}
private class WeakUserSessionWrapper: ClientDelegate {
weak var userSession: UserSession?
private weak var userSession: UserSession?
init(userSession: UserSession) {
self.userSession = userSession
}
func didReceiveSyncUpdate() {
userSession?.didReceiveSyncUpdate()
DispatchQueue.main.async {
self.userSession?.didReceiveSyncUpdate()
}
}
}
class UserSession: ClientDelegate {
private let client: Client
private var rooms: [RoomProxy] = [] {
didSet {
self.callbacks.send(.updatedRoomsList)
}
}
deinit {
client.setDelegate(delegate: nil)
@@ -81,12 +88,27 @@ class UserSession: ClientDelegate {
}
}
func getRoomList(_ completion: @escaping ([RoomModel]) -> Void) {
func getRoomList(_ completion: @escaping ([RoomProxyProtocol]) -> Void) {
fetchRoomList(completion)
}
// MARK: ClientDelegate
func didReceiveSyncUpdate() {
fetchRoomList { [weak self] rooms in
guard let self = self else { return }
if self.rooms != rooms {
self.rooms = rooms
}
}
}
// MARK: Private
func fetchRoomList(_ completion: @escaping ([RoomProxy]) -> Void) {
DispatchQueue.global(qos: .background).async {
let conversations = self.client.conversations()
let rooms = conversations.map {
return RoomModel(room: $0)
let rooms = self.client.conversations().map {
return RoomProxy(room: $0)
}
DispatchQueue.main.async {
@@ -94,12 +116,4 @@ class UserSession: ClientDelegate {
}
}
}
// MARK: ClientDelegate
func didReceiveSyncUpdate() {
DispatchQueue.main.async {
self.callbacks.send(.updatedData)
}
}
}

View File

@@ -1,5 +1,5 @@
//
// MockRoomModel.swift
// MockRoomProxy.swift
// ElementX
//
// Created by Stefan Ceriu on 17.02.2022.
@@ -7,9 +7,10 @@
import Foundation
import UIKit
import Combine
struct MockRoomModel: RoomModelProtocol {
let identifier = UUID().uuidString
struct MockRoomProxy: RoomProxyProtocol {
let id = UUID().uuidString
let name: String? = nil
let displayName: String
@@ -23,6 +24,8 @@ struct MockRoomModel: RoomModelProtocol {
let isPublic = Bool.random()
let isEncrypted = Bool.random()
var callbacks = PassthroughSubject<RoomProxyCallback, Never>()
func loadDisplayName(_ completion: @escaping (Result<String, Error>) -> Void) {
completion(.success(displayName))
}
@@ -30,4 +33,12 @@ struct MockRoomModel: RoomModelProtocol {
func loadAvatar(_ completion: (Result<UIImage?, Error>) -> Void) {
completion(.success(UIImage(systemName: "wand.and.stars")))
}
func startLiveEventListener() {
}
func paginateBackwards(start: UInt, finish: UInt) {
}
}

View File

@@ -0,0 +1,152 @@
//
// RoomProxy.swift
// ElementX
//
// Created by Stefan Ceriu on 14.02.2022.
//
import Foundation
import UIKit
import Combine
import MatrixRustSDK
enum RoomProxyError: Error {
case failedRetrievingDisplayName
case failedRetrievingAvatar
}
private class WeakRoomProxyWrapper: RoomDelegate {
private weak var roomProxy: RoomProxy?
init(roomProxy: RoomProxy) {
self.roomProxy = roomProxy
}
func didReceiveMessage(message: Message) {
DispatchQueue.main.async {
self.roomProxy?.appendMessage(message)
}
}
func didPaginateBackwards(messages: [Message]) {
DispatchQueue.main.async {
self.roomProxy?.preprendMessages(messages)
}
}
}
class RoomProxy: RoomProxyProtocol, Equatable {
private let room: Room
let callbacks = PassthroughSubject<RoomProxyCallback, Never>()
init(room: Room) {
self.room = room
self.room.setDelegate(delegate: WeakRoomProxyWrapper(roomProxy: self))
}
var id: String {
return room.id()
}
var isDirect: Bool {
return room.isDirect()
}
var isPublic: Bool {
return room.isPublic()
}
var isSpace: Bool {
return room.isSpace()
}
var isEncrypted: Bool {
return room.isEncrypted()
}
var name: String? {
return room.name()
}
var topic: String? {
return room.topic()
}
var lastMessage: String? {
didSet {
callbacks.send(.updatedLastMessage)
}
}
var avatarURL: URL? {
guard let urlString = room.avatarUrl() else {
return nil
}
return URL(string: urlString)
}
func loadDisplayName(_ completion: @escaping (Result<String, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let displayName = try self.room.displayName()
DispatchQueue.main.async {
completion(.success(displayName))
}
} catch {
DispatchQueue.main.async {
completion(.failure(RoomProxyError.failedRetrievingDisplayName))
}
}
}
}
func loadAvatar(_ completion: @escaping (Result<UIImage?, Error>) -> Void) {
DispatchQueue.global(qos: .background).async {
do {
let avatarData = try self.room.avatar()
DispatchQueue.main.async {
completion(.success(UIImage(data: Data(bytes: avatarData, count: avatarData.count))))
}
} catch {
DispatchQueue.main.async {
completion(.failure(RoomProxyError.failedRetrievingAvatar))
}
}
}
}
func startLiveEventListener() {
room.startLiveEventListener()
}
func paginateBackwards(start: UInt, finish: UInt) {
room.paginateBackwards(from: UInt8(start), to: UInt8(finish))
}
// MARK: - Equatable
static func == (lhs: RoomProxy, rhs: RoomProxy) -> Bool {
lhs.id == rhs.id
}
// MARK: - Private
fileprivate func preprendMessages(_ messages: [Message]) {
if lastMessage == nil {
lastMessage = messages.last?.content()
}
callbacks.send(.prependedMessages(messages))
}
fileprivate func appendMessage(_ message: Message) {
lastMessage = message.content()
callbacks.send(.addedMessage(message))
}
}

View File

@@ -1,14 +1,22 @@
//
// RoomModelProtocol.swift
// RoomProxyProtocol.swift
// ElementX
//
// Created by Stefan Ceriu on 17.02.2022.
//
import UIKit
import Combine
import MatrixRustSDK
protocol RoomModelProtocol {
var identifier: String { get }
enum RoomProxyCallback {
case prependedMessages([Message])
case addedMessage(Message)
case updatedLastMessage
}
protocol RoomProxyProtocol {
var id: String { get }
var isDirect: Bool { get }
var isPublic: Bool { get }
var isSpace: Bool { get }
@@ -23,4 +31,9 @@ protocol RoomModelProtocol {
func loadDisplayName(_ completion: @escaping (Result<String, Error>) -> Void)
func loadAvatar(_ completion: @escaping (Result<UIImage?, Error>) -> Void)
func startLiveEventListener()
func paginateBackwards(start: UInt, finish: UInt)
var callbacks: PassthroughSubject<RoomProxyCallback, Never> { get }
}

View File

@@ -0,0 +1,20 @@
//
// MockRoomTimelineController.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import Combine
class MockRoomTimelineController: RoomTimelineControllerProtocol {
let timelineItems: [RoomTimelineItemProtocol] = [TextRoomTimelineItem(id: UUID().uuidString, senderDisplayName: "Anne", text: "You rock!", originServerTs: .now),
TextRoomTimelineItem(id: UUID().uuidString, senderDisplayName: "Bob", text: "You rule!", originServerTs: .now)]
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
func paginateBackwards(_ count: UInt) {
}
}

View File

@@ -0,0 +1,48 @@
//
// RoomTimelineController.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import Combine
import MatrixRustSDK
enum RoomTimelineControllerCallback {
case updatedTimelineItems
}
class RoomTimelineController: RoomTimelineControllerProtocol {
private let timelineProvider: RoomTimelineProvider
private var cancellables = Set<AnyCancellable>()
let callbacks = PassthroughSubject<RoomTimelineControllerCallback, Never>()
private(set) var timelineItems = [RoomTimelineItemProtocol]()
init(timelineProvider: RoomTimelineProvider) {
self.timelineProvider = timelineProvider
self.timelineProvider.callbacks.sink { [weak self] callback in
guard let self = self else { return }
switch callback {
case .updatedMessages:
self.timelineItems = self.timelineProvider.messages.map { message in
let timestamp = Date(timeIntervalSince1970: TimeInterval(message.originServerTs()))
return TextRoomTimelineItem(id: message.id(),
senderDisplayName: message.sender(),
text: message.content(),
originServerTs: timestamp)
}
self.callbacks.send(.updatedTimelineItems)
}
}.store(in: &cancellables)
}
func paginateBackwards(_ count: UInt) {
timelineProvider.paginateBackwards(count)
}
}

View File

@@ -0,0 +1,17 @@
//
// RoomTimelineControllerProtocol.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import Combine
protocol RoomTimelineControllerProtocol {
var timelineItems: [RoomTimelineItemProtocol] { get }
var callbacks: PassthroughSubject<RoomTimelineControllerCallback, Never> { get }
func paginateBackwards(_ count: UInt)
}

View File

@@ -0,0 +1,16 @@
//
// RoomTimelineItemProtocol.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
protocol RoomTimelineItemProtocol {
var id: String { get }
var senderDisplayName: String { get }
var text: String { get }
var originServerTs: Date { get }
}

View File

@@ -0,0 +1,61 @@
//
// RoomTimeline.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
import Combine
import MatrixRustSDK
enum RoomTimelineCallback {
case updatedMessages
}
class RoomTimelineProvider {
private let roomProxy: RoomProxyProtocol
private var cancellables = Set<AnyCancellable>()
private var paginationCounter: UInt = 0
private var isWaitingPaginationResponse = false
let callbacks = PassthroughSubject<RoomTimelineCallback, Never>()
private(set) var messages = [Message]()
init(roomProxy: RoomProxyProtocol) {
self.roomProxy = roomProxy
self.roomProxy.startLiveEventListener()
self.roomProxy.callbacks.sink { [weak self] callback in
guard let self = self else { return }
switch callback {
case .addedMessage(let message):
self.messages.append(message)
case .prependedMessages(let messages):
self.messages.insert(contentsOf: messages.reversed(), at: 0)
self.isWaitingPaginationResponse = false
case .updatedLastMessage:
break
}
self.callbacks.send(.updatedMessages)
}.store(in: &cancellables)
}
func paginateBackwards(_ count: UInt) {
if isWaitingPaginationResponse {
return
}
self.roomProxy.paginateBackwards(start: paginationCounter, finish: count)
isWaitingPaginationResponse = true
// This is not in any way correct but it will do for now.
self.paginationCounter += count
}
}

View File

@@ -0,0 +1,16 @@
//
// TextRoomTimelineItem.swift
// ElementX
//
// Created by Stefan Ceriu on 04.03.2022.
// Copyright © 2022 Element. All rights reserved.
//
import Foundation
struct TextRoomTimelineItem: RoomTimelineItemProtocol {
let id: String
let senderDisplayName: String
let text: String
let originServerTs: Date
}

View File

@@ -5,7 +5,7 @@ if [ ! $# -eq 2 ]; then
exit 1
fi
MODULE_DIR="../ElementX/Sources/Modules"
MODULE_DIR="../ElementX/Sources/Modules/Screens"
OUTPUT_DIR=$MODULE_DIR/$1
SCREEN_NAME=$2
SCREEN_VAR_NAME=`echo $SCREEN_NAME | awk '{ print tolower(substr($0, 1, 1)) substr($0, 2) }'`

View File

@@ -12,6 +12,7 @@ end
lane :ios_adhoc do
build_ios_app(
scheme: "ElementX",
clean: true,
export_method: "ad-hoc",
output_directory: "build",
export_options: {
@@ -25,6 +26,7 @@ end
lane :mac_adhoc do
build_mac_app(
scheme: "ElementX",
clean: true,
export_method: "mac-application",
output_directory: "build"
)