New navigation flow
This commit is contained in:
parent
99c035146c
commit
797076bb15
|
@ -10,21 +10,49 @@ final class AppRouter: ObservableObject {
|
|||
|
||||
@Published var error: ErrorResponse?
|
||||
|
||||
@Published var server: Server? {
|
||||
didSet {
|
||||
if server != oldValue, let server {
|
||||
registerDependencies(in: server)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Published var room: Room?
|
||||
|
||||
@Storage(.currentServer) private var currentURL: URL?
|
||||
|
||||
private func registerDependencies(in server: Server) {
|
||||
Store.register(Database.self, factory: RocketChatDatabase(server: server))
|
||||
Store.register(RocketChatClientProtocol.self, factory: RocketChatClient(server: server))
|
||||
Store.register(MessageSending.self, factory: MessageSender(server: server))
|
||||
Store.register(ErrorActionHandling.self, factory: ErrorActionHandler(server: server))
|
||||
Store.register(MessagesLoading.self, factory: MessagesLoader())
|
||||
Store.register(RoomsLoader.self, factory: RoomsLoader(server: server))
|
||||
}
|
||||
}
|
||||
|
||||
extension AppRouter: AppRouting {
|
||||
func route(to route: Route) {
|
||||
self.route = route
|
||||
|
||||
switch route {
|
||||
case .roomList(let server):
|
||||
currentURL = server.url
|
||||
case .roomList(let selectedServer):
|
||||
currentURL = selectedServer.url
|
||||
room = nil
|
||||
server = selectedServer
|
||||
case .room(let selectedServer, let selectedRoom):
|
||||
currentURL = selectedServer.url
|
||||
server = selectedServer
|
||||
room = selectedRoom
|
||||
case .serverList:
|
||||
currentURL = nil
|
||||
default:
|
||||
break
|
||||
room = nil
|
||||
server = nil
|
||||
case .loading:
|
||||
room = nil
|
||||
server = nil
|
||||
}
|
||||
|
||||
self.route = route
|
||||
}
|
||||
|
||||
func present(error: ErrorResponse) {
|
||||
|
@ -40,4 +68,5 @@ enum Route: Equatable {
|
|||
case loading
|
||||
case serverList
|
||||
case roomList(Server)
|
||||
case room(Server, Room)
|
||||
}
|
||||
|
|
|
@ -12,16 +12,10 @@ struct AppView: View {
|
|||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationCompatibleView {
|
||||
switch router.route {
|
||||
case .loading:
|
||||
ProgressView()
|
||||
case .roomList(let server):
|
||||
LoggedInView(server: server)
|
||||
case .serverList:
|
||||
ServerListView()
|
||||
.environment(\.managedObjectContext, database.viewContext)
|
||||
}
|
||||
NavigationView {
|
||||
ServerListView()
|
||||
.environmentObject(router)
|
||||
.environment(\.managedObjectContext, database.viewContext)
|
||||
}
|
||||
.onAppear {
|
||||
loadRoute()
|
||||
|
|
|
@ -18,6 +18,11 @@ final class Deeplink: Deeplinking {
|
|||
return
|
||||
}
|
||||
|
||||
router.route(to: .roomList(server))
|
||||
guard let room = RocketChatDatabase(server: server).room(id: response.rid) else {
|
||||
return
|
||||
}
|
||||
|
||||
router.route(to: .room(server, room))
|
||||
holder.clear()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
import SwiftUI
|
||||
|
||||
extension Binding where Value == Bool {
|
||||
init<Wrapped>(bindingOptional: Binding<Wrapped?>) {
|
||||
self.init(
|
||||
get: {
|
||||
bindingOptional.wrappedValue != nil
|
||||
},
|
||||
set: { newValue in
|
||||
guard newValue == false else { return }
|
||||
|
||||
/// We only handle `false` booleans to set our optional to `nil`
|
||||
/// as we can't handle `true` for restoring the previous value.
|
||||
bindingOptional.wrappedValue = nil
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension Binding {
|
||||
func mappedToBool<Wrapped>() -> Binding<Bool> where Value == Wrapped? {
|
||||
Binding<Bool>(bindingOptional: self)
|
||||
}
|
||||
}
|
|
@ -42,7 +42,9 @@ extension ServersLoader: ServersLoading {
|
|||
switch result {
|
||||
case .success(let message):
|
||||
for server in message.servers {
|
||||
self.database.process(updatedServer: server)
|
||||
DispatchQueue.main.async {
|
||||
self.database.process(updatedServer: server)
|
||||
}
|
||||
}
|
||||
|
||||
promise(.success(()))
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
import SwiftUI
|
||||
|
||||
struct LazyView<Content: View>: View {
|
||||
private let build: () -> Content
|
||||
|
||||
init(_ build: @autoclosure @escaping () -> Content) {
|
||||
self.build = build
|
||||
}
|
||||
|
||||
var body: Content {
|
||||
build()
|
||||
}
|
||||
}
|
|
@ -1,29 +1,20 @@
|
|||
import SwiftUI
|
||||
|
||||
struct LoggedInView: View {
|
||||
@Dependency private var router: AppRouting
|
||||
@Dependency private var database: Database
|
||||
@Dependency private var roomsLoader: RoomsLoader
|
||||
|
||||
@EnvironmentObject private var router: AppRouter
|
||||
|
||||
private let database: Database
|
||||
private let server: Server
|
||||
|
||||
init(server: Server) {
|
||||
self.server = server
|
||||
self.database = RocketChatDatabase(server: server)
|
||||
|
||||
registerDependencies()
|
||||
}
|
||||
|
||||
private func registerDependencies() {
|
||||
Store.register(Database.self, factory: database)
|
||||
Store.register(RocketChatClientProtocol.self, factory: RocketChatClient(server: server))
|
||||
Store.register(MessageSending.self, factory: MessageSender(server: server))
|
||||
Store.register(ErrorActionHandling.self, factory: ErrorActionHandler(server: server))
|
||||
Store.register(MessagesLoading.self, factory: MessagesLoader())
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
RoomListView(server: server)
|
||||
.environmentObject(RoomsLoader(server: server))
|
||||
RoomListView(server: server, roomsLoader: roomsLoader)
|
||||
.environmentObject(router)
|
||||
.environment(\.managedObjectContext, database.viewContext)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
import SwiftUI
|
||||
|
||||
struct NavigationCompatibleView<Content: View>: View {
|
||||
private let content: () -> Content
|
||||
|
||||
init(@ViewBuilder content: @escaping () -> Content) {
|
||||
self.content = content
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if #available(watchOS 10.0, *) {
|
||||
NavigationStack {
|
||||
content()
|
||||
}
|
||||
} else {
|
||||
NavigationView {
|
||||
content()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
import SwiftUI
|
||||
|
||||
struct NavigationStackModifier<Item, Destination: View>: ViewModifier {
|
||||
let item: Binding<Item?>
|
||||
let destination: (Item) -> Destination
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content.background {
|
||||
NavigationLink(isActive: item.mappedToBool()) {
|
||||
if let item = item.wrappedValue {
|
||||
destination(item)
|
||||
} else {
|
||||
EmptyView()
|
||||
}
|
||||
} label: {
|
||||
EmptyView()
|
||||
}
|
||||
.opacity(0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension View {
|
||||
func navigationDestination<Item, Destination: View>(
|
||||
for binding: Binding<Item?>,
|
||||
@ViewBuilder destination: @escaping (Item) -> Destination
|
||||
) -> some View {
|
||||
self.modifier(NavigationStackModifier(item: binding, destination: destination))
|
||||
}
|
||||
}
|
|
@ -2,11 +2,12 @@ import SwiftUI
|
|||
|
||||
struct RoomListView: View {
|
||||
@Dependency private var database: Database
|
||||
@Dependency private var router: AppRouting
|
||||
|
||||
@EnvironmentObject private var router: AppRouter
|
||||
|
||||
@ObservedObject private var server: Server
|
||||
|
||||
@EnvironmentObject private var roomsLoader: RoomsLoader
|
||||
@StateObject private var roomsLoader: RoomsLoader
|
||||
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
|
@ -14,19 +15,22 @@ struct RoomListView: View {
|
|||
|
||||
@State private var roomID: String?
|
||||
|
||||
init(server: Server) {
|
||||
init(server: Server, roomsLoader: RoomsLoader) {
|
||||
self.server = server
|
||||
_roomsLoader = StateObject(wrappedValue: roomsLoader)
|
||||
_rooms = FetchRequest(fetchRequest: server.roomsRequest)
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
List(rooms, id: \.id) { room in
|
||||
NavigationLink(tag: room.rid, selection: $roomID) {
|
||||
MessageListView(room: room, server: server)
|
||||
.environment(\.managedObjectContext, database.viewContext)
|
||||
} label: {
|
||||
RoomView(viewModel: .init(room: room, server: server))
|
||||
}
|
||||
RoomView(viewModel: .init(room: room, server: server))
|
||||
.onTapGesture {
|
||||
router.route(to: .room(server, room))
|
||||
}
|
||||
}
|
||||
.navigationDestination(for: $router.room) { room in
|
||||
MessageListView(room: room, server: server)
|
||||
.environment(\.managedObjectContext, database.viewContext)
|
||||
}
|
||||
.onAppear {
|
||||
roomsLoader.start()
|
||||
|
@ -45,13 +49,6 @@ struct RoomListView: View {
|
|||
}
|
||||
}
|
||||
.navigationTitle("Rooms")
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .automatic) {
|
||||
Button("Servers") {
|
||||
router.route(to: .serverList)
|
||||
}
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
if roomsLoader.state == .loading {
|
||||
ProgressView()
|
||||
|
|
|
@ -3,15 +3,14 @@ import CoreData
|
|||
import SwiftUI
|
||||
|
||||
struct ServerListView: View {
|
||||
@Dependency private var router: AppRouting
|
||||
@EnvironmentObject private var router: AppRouter
|
||||
|
||||
@Dependency private var serversLoader: ServersLoading
|
||||
|
||||
@State private var state: ViewState = .loading
|
||||
|
||||
@FetchRequest<Server> private var servers: FetchedResults<Server>
|
||||
|
||||
@Environment(\.scenePhase) private var scenePhase
|
||||
|
||||
init() {
|
||||
let fetchRequest = Server.fetchRequest()
|
||||
fetchRequest.sortDescriptors = []
|
||||
|
@ -63,6 +62,10 @@ struct ServerListView: View {
|
|||
Text("Servers").foregroundColor(.red)
|
||||
}
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationDestination(for: $router.server) { server in
|
||||
LoggedInView(server: server)
|
||||
.environmentObject(router)
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .default) {
|
||||
Button {
|
||||
|
|
|
@ -257,10 +257,14 @@
|
|||
1EDFD0FA2B589B8F002FEE5F /* MessagesLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD0F92B589B8F002FEE5F /* MessagesLoader.swift */; };
|
||||
1EDFD1062B58A66E002FEE5F /* CancelBag.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD1052B58A66E002FEE5F /* CancelBag.swift */; };
|
||||
1EDFD1082B58AA77002FEE5F /* RoomsLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EDFD1072B58AA77002FEE5F /* RoomsLoader.swift */; };
|
||||
1EE096F72BACD1E400780078 /* NavigationCompatibleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096F62BACD1E400780078 /* NavigationCompatibleView.swift */; };
|
||||
1EE096F82BACD1E400780078 /* NavigationCompatibleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096F62BACD1E400780078 /* NavigationCompatibleView.swift */; };
|
||||
1EE096FA2BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096F92BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift */; };
|
||||
1EE096FB2BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096F92BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift */; };
|
||||
1EE096FD2BACD58300780078 /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096FC2BACD58300780078 /* LazyView.swift */; };
|
||||
1EE096FE2BACD58300780078 /* LazyView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096FC2BACD58300780078 /* LazyView.swift */; };
|
||||
1EE097002BACD64C00780078 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096FF2BACD64C00780078 /* Binding+Extensions.swift */; };
|
||||
1EE097012BACD64C00780078 /* Binding+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE096FF2BACD64C00780078 /* Binding+Extensions.swift */; };
|
||||
1EE097032BACD66900780078 /* NavigationStackModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE097022BACD66900780078 /* NavigationStackModifier.swift */; };
|
||||
1EE097042BACD66900780078 /* NavigationStackModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EE097022BACD66900780078 /* NavigationStackModifier.swift */; };
|
||||
1EF5FBD1250C109E00614FEA /* Encryption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EF5FBD0250C109E00614FEA /* Encryption.swift */; };
|
||||
1EFEB5982493B6640072EDC0 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1EFEB5972493B6640072EDC0 /* NotificationService.swift */; };
|
||||
1EFEB59C2493B6640072EDC0 /* NotificationService.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1EFEB5952493B6640072EDC0 /* NotificationService.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
|
@ -570,8 +574,10 @@
|
|||
1EDFD0F92B589B8F002FEE5F /* MessagesLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagesLoader.swift; sourceTree = "<group>"; };
|
||||
1EDFD1052B58A66E002FEE5F /* CancelBag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CancelBag.swift; sourceTree = "<group>"; };
|
||||
1EDFD1072B58AA77002FEE5F /* RoomsLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomsLoader.swift; sourceTree = "<group>"; };
|
||||
1EE096F62BACD1E400780078 /* NavigationCompatibleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationCompatibleView.swift; sourceTree = "<group>"; };
|
||||
1EE096F92BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ToolbarItemPlacement+Extensions.swift"; sourceTree = "<group>"; };
|
||||
1EE096FC2BACD58300780078 /* LazyView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyView.swift; sourceTree = "<group>"; };
|
||||
1EE096FF2BACD64C00780078 /* Binding+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Binding+Extensions.swift"; sourceTree = "<group>"; };
|
||||
1EE097022BACD66900780078 /* NavigationStackModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationStackModifier.swift; sourceTree = "<group>"; };
|
||||
1EF5FBD0250C109E00614FEA /* Encryption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Encryption.swift; sourceTree = "<group>"; };
|
||||
1EFEB5952493B6640072EDC0 /* NotificationService.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = NotificationService.appex; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
1EFEB5972493B6640072EDC0 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
|
@ -815,6 +821,7 @@
|
|||
1E29A31C2B5871B60093C03C /* Date+Extensions.swift */,
|
||||
1E675B712BAC49B000438590 /* Color+Extensions.swift */,
|
||||
1EE096F92BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift */,
|
||||
1EE096FF2BACD64C00780078 /* Binding+Extensions.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
|
@ -960,7 +967,8 @@
|
|||
1E638E982B5F0A2900E645E4 /* ChatScrollView.swift */,
|
||||
1E06561C2B7E9C1C0081B01F /* MessageActionView.swift */,
|
||||
1E388AC02B934CD4006FBDB0 /* RemoteImage.swift */,
|
||||
1EE096F62BACD1E400780078 /* NavigationCompatibleView.swift */,
|
||||
1EE096FC2BACD58300780078 /* LazyView.swift */,
|
||||
1EE097022BACD66900780078 /* NavigationStackModifier.swift */,
|
||||
);
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2065,15 +2073,16 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1EE096FD2BACD58300780078 /* LazyView.swift in Sources */,
|
||||
1E29A3242B5874FF0093C03C /* MessageComposerView.swift in Sources */,
|
||||
1EB375892B55DBFB00AEC3D7 /* Server.swift in Sources */,
|
||||
1E06561D2B7E9C1C0081B01F /* MessageActionView.swift in Sources */,
|
||||
1EE096FA2BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift in Sources */,
|
||||
1E29A3162B5868DF0093C03C /* MessageListView.swift in Sources */,
|
||||
1EE096F72BACD1E400780078 /* NavigationCompatibleView.swift in Sources */,
|
||||
1E4AFC172B5AF09C00E2AA7D /* Store.swift in Sources */,
|
||||
1ED1EC8B2B86817100F6620C /* Deeplink.swift in Sources */,
|
||||
1E29A2F42B585B070093C03C /* SubscriptionsResponse.swift in Sources */,
|
||||
1EE097002BACD64C00780078 /* Binding+Extensions.swift in Sources */,
|
||||
1ED1EC892B867E2400F6620C /* ExtensionDelegate.swift in Sources */,
|
||||
1E29A2F92B585B070093C03C /* SubscriptionsRequest.swift in Sources */,
|
||||
1E29A2F22B585B070093C03C /* HistoryResponse.swift in Sources */,
|
||||
|
@ -2094,6 +2103,7 @@
|
|||
1E06561B2B7E91FB0081B01F /* ErrorActionHandler.swift in Sources */,
|
||||
1E29A2FC2B585B070093C03C /* SendMessageRequest.swift in Sources */,
|
||||
1E29A30C2B585D1D0093C03C /* String+Extensions.swift in Sources */,
|
||||
1EE097032BACD66900780078 /* NavigationStackModifier.swift in Sources */,
|
||||
1ED033CD2B55D671004F4930 /* RocketChatDatabase.swift in Sources */,
|
||||
1E29A3122B5866090093C03C /* Room.swift in Sources */,
|
||||
1E29A3032B585B070093C03C /* FailableDecodable.swift in Sources */,
|
||||
|
@ -2147,15 +2157,16 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
1EE096FE2BACD58300780078 /* LazyView.swift in Sources */,
|
||||
1ED1EC902B86997F00F6620C /* MessageComposerView.swift in Sources */,
|
||||
1ED1EC912B86997F00F6620C /* Server.swift in Sources */,
|
||||
1ED1EC922B86997F00F6620C /* MessageActionView.swift in Sources */,
|
||||
1EE096FB2BACD1F200780078 /* ToolbarItemPlacement+Extensions.swift in Sources */,
|
||||
1ED1EC932B86997F00F6620C /* MessageListView.swift in Sources */,
|
||||
1EE096F82BACD1E400780078 /* NavigationCompatibleView.swift in Sources */,
|
||||
1ED1EC942B86997F00F6620C /* Store.swift in Sources */,
|
||||
1ED1EC952B86997F00F6620C /* Deeplink.swift in Sources */,
|
||||
1ED1EC962B86997F00F6620C /* SubscriptionsResponse.swift in Sources */,
|
||||
1EE097012BACD64C00780078 /* Binding+Extensions.swift in Sources */,
|
||||
1ED1EC972B86997F00F6620C /* ExtensionDelegate.swift in Sources */,
|
||||
1ED1EC982B86997F00F6620C /* SubscriptionsRequest.swift in Sources */,
|
||||
1ED1EC992B86997F00F6620C /* HistoryResponse.swift in Sources */,
|
||||
|
@ -2176,6 +2187,7 @@
|
|||
1ED1ECA82B86997F00F6620C /* ErrorActionHandler.swift in Sources */,
|
||||
1ED1ECA92B86997F00F6620C /* SendMessageRequest.swift in Sources */,
|
||||
1ED1ECAA2B86997F00F6620C /* String+Extensions.swift in Sources */,
|
||||
1EE097042BACD66900780078 /* NavigationStackModifier.swift in Sources */,
|
||||
1ED1ECAB2B86997F00F6620C /* RocketChatDatabase.swift in Sources */,
|
||||
1ED1ECAC2B86997F00F6620C /* Room.swift in Sources */,
|
||||
1ED1ECAD2B86997F00F6620C /* FailableDecodable.swift in Sources */,
|
||||
|
|
Loading…
Reference in New Issue