| 1 | import SwiftUI |
| 2 | import LChatCore |
| 3 | |
| 4 | // 🤖 iOS host for Eleven's native onboarding conversation (setup / welcome): |
| 5 | // owns the model's lifecycle and wires the finish chips' thread hop into the |
| 6 | // navigation path. Models are cached per thread so leaving and returning |
| 7 | // resumes the same run (the flow itself also resumes from recorded state). |
| 8 | struct ElevenChatScreen: View { |
| 9 | let instance: SavedInstance |
| 10 | let thread: ChatThread |
| 11 | @ObservedObject var store: ThreadStore |
| 12 | @Binding var path: [Route] |
| 13 | @State private var model: ElevenConversationModel? |
| 14 | @State private var showDiscover = false |
| 15 | @State private var showQuickForm = false |
| 16 | @ScaledMetric(relativeTo: .body) private var chatFontSize: CGFloat = 17 |
| 17 | @AppStorage("nativeTheme") private var themeName = "classic" |
| 18 | |
| 19 | var body: some View { |
| 20 | Group { |
| 21 | if let model { |
| 22 | ElevenChatView(model: model, theme: .named(themeName)) |
| 23 | .environment(\.chatFontSize, chatFontSize) |
| 24 | } else { |
| 25 | ProgressView() |
| 26 | } |
| 27 | } |
| 28 | .navigationTitle(thread.title) |
| 29 | .navigationBarTitleDisplayMode(.inline) |
| 30 | .sheet(isPresented: $showDiscover) { |
| 31 | // The community directory (shared view; web: showDiscover) — the |
| 32 | // welcome finish's "Browse open groups" chip lands here. |
| 33 | NavigationStack { |
| 34 | DiscoverGroupsView(store: store) { id in |
| 35 | showDiscover = false |
| 36 | path.append(.thread(id)) |
| 37 | } |
| 38 | } |
| 39 | } |
| 40 | .sheet(isPresented: $showQuickForm) { |
| 41 | // "Fill in a form instead" — the whole conversation's questions at |
| 42 | // once; submitting fast-fills the flow's records and it resumes. |
| 43 | if let model { |
| 44 | ElevenQuickFormView(model: model) |
| 45 | } |
| 46 | } |
| 47 | .onAppear { |
| 48 | guard model == nil else { return } |
| 49 | let m = ElevenConversationCache.shared.model(for: instance, thread: thread, |
| 50 | myUserId: store.myUserId) |
| 51 | m?.openQuickForm = { showQuickForm = true } |
| 52 | m?.openThread = { id in path.append(.thread(id)) } |
| 53 | m?.openAdmin = { |
| 54 | Task { @MainActor in |
| 55 | if let id = await store.openOrCreateAppThread(app: "admin", title: "Admin") { |
| 56 | path.append(.thread(id)) |
| 57 | } |
| 58 | } |
| 59 | } |
| 60 | m?.openDiscover = { showDiscover = true } |
| 61 | // Outgoing payloads stamp the SavedInstance's name — persist a |
| 62 | // flow rename, the way ServerSettingsSheet persists a manual one. |
| 63 | m?.onNameSet = { name in |
| 64 | var instances = InstancesStore.load() |
| 65 | if let i = instances.firstIndex(where: { $0.id == instance.id }) { |
| 66 | instances[i].screenName = name |
| 67 | InstancesStore.save(instances) |
| 68 | } |
| 69 | } |
| 70 | m?.start(roster: store.roster, threads: store.threads) |
| 71 | model = m |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | |