This commit is contained in:
fmodf 2024-10-15 13:39:23 +02:00
parent c426847700
commit acf2807056
10 changed files with 77 additions and 22 deletions

View file

@ -100,12 +100,6 @@ extension Client {
_ = try await connection.module(.roster).removeItem(jid: JID(roster.contactBareJid)) _ = try await connection.module(.roster).removeItem(jid: JID(roster.contactBareJid))
} }
func setActive(_ active: Bool) {
Task {
try? await credentials.setActive(flag: active)
}
}
func connect() async { func connect() async {
guard credentials.isActive, state == .enabled(.disconnected) else { guard credentials.isActive, state == .enabled(.disconnected) else {
return return

View file

@ -5,7 +5,7 @@ import Photos
import SwiftUI import SwiftUI
@MainActor @MainActor
final class SettingsStore: ObservableObject { final class ChatSettingsStore: ObservableObject {
@Published var chat: Chat? @Published var chat: Chat?
private let client: Client private let client: Client
@ -21,7 +21,7 @@ final class SettingsStore: ObservableObject {
} }
} }
extension SettingsStore { extension ChatSettingsStore {
func setSecured(_ secured: Bool) { func setSecured(_ secured: Bool) {
Task { Task {
try? await chat?.setEncrypted(secured) try? await chat?.setEncrypted(secured)
@ -30,7 +30,7 @@ extension SettingsStore {
} }
// MARK: - Processing attachments // MARK: - Processing attachments
private extension SettingsStore { private extension ChatSettingsStore {
func subscribe() { func subscribe() {
chatCancellable = ValueObservation.tracking(Chat chatCancellable = ValueObservation.tracking(Chat
.filter(Column("account") == roster.bareJid && Column("participant") == roster.contactBareJid) .filter(Column("account") == roster.bareJid && Column("participant") == roster.contactBareJid)

View file

@ -131,7 +131,7 @@ extension ClientsStore {
// MARK: - Produce stores for conversation // MARK: - Produce stores for conversation
extension ClientsStore { extension ClientsStore {
// swiftlint:disable:next large_tuple // swiftlint:disable:next large_tuple
func conversationStores(for roster: Roster) async throws -> (MessagesStore, AttachmentsStore, SettingsStore) { func conversationStores(for roster: Roster) async throws -> (MessagesStore, AttachmentsStore, ChatSettingsStore) {
while !ready { while !ready {
await Task.yield() await Task.yield()
} }
@ -142,12 +142,12 @@ extension ClientsStore {
let conversationStore = MessagesStore(roster: roster, client: client) let conversationStore = MessagesStore(roster: roster, client: client)
let attachmentsStore = AttachmentsStore(roster: roster, client: client) let attachmentsStore = AttachmentsStore(roster: roster, client: client)
let settingsStore = SettingsStore(roster: roster, client: client) let settingsStore = ChatSettingsStore(roster: roster, client: client)
return (conversationStore, attachmentsStore, settingsStore) return (conversationStore, attachmentsStore, settingsStore)
} }
// swiftlint:disable:next large_tuple // swiftlint:disable:next large_tuple
func conversationStores(for chat: Chat) async throws -> (MessagesStore, AttachmentsStore, SettingsStore) { func conversationStores(for chat: Chat) async throws -> (MessagesStore, AttachmentsStore, ChatSettingsStore) {
while !ready { while !ready {
await Task.yield() await Task.yield()
} }
@ -159,7 +159,7 @@ extension ClientsStore {
let roster = try await chat.fetchRoster() let roster = try await chat.fetchRoster()
let conversationStore = MessagesStore(roster: roster, client: client) let conversationStore = MessagesStore(roster: roster, client: client)
let attachmentsStore = AttachmentsStore(roster: roster, client: client) let attachmentsStore = AttachmentsStore(roster: roster, client: client)
let settingsStore = SettingsStore(roster: roster, client: client) let settingsStore = ChatSettingsStore(roster: roster, client: client)
return (conversationStore, attachmentsStore, settingsStore) return (conversationStore, attachmentsStore, settingsStore)
} }
} }

View file

@ -0,0 +1,32 @@
import Combine
import Foundation
import GRDB
import Photos
import SwiftUI
@MainActor
final class GlobalSettingsStore: ObservableObject {
static let shared = GlobalSettingsStore()
@Published var credentials: [Credentials] = []
private var credentialsCancellable: AnyCancellable?
init() {
subscribe()
}
}
private extension GlobalSettingsStore {
func subscribe() {
credentialsCancellable = ValueObservation.tracking(Credentials
.fetchAll
)
.publisher(in: Database.shared.dbQueue, scheduling: .immediate)
.receive(on: DispatchQueue.main)
.sink { _ in
} receiveValue: { [weak self] credentials in
self?.credentials = credentials
}
}
}

View file

@ -5,6 +5,7 @@ import SwiftUI
@MainActor @MainActor
struct ConversationsClassic: App { struct ConversationsClassic: App {
private let clientsStore = ClientsStore.shared private let clientsStore = ClientsStore.shared
private let globalSettingsStore = GlobalSettingsStore.shared
init() { init() {
// There's a bug on iOS 17 where sheet may not load with large title, even if modifiers are set, which causes some tests to fail // There's a bug on iOS 17 where sheet may not load with large title, even if modifiers are set, which causes some tests to fail
@ -16,6 +17,7 @@ struct ConversationsClassic: App {
WindowGroup { WindowGroup {
RootView() RootView()
.environmentObject(clientsStore) .environmentObject(clientsStore)
.environmentObject(globalSettingsStore)
} }
} }
} }

View file

@ -1,6 +1,7 @@
import SwiftUI import SwiftUI
struct WelcomeScreen: View { struct WelcomeScreen: View {
@EnvironmentObject var globalSettingsStore: GlobalSettingsStore
@Environment(\.router) var router @Environment(\.router) var router
var body: some View { var body: some View {
@ -9,6 +10,23 @@ struct WelcomeScreen: View {
Color.Material.Background.light Color.Material.Background.light
.ignoresSafeArea() .ignoresSafeArea()
if !globalSettingsStore.credentials.isEmpty && globalSettingsStore.credentials.allSatisfy({ !$0.isActive }) {
VStack {
HStack {
Spacer()
Image(systemName: "gear")
.foregroundColor(.Material.Elements.active)
.tappablePadding(.symmetric(10)) {
router.showScreen(.push) { _ in
SettingsScreen()
}
}
}
.padding()
Spacer()
}
}
// content // content
VStack(spacing: 32) { VStack(spacing: 32) {
// icon // icon

View file

@ -7,7 +7,7 @@ struct ConversationScreen: View {
@Environment(\.router) var router @Environment(\.router) var router
@StateObject var messagesStore: MessagesStore @StateObject var messagesStore: MessagesStore
@StateObject var attachments: AttachmentsStore @StateObject var attachments: AttachmentsStore
@StateObject var settings: SettingsStore @StateObject var settings: ChatSettingsStore
@State private var autoScroll = true @State private var autoScroll = true
@State private var firstIsVisible = true @State private var firstIsVisible = true

View file

@ -5,7 +5,7 @@ import SwiftUI
struct ConversationSettingsScreen: View { struct ConversationSettingsScreen: View {
@Environment(\.router) var router @Environment(\.router) var router
@EnvironmentObject var settingsStore: SettingsStore @EnvironmentObject var settingsStore: ChatSettingsStore
var body: some View { var body: some View {
ZStack { ZStack {

View file

@ -2,6 +2,7 @@ import SwiftUI
struct SettingsScreen: View { struct SettingsScreen: View {
@EnvironmentObject var clientsStore: ClientsStore @EnvironmentObject var clientsStore: ClientsStore
@EnvironmentObject var settingsStore: GlobalSettingsStore
@Environment(\.router) var router @Environment(\.router) var router
var body: some View { var body: some View {
@ -22,19 +23,21 @@ struct SettingsScreen: View {
// Accounts section // Accounts section
SharedSectionTitle(text: L10n.Settings.Section.Accounts.title) SharedSectionTitle(text: L10n.Settings.Section.Accounts.title)
ForEach(clientsStore.clients) { client in ForEach(settingsStore.credentials) { creds in
SharedListRow( SharedListRow(
iconType: .charCircle(client.credentials.bareJid), iconType: .charCircle(creds.bareJid),
text: client.credentials.bareJid, text: creds.bareJid,
controlType: .switcher(isOn: Binding( controlType: .switcher(isOn: Binding(
get: { client.credentials.isActive }, get: { creds.isActive },
set: { new in set: { new in
client.setActive(new) Task {
try? await creds.setActive(flag: new)
}
} }
)) ))
) )
.onTapGesture { .onTapGesture {
print("Tapped account \(client.credentials.bareJid)") print("Tapped account \(creds.bareJid)")
} }
} }
@ -83,6 +86,11 @@ struct SettingsScreen: View {
Spacer() Spacer()
} }
} }
// .onDisappear {
// if settingsStore.credentials.isEmpty || settingsStore.credentials.allSatisfy({ !$0.isActive }) {
// router.dismissScreenStack()
// }
// }
} }
@ViewBuilder private var addAccountSelector: some View { @ViewBuilder private var addAccountSelector: some View {

View file

@ -3,11 +3,12 @@ import SwiftUI
struct RootView: View { struct RootView: View {
@EnvironmentObject var clientsStore: ClientsStore @EnvironmentObject var clientsStore: ClientsStore
@EnvironmentObject var globalSettingsStore: GlobalSettingsStore
var body: some View { var body: some View {
Group { Group {
if clientsStore.ready { if clientsStore.ready {
if clientsStore.clients.isEmpty || clientsStore.clients.allSatisfy({ !$0.credentials.isActive }) { if clientsStore.clients.isEmpty || globalSettingsStore.credentials.allSatisfy({ !$0.isActive }) {
RouterView { _ in RouterView { _ in
WelcomeScreen() WelcomeScreen()
} }