eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1import SwiftUI
2import 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).
8struct 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 @State private var showSignIn = false
17 /// The key/access step's "Sign in" chip presents the ordinary doors β€”
18 /// its own model, so a sign-in taken here behaves exactly like one taken
19 /// on the first-run screen (same adopt seam, same last-used marker).
20 @StateObject private var signIn = HomeSignInModel()
21 @ScaledMetric(relativeTo: .body) private var chatFontSize: CGFloat = 17
22 @AppStorage("nativeTheme") private var themeName = "classic"
23
24 var body: some View {
25 Group {
26 if let model {
27 ElevenChatView(model: model, theme: .named(themeName))
28 .environment(\.chatFontSize, chatFontSize)
29 } else {
30 ProgressView()
31 }
32 }
33 .navigationTitle(thread.title)
34 .navigationBarTitleDisplayMode(.inline)
35 .sheet(isPresented: $showDiscover) {
36 // The community directory (shared view; web: showDiscover) β€” the
37 // welcome finish's "Browse open groups" chip lands here.
38 NavigationStack {
39 DiscoverGroupsView(store: store) { id in
40 showDiscover = false
41 path.append(.thread(id))
42 }
43 }
44 }
45 .sheet(isPresented: $showQuickForm) {
46 // "Fill in a form instead" β€” the whole conversation's questions at
47 // once; submitting fast-fills the flow's records and it resumes.
48 if let model {
49 ElevenQuickFormView(model: model)
50 }
51 }
52 .sheet(isPresented: $showSignIn) {
53 // iOS 18 gates the ceremony (PRF); below it the chip is never
54 // offered at all β€” signInToEleven stays nil, so the flow records
55 // where the key actually lives instead of pitching.
56 if #available(iOS 18.0, *) {
57 NavigationStack { SignInFirstRun(model: signIn) }
58 }
59 }
60 // The doors don't dismiss themselves (first-run swaps the whole
61 // screen instead); close on a landed sign-in, and the conversation's
62 // settle picks the session up from there.
63 .onChange(of: signIn.phase) { _, phase in
64 if phase.signedIn { showSignIn = false }
65 }
66 .onAppear {
67 guard model == nil else { return }
68 let m = ElevenConversationCache.shared.model(for: instance, thread: thread,
69 myUserId: store.myUserId)
70 m?.openQuickForm = { showQuickForm = true }
71 m?.openThread = { id in path.append(.thread(id)) }
72 m?.openAdmin = {
73 Task { @MainActor in
74 if let id = await store.openOrCreateAppThread(app: "admin", title: "Admin") {
75 path.append(.thread(id))
76 }
77 }
78 }
79 m?.openDiscover = { showDiscover = true }
80 if #available(iOS 18.0, *) {
81 m?.signInToEleven = { showSignIn = true }
82 }
83 // Outgoing payloads stamp the SavedInstance's name β€” persist a
84 // flow rename, the way ServerSettingsSheet persists a manual one.
85 m?.onNameSet = { name in
86 var instances = InstancesStore.load()
87 if let i = instances.firstIndex(where: { $0.id == instance.id }) {
88 instances[i].screenName = name
89 InstancesStore.save(instances)
90 }
91 }
92 m?.start(roster: store.roster, threads: store.threads)
93 model = m
94 }
95 }
96}
97