Skip to content

Commit fa2cca7

Browse files
grichaclaude
andcommitted
Add mobile app feature parity with web UI
- Add workspace detail screen with sessions and settings tabs - Add session detail screen with full conversation view - Add setup screen for first-time server configuration - Add global sessions list with date grouping and agent filtering - Add stats bar to workspaces screen (total/running/stopped) - Add sync all workspaces button to settings - Add stack navigation for drill-down screens - Update Maestro tests for new UI structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 628a096 commit fa2cca7

20 files changed

+1942
-953
lines changed

mobile/.maestro/config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ flows:
33
- navigation.yaml
44
- settings.yaml
55
- workspace-actions.yaml
6+
- workspace-detail.yaml
67
- session-filter.yaml

mobile/.maestro/navigation.yaml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,15 @@ appId: com.subroutine.workspace
2323

2424
- runFlow:
2525
when:
26-
visible: "Filter by agent:"
26+
visible: "All"
2727
commands:
28-
- assertVisible: "All Agents"
28+
- assertVisible: "Claude"
29+
- assertVisible: "OpenCode"
2930

3031
# Navigate to Settings tab
3132
- tapOn: "Settings"
33+
- assertVisible: "Settings"
3234
- assertVisible: "Connection"
33-
- assertVisible: "Coding Agents"
3435

3536
# Navigate back to Workspaces tab
3637
- tapOn: "Workspaces"

mobile/.maestro/session-filter.yaml

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
appId: com.subroutine.workspace
22
---
33
# Test: Session Screen and Filter
4-
# Verifies sessions screen loads and filter works when connected
4+
# Verifies sessions screen loads and filter chips work when connected
55

66
- launchApp:
77
clearState: true
@@ -20,17 +20,25 @@ appId: com.subroutine.workspace
2020
commands:
2121
- assertVisible: "Retry"
2222

23-
# Test filter when connected
23+
# Test filter chips when connected
2424
- runFlow:
2525
when:
26-
visible: "Filter by agent:"
26+
visible: "All"
2727
commands:
28-
- assertVisible: "All Agents"
29-
- tapOn: "All Agents"
30-
- assertVisible: "Claude Code"
28+
- assertVisible: "Claude"
3129
- assertVisible: "OpenCode"
32-
- tapOn: "Claude Code"
33-
- assertVisible: "Claude Code"
30+
- assertVisible: "Codex"
31+
# Test selecting Claude filter
32+
- tapOn: "Claude"
33+
# Test selecting All filter
34+
- tapOn: "All"
35+
36+
# Handle empty state
37+
- runFlow:
38+
when:
39+
visible: "No sessions found"
40+
commands:
41+
- assertVisible: "Start a coding agent in a workspace"
3442

3543
# Navigate back to verify everything still works
3644
- tapOn: "Workspaces"

mobile/.maestro/settings.yaml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,15 @@ appId: com.subroutine.workspace
1313

1414
- tapOn: "Settings"
1515

16-
# Connection section (always visible at top)
16+
# Settings header and Connection section (always visible at top)
17+
- assertVisible: "Settings"
1718
- assertVisible: "Connection"
18-
- assertVisible: "Server URL"
19+
- assertVisible: "Agent Server"
20+
- assertVisible: "Hostname"
21+
22+
# Sync section
23+
- assertVisible: "Sync"
24+
- assertVisible: "Sync All Workspaces"
1925

2026
# Coding Agents section header
2127
- assertVisible: "Coding Agents"
@@ -41,7 +47,13 @@ appId: com.subroutine.workspace
4147
direction: UP
4248
duration: 300
4349

44-
# Scripts and About sections
50+
# File Mappings and Scripts sections
51+
- assertVisible: "File Mappings"
52+
53+
- swipe:
54+
direction: UP
55+
duration: 300
56+
4557
- assertVisible: "Scripts"
4658

4759
- swipe:

mobile/.maestro/workspace-actions.yaml

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,23 @@ appId: com.subroutine.workspace
1818
commands:
1919
- assertVisible: "Tap + to create one"
2020

21-
# If workspaces exist, test the start/stop buttons
21+
# If workspaces exist, test navigating to detail screen
2222
- runFlow:
2323
when:
24-
visible:
25-
text: "SSH:"
24+
visible: "Details"
2625
commands:
27-
# Tap on the workspace card to open detail modal
28-
- tapOn:
29-
text: "SSH:"
30-
# Should see workspace detail modal with logs
31-
- assertVisible:
32-
text: "Logs"
33-
optional: true
34-
# Close modal if open
26+
# Tap on the Details button to navigate to workspace detail
27+
- tapOn: "Details"
28+
# Should see workspace detail screen with Sessions and Settings tabs
29+
- assertVisible: "Sessions"
30+
- assertVisible: "Settings"
31+
# Test switching to Settings tab
32+
- tapOn: "Settings"
33+
# Should see settings options
34+
- assertVisible: "Workspace Info"
35+
# Go back to workspaces list
3536
- tapOn:
36-
text: "Close"
37+
text: ".*←.*"
3738
optional: true
3839

3940
# Test start button if workspace is stopped
@@ -46,7 +47,7 @@ appId: com.subroutine.workspace
4647
# Wait a moment for the action to register
4748
- extendedWaitUntil:
4849
visible:
49-
text: "running|Starting"
50+
text: "running|Stop"
5051
timeout: 5000
5152

5253
# Test stop button if workspace is running
@@ -59,7 +60,7 @@ appId: com.subroutine.workspace
5960
# Wait a moment for the action to register
6061
- extendedWaitUntil:
6162
visible:
62-
text: "stopped|Stopping"
63+
text: "stopped|Start"
6364
timeout: 5000
6465

6566
# Test recover button if workspace is in error state
@@ -69,8 +70,8 @@ appId: com.subroutine.workspace
6970
text: "Recover"
7071
commands:
7172
- tapOn: "Recover"
72-
# Verify recovery confirmation dialog appears
73-
- assertVisible: "Recover Workspace"
74-
- assertVisible: "Attempt to recover"
75-
# Cancel the recovery for now
76-
- tapOn: "Cancel"
73+
# Wait for recovery to attempt
74+
- extendedWaitUntil:
75+
visible:
76+
text: "running|Start"
77+
timeout: 5000
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
appId: com.subroutine.workspace
2+
---
3+
# Test: Workspace Detail Screen Navigation
4+
# Verifies workspace detail screen displays correctly with tabs
5+
6+
- launchApp:
7+
clearState: true
8+
9+
# Wait for app to fully load
10+
- extendedWaitUntil:
11+
visible: "Workspaces"
12+
timeout: 10000
13+
14+
# If no workspaces exist, test gracefully exits
15+
- runFlow:
16+
when:
17+
visible: "No workspaces yet"
18+
commands:
19+
- assertVisible: "Tap + to create one"
20+
21+
# If workspaces exist, navigate to detail screen
22+
- runFlow:
23+
when:
24+
visible: "Details"
25+
commands:
26+
# Tap on Details button to navigate
27+
- tapOn: "Details"
28+
29+
# Should see the detail screen with back button
30+
- assertVisible: ""
31+
32+
# Should see the tab bar with Sessions and Settings
33+
- assertVisible: "Sessions"
34+
- assertVisible: "Settings"
35+
36+
# Sessions tab should be active by default
37+
# Test switching to Settings tab
38+
- tapOn: "Settings"
39+
40+
# Should see workspace settings content
41+
- assertVisible: "Workspace Info"
42+
- assertVisible: "Sync Credentials"
43+
- assertVisible: "Actions"
44+
45+
# Test switching back to Sessions tab
46+
- tapOn: "Sessions"
47+
48+
# Handle empty sessions
49+
- runFlow:
50+
when:
51+
visible: "No sessions"
52+
commands:
53+
- assertVisible: "No sessions"
54+
55+
# Navigate back to workspaces list
56+
- tapOn: ""
57+
58+
# Should be back on workspaces screen
59+
- assertVisible: "Workspaces"

mobile/.maestro/workspaces.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,13 @@ appId: com.subroutine.workspace
2727
visible: "No workspaces yet"
2828
commands:
2929
- assertVisible: "Tap + to create one"
30+
31+
# If workspaces exist, verify stats bar and workspace items
32+
- runFlow:
33+
when:
34+
visible: "Total"
35+
commands:
36+
- assertVisible: "Running"
37+
- assertVisible: "Stopped"
38+
# Verify workspace items have expected buttons
39+
- assertVisible: "Details"

mobile/App.tsx

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
1+
import { useState, useEffect } from 'react'
12
import { StatusBar } from 'expo-status-bar'
2-
import { View } from 'react-native'
3+
import { View, ActivityIndicator } from 'react-native'
34
import { NavigationContainer, DefaultTheme } from '@react-navigation/native'
45
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
5-
import { SafeAreaProvider, useSafeAreaInsets } from 'react-native-safe-area-context'
6+
import { SafeAreaProvider } from 'react-native-safe-area-context'
67
import { TabNavigator } from './src/navigation/TabNavigator'
78
import { NetworkProvider, ConnectionBanner } from './src/lib/network'
9+
import { SetupScreen } from './src/screens/SetupScreen'
10+
import { loadServerConfig, isConfigured } from './src/lib/api'
811

912
const queryClient = new QueryClient({
1013
defaultOptions: {
@@ -23,35 +26,60 @@ const DarkTheme = {
2326
...DefaultTheme.colors,
2427
primary: '#0a84ff',
2528
background: '#000',
26-
card: '#1c1c1e',
29+
card: '#000',
2730
text: '#fff',
2831
border: '#2c2c2e',
2932
notification: '#ff3b30',
3033
},
3134
}
3235

3336
function AppContent() {
34-
const insets = useSafeAreaInsets()
35-
return (
36-
<View style={{ flex: 1, backgroundColor: '#000' }}>
37-
<View style={{ paddingTop: insets.top }}>
38-
<ConnectionBanner />
37+
const [loading, setLoading] = useState(true)
38+
const [configured, setConfigured] = useState(false)
39+
40+
useEffect(() => {
41+
loadServerConfig().then(() => {
42+
setConfigured(isConfigured())
43+
setLoading(false)
44+
})
45+
}, [])
46+
47+
if (loading) {
48+
return (
49+
<View style={{ flex: 1, backgroundColor: '#000', justifyContent: 'center', alignItems: 'center' }}>
50+
<ActivityIndicator size="large" color="#0a84ff" />
51+
<StatusBar style="light" />
3952
</View>
40-
<TabNavigator />
53+
)
54+
}
55+
56+
if (!configured) {
57+
return (
58+
<>
59+
<SetupScreen onComplete={() => setConfigured(true)} />
60+
<StatusBar style="light" />
61+
</>
62+
)
63+
}
64+
65+
return (
66+
<NetworkProvider>
67+
<NavigationContainer theme={DarkTheme}>
68+
<View style={{ flex: 1, backgroundColor: '#000' }}>
69+
<ConnectionBanner />
70+
<TabNavigator />
71+
</View>
72+
</NavigationContainer>
4173
<StatusBar style="light" />
42-
</View>
74+
</NetworkProvider>
4375
)
4476
}
4577

4678
export default function App() {
4779
return (
4880
<SafeAreaProvider>
4981
<QueryClientProvider client={queryClient}>
50-
<NetworkProvider>
51-
<NavigationContainer theme={DarkTheme}>
52-
<AppContent />
53-
</NavigationContainer>
54-
</NetworkProvider>
82+
<AppContent />
5583
</QueryClientProvider>
5684
</SafeAreaProvider>
5785
)

mobile/app.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
"backgroundColor": "#ffffff"
1414
},
1515
"ios": {
16-
"supportsTablet": true
16+
"supportsTablet": true,
17+
"bundleIdentifier": "com.subroutine.workspace"
1718
},
1819
"android": {
1920
"package": "com.subroutine.workspace",

0 commit comments

Comments
 (0)