expo-dev-launcher 6.0.0 → 6.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/android/build.gradle +3 -3
  3. package/android/src/debug/java/expo/modules/devlauncher/compose/BindingView.kt +4 -2
  4. package/android/src/debug/java/expo/modules/devlauncher/compose/DevLauncherBottomTabsNavigator.kt +4 -2
  5. package/android/src/debug/java/expo/modules/devlauncher/compose/models/BranchesViewModel.kt +57 -13
  6. package/android/src/debug/java/expo/modules/devlauncher/compose/primitives/Accordion.kt +40 -45
  7. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Profile.kt +98 -18
  8. package/android/src/debug/java/expo/modules/devlauncher/compose/routes/Updates.kt +3 -1
  9. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/BranchScreen.kt +171 -146
  10. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/BranchesScreen.kt +248 -150
  11. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/CrashReportScreen.kt +75 -46
  12. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/HomeScreen.kt +234 -238
  13. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/NoUpdatesScreen.kt +52 -35
  14. package/android/src/debug/java/expo/modules/devlauncher/compose/screens/SettingsScreen.kt +246 -107
  15. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AccountAvatar.kt +17 -9
  16. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AccountSelector.kt +63 -46
  17. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ActionButton.kt +51 -3
  18. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/AppHeader.kt +70 -44
  19. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/BottomSheet.kt +42 -2
  20. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/BottomTabBar.kt +14 -10
  21. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/BottomTabButton.kt +46 -23
  22. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/DevelopmentServerHelp.kt +23 -16
  23. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/RunningAppCard.kt +65 -34
  24. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ScreenHeaderContainer.kt +0 -5
  25. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/ServerUrlInput.kt +56 -17
  26. package/android/src/debug/java/expo/modules/devlauncher/compose/ui/SignUp.kt +20 -27
  27. package/android/src/main/res/drawable/arrow_right.xml +9 -0
  28. package/android/src/main/res/drawable/check_circle.xml +10 -0
  29. package/android/src/main/res/drawable/chevron_right.xml +10 -0
  30. package/android/src/main/res/drawable/copy.xml +13 -0
  31. package/android/src/main/res/drawable/expo_logo.xml +9 -0
  32. package/android/src/main/res/drawable/home.xml +10 -0
  33. package/android/src/main/res/drawable/inspect.xml +10 -0
  34. package/android/src/main/res/drawable/log_in.xml +12 -0
  35. package/android/src/main/res/drawable/pulse.xml +10 -0
  36. package/android/src/main/res/drawable/reload.xml +10 -0
  37. package/android/src/main/res/drawable/scan.xml +42 -0
  38. package/android/src/main/res/drawable/settings.xml +14 -0
  39. package/android/src/main/res/drawable/show_at_launch.xml +12 -0
  40. package/android/src/main/res/drawable/signal.xml +26 -0
  41. package/android/src/main/res/drawable/user.xml +13 -0
  42. package/ios/EXDevLauncherController.m +23 -4
  43. package/ios/SwiftUI/CrashReportView.swift +11 -3
  44. package/ios/SwiftUI/DevLauncherViews.swift +18 -2
  45. package/ios/SwiftUI/DevServersView.swift +2 -2
  46. package/ios/SwiftUI/HomeTabView.swift +4 -0
  47. package/ios/SwiftUI/Navigation/Navigation.swift +3 -0
  48. package/ios/SwiftUI/SettingsTabView.swift +4 -3
  49. package/package.json +4 -4
package/CHANGELOG.md CHANGED
@@ -10,6 +10,16 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 6.0.2 — 2025-08-16
14
+
15
+ _This version does not introduce any user-facing changes._
16
+
17
+ ## 6.0.1 — 2025-08-15
18
+
19
+ ### 🐛 Bug fixes
20
+
21
+ - [ios] Fix tvOS and Swiftlint after UI refresh. ([#38808](https://github.com/expo/expo/pull/38808) by [@douglowder](https://github.com/douglowder))
22
+
13
23
  ## 6.0.0 — 2025-08-13
14
24
 
15
25
  ### 🎉 New features
@@ -20,13 +20,13 @@ expoModule {
20
20
  }
21
21
 
22
22
  group = "host.exp.exponent"
23
- version = "6.0.0"
23
+ version = "6.0.2"
24
24
 
25
25
  android {
26
26
  namespace "expo.modules.devlauncher"
27
27
  defaultConfig {
28
28
  versionCode 9
29
- versionName "6.0.0"
29
+ versionName "6.0.2"
30
30
  }
31
31
 
32
32
  buildTypes {
@@ -91,7 +91,7 @@ dependencies {
91
91
  api project.dependencies.platform("io.insert-koin:koin-bom:3.5.6")
92
92
  api "io.insert-koin:koin-core"
93
93
 
94
- def composeVersion = "1.8.2"
94
+ def composeVersion = "1.9.0"
95
95
 
96
96
  implementation "androidx.compose.foundation:foundation-android:$composeVersion"
97
97
  implementation "androidx.compose.ui:ui:$composeVersion"
@@ -10,8 +10,10 @@ class BindingView(context: Context) : LinearLayout(context) {
10
10
  addView(
11
11
  ComposeView(context).apply {
12
12
  setContent {
13
- AppTheme {
14
- DevLauncherBottomTabsNavigator()
13
+ expo.modules.devmenu.compose.newtheme.AppTheme {
14
+ AppTheme {
15
+ DevLauncherBottomTabsNavigator()
16
+ }
15
17
  }
16
18
  }
17
19
  }
@@ -7,6 +7,7 @@ import androidx.compose.animation.core.tween
7
7
  import androidx.compose.foundation.background
8
8
  import androidx.compose.foundation.layout.Box
9
9
  import androidx.compose.foundation.layout.fillMaxSize
10
+ import androidx.compose.foundation.layout.statusBarsPadding
10
11
  import androidx.compose.runtime.Composable
11
12
  import androidx.compose.runtime.remember
12
13
  import androidx.compose.ui.Modifier
@@ -25,7 +26,7 @@ import expo.modules.devlauncher.compose.routes.UpdatesRoute
25
26
  import expo.modules.devlauncher.compose.ui.BottomTabBar
26
27
  import expo.modules.devlauncher.compose.ui.Full
27
28
  import expo.modules.devlauncher.compose.ui.rememberBottomSheetState
28
- import expo.modules.devmenu.compose.theme.Theme
29
+ import expo.modules.devmenu.compose.newtheme.NewAppTheme
29
30
  import kotlinx.serialization.Serializable
30
31
 
31
32
  @Composable
@@ -35,7 +36,8 @@ fun DefaultScreenContainer(
35
36
  Box(
36
37
  modifier = Modifier
37
38
  .fillMaxSize()
38
- .background(Theme.colors.background.secondary)
39
+ .background(NewAppTheme.colors.background.default)
40
+ .statusBarsPadding()
39
41
  ) {
40
42
  content()
41
43
  }
@@ -8,10 +8,13 @@ import expo.modules.devlauncher.compose.Update
8
8
  import expo.modules.devlauncher.services.ApolloClientService
9
9
  import expo.modules.devlauncher.services.AppService
10
10
  import expo.modules.devlauncher.services.ApplicationInfo
11
+ import expo.modules.devlauncher.services.SessionService
12
+ import expo.modules.devlauncher.services.UserState
11
13
  import expo.modules.devlauncher.services.inject
12
14
  import kotlinx.coroutines.flow.MutableStateFlow
13
15
  import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
14
- import kotlinx.coroutines.flow.onStart
16
+ import kotlinx.coroutines.flow.combine
17
+ import kotlinx.coroutines.flow.onEach
15
18
  import kotlinx.coroutines.flow.stateIn
16
19
  import kotlinx.coroutines.launch
17
20
 
@@ -22,12 +25,14 @@ sealed interface BranchesAction {
22
25
 
23
26
  data class BranchesState(
24
27
  val branches: List<Branch> = emptyList(),
25
- val isLoading: Boolean = false
28
+ val isLoading: Boolean = false,
29
+ val needToSignIn: Boolean = false
26
30
  )
27
31
 
28
32
  class BranchesViewModel : ViewModel() {
29
33
  private val apolloClientService = inject<ApolloClientService>()
30
34
  private val appService = inject<AppService>()
35
+ private val sessionService = inject<SessionService>()
31
36
 
32
37
  private val hasMore = mutableStateOf(true)
33
38
 
@@ -36,20 +41,60 @@ class BranchesViewModel : ViewModel() {
36
41
  private var _state = MutableStateFlow(
37
42
  BranchesState(
38
43
  branches = emptyList(),
39
- isLoading = areUpdatesConfigured
44
+ isLoading = areUpdatesConfigured,
45
+ needToSignIn = sessionService.user.value == UserState.LoggedOut
40
46
  )
41
47
  )
42
48
 
43
- val state = _state.onStart {
44
- if (areUpdatesConfigured) {
45
- hasMore.value = true
46
- loadMoreBranches()
49
+ private val _sessionState = sessionService.user.onEach { newUser ->
50
+ when (newUser) {
51
+ UserState.LoggedOut -> {
52
+ hasMore.value = false
53
+ _state.value = _state.value.copy(
54
+ branches = emptyList(),
55
+ isLoading = false
56
+ )
57
+ }
58
+
59
+ is UserState.LoggedIn -> {
60
+ if (areUpdatesConfigured) {
61
+ hasMore.value = true
62
+ _state.value.copy(branches = emptyList(), isLoading = true)
63
+ viewModelScope.launch {
64
+ loadMoreBranches()
65
+ }
66
+ }
67
+ }
68
+
69
+ is UserState.Fetching -> {
70
+ _state.value = _state.value.copy(
71
+ isLoading = true,
72
+ branches = emptyList()
73
+ )
74
+ }
47
75
  }
48
- }.stateIn(
49
- scope = viewModelScope,
50
- started = WhileSubscribed(5_000),
51
- initialValue = _state.value
52
- )
76
+ }
77
+
78
+ val state = _state
79
+ .combine(_sessionState) { branchState, newUser ->
80
+ when (newUser) {
81
+ UserState.Fetching -> {
82
+ branchState.copy(needToSignIn = false)
83
+ }
84
+
85
+ UserState.LoggedOut -> {
86
+ branchState.copy(needToSignIn = true)
87
+ }
88
+
89
+ is UserState.LoggedIn -> {
90
+ branchState.copy(needToSignIn = false)
91
+ }
92
+ }
93
+ }.stateIn(
94
+ scope = viewModelScope,
95
+ started = WhileSubscribed(5_000),
96
+ initialValue = _state.value
97
+ )
53
98
 
54
99
  private suspend fun loadMoreBranches() {
55
100
  val updateConfiguration = appService.applicationInfo as? ApplicationInfo.Updates
@@ -97,7 +142,6 @@ class BranchesViewModel : ViewModel() {
97
142
  } ?: emptyList()
98
143
 
99
144
  hasMore.value = branches.size == limit
100
-
101
145
  _state.value = _state.value.copy(
102
146
  isLoading = false,
103
147
  branches = _state.value.branches + branches
@@ -6,11 +6,10 @@ import androidx.compose.animation.core.tween
6
6
  import androidx.compose.animation.expandVertically
7
7
  import androidx.compose.animation.shrinkVertically
8
8
  import androidx.compose.foundation.clickable
9
+ import androidx.compose.foundation.layout.Arrangement
9
10
  import androidx.compose.foundation.layout.Box
10
11
  import androidx.compose.foundation.layout.Column
11
12
  import androidx.compose.foundation.layout.Row
12
- import androidx.compose.foundation.layout.Spacer
13
- import androidx.compose.foundation.layout.padding
14
13
  import androidx.compose.foundation.layout.size
15
14
  import androidx.compose.runtime.Composable
16
15
  import androidx.compose.runtime.getValue
@@ -20,11 +19,14 @@ import androidx.compose.runtime.setValue
20
19
  import androidx.compose.ui.Alignment
21
20
  import androidx.compose.ui.Modifier
22
21
  import androidx.compose.ui.draw.rotate
22
+ import androidx.compose.ui.res.painterResource
23
23
  import androidx.compose.ui.tooling.preview.Preview
24
+ import androidx.compose.ui.unit.dp
25
+ import com.composeunstyled.Icon
24
26
  import expo.modules.devlauncher.R
25
- import expo.modules.devmenu.compose.primitives.DayNighIcon
27
+ import expo.modules.devmenu.compose.newtheme.NewAppTheme
28
+ import expo.modules.devmenu.compose.primitives.NewText
26
29
  import expo.modules.devmenu.compose.primitives.Text
27
- import expo.modules.devmenu.compose.theme.Theme
28
30
 
29
31
  @Composable
30
32
  fun Accordion(
@@ -39,53 +41,46 @@ fun Accordion(
39
41
  label = "accordion-arrow"
40
42
  )
41
43
 
42
- Box(
43
- modifier = modifier
44
- ) {
45
- Column {
46
- Box(
47
- modifier = Modifier
48
- .clickable { expanded = !expanded }
44
+ Column {
45
+ Box(
46
+ modifier = Modifier
47
+ .clickable { expanded = !expanded }
48
+ ) {
49
+ Row(
50
+ horizontalArrangement = Arrangement.spacedBy(NewAppTheme.spacing.`2`),
51
+ verticalAlignment = Alignment.CenterVertically,
52
+ modifier = modifier
49
53
  ) {
50
- Row(
51
- verticalAlignment = Alignment.CenterVertically,
54
+ Icon(
55
+ painter = painterResource(R.drawable.arrow_right),
56
+ contentDescription = "Accordion Arrow",
57
+ tint = NewAppTheme.colors.text.link,
52
58
  modifier = Modifier
53
- .padding(Theme.spacing.medium)
54
- ) {
55
- DayNighIcon(
56
- id = R.drawable.chevron_right_icon,
57
- contentDescription = "Accordion Arrow",
58
- modifier = Modifier
59
- .rotate(arrowRotation)
60
- .size(Theme.sizing.icon.extraSmall)
61
- )
62
- Spacer(Modifier.size(Theme.spacing.small))
63
- Text(
64
- text = text,
65
- modifier = Modifier.weight(1f)
66
- )
67
- }
68
- }
59
+ .rotate(arrowRotation)
60
+ .size(16.dp)
61
+ )
69
62
 
70
- AnimatedVisibility(
71
- visible = expanded,
72
- enter = expandVertically(
73
- expandFrom = Alignment.Top,
74
- animationSpec = tween()
75
- ),
76
- exit = shrinkVertically(
77
- shrinkTowards = Alignment.Top,
78
- animationSpec = tween()
63
+ NewText(
64
+ text = text,
65
+ style = NewAppTheme.font.sm,
66
+ color = NewAppTheme.colors.text.link
79
67
  )
80
- ) {
81
- Box(
82
- modifier = Modifier
83
- .padding(horizontal = Theme.spacing.small)
84
- ) {
85
- accordionContent()
86
- }
87
68
  }
88
69
  }
70
+
71
+ AnimatedVisibility(
72
+ visible = expanded,
73
+ enter = expandVertically(
74
+ expandFrom = Alignment.Top,
75
+ animationSpec = tween()
76
+ ),
77
+ exit = shrinkVertically(
78
+ shrinkTowards = Alignment.Top,
79
+ animationSpec = tween()
80
+ )
81
+ ) {
82
+ accordionContent()
83
+ }
89
84
  }
90
85
  }
91
86
 
@@ -1,21 +1,43 @@
1
1
  package expo.modules.devlauncher.compose.routes
2
2
 
3
3
  import androidx.activity.compose.rememberLauncherForActivityResult
4
+ import androidx.compose.foundation.background
5
+ import androidx.compose.foundation.layout.Arrangement
6
+ import androidx.compose.foundation.layout.Box
7
+ import androidx.compose.foundation.layout.Column
8
+ import androidx.compose.foundation.layout.Row
9
+ import androidx.compose.foundation.layout.fillMaxWidth
10
+ import androidx.compose.foundation.layout.height
11
+ import androidx.compose.foundation.layout.size
12
+ import androidx.compose.foundation.shape.RoundedCornerShape
4
13
  import androidx.compose.runtime.Composable
5
14
  import androidx.compose.runtime.getValue
15
+ import androidx.compose.ui.Alignment
16
+ import androidx.compose.ui.Modifier
17
+ import androidx.compose.ui.draw.dropShadow
18
+ import androidx.compose.ui.graphics.Color
19
+ import androidx.compose.ui.graphics.shadow.Shadow
20
+ import androidx.compose.ui.res.painterResource
21
+ import androidx.compose.ui.text.style.TextAlign
22
+ import androidx.compose.ui.unit.DpOffset
23
+ import androidx.compose.ui.unit.dp
24
+ import androidx.compose.ui.unit.sp
6
25
  import androidx.lifecycle.compose.collectAsStateWithLifecycle
7
26
  import androidx.lifecycle.viewmodel.compose.viewModel
8
27
  import com.composables.core.ModalBottomSheetState
9
- import com.composables.core.SheetDetent.Companion.Hidden
28
+ import com.composeunstyled.Icon
29
+ import expo.modules.devlauncher.R
10
30
  import expo.modules.devlauncher.compose.AuthActivity
11
31
  import expo.modules.devlauncher.compose.AuthRequestType
12
32
  import expo.modules.devlauncher.compose.AuthResult
13
33
  import expo.modules.devlauncher.compose.models.ProfileState
14
34
  import expo.modules.devlauncher.compose.models.ProfileViewModel
35
+ import expo.modules.devlauncher.compose.primitives.CircularProgressBar
15
36
  import expo.modules.devlauncher.compose.ui.AccountSelector
16
37
  import expo.modules.devlauncher.compose.ui.BottomSheet
17
- import expo.modules.devlauncher.compose.ui.ProfileLayout
18
38
  import expo.modules.devlauncher.compose.ui.SignUp
39
+ import expo.modules.devmenu.compose.newtheme.NewAppTheme
40
+ import expo.modules.devmenu.compose.primitives.NewText
19
41
 
20
42
  @Composable
21
43
  fun ProfileRoute(
@@ -34,24 +56,74 @@ fun ProfileRoute(
34
56
  }
35
57
 
36
58
  BottomSheet(bottomSheetState) {
37
- ProfileLayout(onClose = {
38
- bottomSheetState.targetDetent = Hidden
39
- }) {
40
- when (state) {
41
- is ProfileState.LoggedIn -> {
42
- val state = state as ProfileState.LoggedIn
43
- AccountSelector(
44
- accounts = state.accounts,
45
- onSignOut = {
46
- viewModel.onAction(ProfileViewModel.Action.SignOut)
47
- },
48
- onClick = { account ->
49
- viewModel.onAction(ProfileViewModel.Action.SwitchAccount(account))
59
+ when (state) {
60
+ is ProfileState.LoggedIn -> {
61
+ val state = state as ProfileState.LoggedIn
62
+ AccountSelector(
63
+ accounts = state.accounts,
64
+ onSignOut = {
65
+ viewModel.onAction(ProfileViewModel.Action.SignOut)
66
+ },
67
+ onClick = { account ->
68
+ viewModel.onAction(ProfileViewModel.Action.SwitchAccount(account))
69
+ }
70
+ )
71
+ }
72
+
73
+ ProfileState.LoggedOut -> {
74
+ Column(
75
+ verticalArrangement = Arrangement.spacedBy(NewAppTheme.spacing.`4`)
76
+ ) {
77
+ Row(
78
+ horizontalArrangement = Arrangement.Center,
79
+ modifier = Modifier
80
+ .fillMaxWidth()
81
+ ) {
82
+ Box(
83
+ modifier = Modifier
84
+ .size(44.dp)
85
+ .dropShadow(
86
+ shape = RoundedCornerShape(NewAppTheme.borderRadius.md),
87
+ shadow = Shadow(
88
+ radius = 10.dp,
89
+ offset = DpOffset(0.dp, 5.dp),
90
+ color = Color.Black.copy(alpha = 0.05f)
91
+ )
92
+ )
93
+ .dropShadow(
94
+ shape = RoundedCornerShape(NewAppTheme.borderRadius.md),
95
+ shadow = Shadow(
96
+ radius = 25.dp,
97
+ offset = DpOffset(0.dp, 15.dp),
98
+ color = Color.Black.copy(alpha = 0.12f)
99
+ )
100
+ )
101
+ .background(
102
+ NewAppTheme.colors.background.default,
103
+ shape = RoundedCornerShape(NewAppTheme.borderRadius.md)
104
+ )
105
+ ) {
106
+ Icon(
107
+ painter = painterResource(R.drawable.expo_logo),
108
+ contentDescription = "Expo logo",
109
+ tint = NewAppTheme.colors.icon.default,
110
+ modifier = Modifier
111
+ .size(24.dp)
112
+ .align(Alignment.Center)
113
+ )
50
114
  }
115
+ }
116
+
117
+ NewText(
118
+ "Login or create an account to view local\ndevelopment servers and more",
119
+ style = NewAppTheme.font.sm.merge(
120
+ lineHeight = 19.sp,
121
+ textAlign = TextAlign.Center
122
+ ),
123
+ color = NewAppTheme.colors.text.secondary,
124
+ modifier = Modifier.fillMaxWidth()
51
125
  )
52
- }
53
126
 
54
- ProfileState.LoggedOut -> {
55
127
  SignUp(
56
128
  onLogIn = {
57
129
  authLauncher.launch(AuthRequestType.LOGIN)
@@ -61,8 +133,16 @@ fun ProfileRoute(
61
133
  }
62
134
  )
63
135
  }
136
+ }
64
137
 
65
- ProfileState.Fetching -> {
138
+ ProfileState.Fetching -> {
139
+ Box(
140
+ contentAlignment = Alignment.Center,
141
+ modifier = Modifier
142
+ .fillMaxWidth()
143
+ .height(300.dp)
144
+ ) {
145
+ CircularProgressBar(size = 72.dp)
66
146
  }
67
147
  }
68
148
  }
@@ -44,11 +44,13 @@ fun UpdatesRoute(
44
44
  composable<Routes.Updates.Branches> {
45
45
  val viewModel = viewModel<BranchesViewModel>()
46
46
 
47
+ val state by viewModel.state.collectAsStateWithLifecycle()
48
+
47
49
  if (viewModel.areUpdatesConfigured) {
48
- val state by viewModel.state.collectAsStateWithLifecycle()
49
50
  BranchesScreen(
50
51
  branches = state.branches,
51
52
  isLoading = state.isLoading,
53
+ needToSignIn = state.needToSignIn,
52
54
  onProfileClick = onProfileClick,
53
55
  onAction = { action ->
54
56
  when (action) {