@@ -11,6 +11,7 @@ import TableProModels
1111struct ConnectedView : View {
1212 @Environment ( AppState . self) private var appState
1313 @Environment ( \. scenePhase) private var scenePhase
14+ @Environment ( \. horizontalSizeClass) private var sizeClass
1415 let connection : DatabaseConnection
1516
1617 private static let logger = Logger ( subsystem: " com.TablePro " , category: " ConnectedView " )
@@ -29,6 +30,7 @@ struct ConnectedView: View {
2930 @State private var activeDatabase : String = " "
3031 @State private var schemas : [ String ] = [ ]
3132 @State private var activeSchema : String = " public "
33+ @State private var selectedTable : TableInfo ?
3234 @State private var isSwitching = false
3335 @State private var isReconnecting = false
3436 @State private var hapticSuccess = false
@@ -129,16 +131,12 @@ struct ConnectedView: View {
129131 } message: {
130132 Text ( failureAlertMessage ?? " " )
131133 }
132- . navigationTitle ( supportsDatabaseSwitching && databases. count > 1 ? " " : displayName)
134+ . navigationTitle ( sizeClass != . regular ? ( supportsDatabaseSwitching && databases. count > 1 ? " " : displayName) : " " )
133135 . navigationBarTitleDisplayMode ( . inline)
134136 . safeAreaInset ( edge: . top) {
135- Picker ( " Tab " , selection: selectedTabBinding) {
136- Text ( " Tables " ) . tag ( ConnectedTab . tables)
137- Text ( " Query " ) . tag ( ConnectedTab . query)
137+ if sizeClass != . regular {
138+ tabPicker
138139 }
139- . pickerStyle ( . segmented)
140- . padding ( . horizontal)
141- . padding ( . vertical, 8 )
142140 }
143141 . background {
144142 Button ( " " ) { selectedTabRaw = ConnectedTab . tables. rawValue }
@@ -149,63 +147,23 @@ struct ConnectedView: View {
149147 . hidden ( )
150148 }
151149 . toolbar {
152- if connection. safeModeLevel != . off {
153- ToolbarItem ( placement: . topBarTrailing) {
154- Image ( systemName: connection. safeModeLevel == . readOnly ? " lock.fill " : " shield.fill " )
155- . foregroundStyle ( connection. safeModeLevel == . readOnly ? . red : . orange)
156- . font ( . caption)
150+ if sizeClass != . regular {
151+ if connection. safeModeLevel != . off {
152+ ToolbarItem ( placement: . topBarTrailing) {
153+ Image ( systemName: connection. safeModeLevel == . readOnly ? " lock.fill " : " shield.fill " )
154+ . foregroundStyle ( connection. safeModeLevel == . readOnly ? . red : . orange)
155+ . font ( . caption)
156+ }
157157 }
158- }
159- if supportsDatabaseSwitching && databases. count > 1 {
160- ToolbarItem ( placement: . topBarLeading) {
161- Menu {
162- ForEach ( databases, id: \. self) { db in
163- Button {
164- Task { await switchDatabase ( to: db) }
165- } label: {
166- if db == activeDatabase {
167- Label ( db, systemImage: " checkmark " )
168- } else {
169- Text ( db)
170- }
171- }
172- }
173- } label: {
174- HStack ( spacing: 4 ) {
175- Text ( activeDatabase)
176- . font ( . subheadline)
177- if isSwitching {
178- ProgressView ( )
179- . controlSize ( . mini)
180- } else {
181- Image ( systemName: " chevron.down " )
182- . font ( . caption2)
183- . foregroundStyle ( . secondary)
184- }
185- }
158+ if supportsDatabaseSwitching && databases. count > 1 {
159+ ToolbarItem ( placement: . topBarLeading) {
160+ databaseSwitcherMenu
186161 }
187- . disabled ( isSwitching)
188162 }
189- }
190- if supportsSchemas && schemas. count > 1 && selectedTab == . tables {
191- ToolbarItem ( placement: . topBarTrailing) {
192- Menu {
193- ForEach ( schemas, id: \. self) { schema in
194- Button {
195- Task { await switchSchema ( to: schema) }
196- } label: {
197- if schema == activeSchema {
198- Label ( schema, systemImage: " checkmark " )
199- } else {
200- Text ( schema)
201- }
202- }
203- }
204- } label: {
205- Label ( activeSchema, systemImage: " square.3.layers.3d " )
206- . font ( . subheadline)
163+ if supportsSchemas && schemas. count > 1 && selectedTab == . tables {
164+ ToolbarItem ( placement: . topBarTrailing) {
165+ schemaSwitcherMenu
207166 }
208- . disabled ( isSwitching)
209167 }
210168 }
211169 }
@@ -219,6 +177,9 @@ struct ConnectedView: View {
219177 let key = connection. id. uuidString
220178 activeDatabase = UserDefaults . standard. string ( forKey: " lastDB. \( key) " ) ?? " "
221179 activeSchema = UserDefaults . standard. string ( forKey: " lastSchema. \( key) " ) ?? " public "
180+ if let savedTable = UserDefaults . standard. string ( forKey: " lastTable. \( key) " ) {
181+ selectedTable = tables. first { $0. name == savedTable }
182+ }
222183
223184 let hasDriver = appState. connectionManager. session ( for: connection. id) ? . driver != nil
224185 if !hasDriver, !isConnecting, appError == nil {
@@ -232,14 +193,90 @@ struct ConnectedView: View {
232193 . onChange ( of: activeSchema) { _, newValue in
233194 UserDefaults . standard. set ( newValue, forKey: " lastSchema. \( connection. id. uuidString) " )
234195 }
196+ . onChange ( of: selectedTable) { _, newValue in
197+ UserDefaults . standard. set ( newValue? . name, forKey: " lastTable. \( connection. id. uuidString) " )
198+ }
235199 . onChange ( of: scenePhase) { _, phase in
236200 if phase == . active, session != nil {
237201 Task { await reconnectIfNeeded ( ) }
238202 }
239203 }
240204 }
241205
206+ @ViewBuilder
242207 private var connectedContent : some View {
208+ if sizeClass == . regular {
209+ iPadContent
210+ } else {
211+ iPhoneContent
212+ }
213+ }
214+
215+ private var iPadContent : some View {
216+ NavigationSplitView {
217+ TableListView (
218+ connection: connection,
219+ tables: tables,
220+ session: session,
221+ selectedTable: $selectedTable,
222+ onRefresh: { await refreshTables ( ) }
223+ )
224+ . navigationTitle ( displayName)
225+ . navigationBarTitleDisplayMode ( . inline)
226+ . toolbar {
227+ if connection. safeModeLevel != . off {
228+ ToolbarItem ( placement: . topBarTrailing) {
229+ Image ( systemName: connection. safeModeLevel == . readOnly ? " lock.fill " : " shield.fill " )
230+ . foregroundStyle ( connection. safeModeLevel == . readOnly ? . red : . orange)
231+ . font ( . caption)
232+ }
233+ }
234+ if supportsDatabaseSwitching && databases. count > 1 {
235+ ToolbarItem ( placement: . topBarLeading) {
236+ databaseSwitcherMenu
237+ }
238+ }
239+ if supportsSchemas && schemas. count > 1 && selectedTab == . tables {
240+ ToolbarItem ( placement: . topBarTrailing) {
241+ schemaSwitcherMenu
242+ }
243+ }
244+ }
245+ } detail: {
246+ NavigationStack {
247+ switch selectedTab {
248+ case . tables:
249+ if let table = selectedTable {
250+ DataBrowserView ( connection: connection, table: table, session: session)
251+ . id ( table. id)
252+ } else {
253+ ContentUnavailableView (
254+ " Select a Table " ,
255+ systemImage: " tablecells " ,
256+ description: Text ( " Choose a table from the sidebar. " )
257+ )
258+ }
259+ case . query:
260+ QueryEditorView (
261+ session: session,
262+ tables: tables,
263+ databaseType: connection. type,
264+ safeModeLevel: connection. safeModeLevel,
265+ queryHistory: $queryHistory,
266+ connectionId: connection. id,
267+ historyStorage: historyStorage
268+ )
269+ }
270+ }
271+ }
272+ . navigationSplitViewStyle ( . balanced)
273+ . safeAreaInset ( edge: . bottom) {
274+ tabPicker
275+ . background ( . bar)
276+ }
277+ }
278+
279+ private var iPhoneContent : some View {
243280 VStack ( spacing: 0 ) {
244281 switch selectedTab {
245282 case . tables:
@@ -263,6 +300,66 @@ struct ConnectedView: View {
263300 }
264301 }
265302
303+ private var tabPicker : some View {
304+ Picker ( " Tab " , selection: selectedTabBinding) {
305+ Text ( " Tables " ) . tag ( ConnectedTab . tables)
306+ Text ( " Query " ) . tag ( ConnectedTab . query)
307+ }
308+ . pickerStyle ( . segmented)
309+ . padding ( . horizontal)
310+ . padding ( . vertical, 8 )
311+ }
312+
313+ private var databaseSwitcherMenu : some View {
314+ Menu {
315+ ForEach ( databases, id: \. self) { db in
316+ Button {
317+ Task { await switchDatabase ( to: db) }
318+ } label: {
319+ if db == activeDatabase {
320+ Label ( db, systemImage: " checkmark " )
321+ } else {
322+ Text ( db)
323+ }
324+ }
325+ }
326+ } label: {
327+ HStack ( spacing: 4 ) {
328+ Text ( activeDatabase)
329+ . font ( . subheadline)
330+ if isSwitching {
331+ ProgressView ( )
332+ . controlSize ( . mini)
333+ } else {
334+ Image ( systemName: " chevron.down " )
335+ . font ( . caption2)
336+ . foregroundStyle ( . secondary)
337+ }
338+ }
339+ }
340+ . disabled ( isSwitching)
341+ }
342+
343+ private var schemaSwitcherMenu : some View {
344+ Menu {
345+ ForEach ( schemas, id: \. self) { schema in
346+ Button {
347+ Task { await switchSchema ( to: schema) }
348+ } label: {
349+ if schema == activeSchema {
350+ Label ( schema, systemImage: " checkmark " )
351+ } else {
352+ Text ( schema)
353+ }
354+ }
355+ }
356+ } label: {
357+ Label ( activeSchema, systemImage: " square.3.layers.3d " )
358+ . font ( . subheadline)
359+ }
360+ . disabled ( isSwitching)
361+ }
362+
266363 private func connect( ) async {
267364 guard !isConnectInProgress else { return }
268365 guard session == nil else {
0 commit comments