expo-dev-menu 7.0.12 → 7.0.13

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.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,13 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 7.0.13 — 2025-10-01
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [iOS] Adjust tvOS colors. ([#40006](https://github.com/expo/expo/pull/40006) by [@douglowder](https://github.com/douglowder))
18
+ - [Android] Render registered custom Dev Menu items and allow invoking callbacks ([#39995](https://github.com/expo/expo/pull/39995) by [@Jc-Cloete](https://github.com/Jc-Cloete))
19
+
13
20
  ## 7.0.12 — 2025-09-22
14
21
 
15
22
  _This version does not introduce any user-facing changes._
@@ -12,7 +12,7 @@ apply plugin: 'expo-module-gradle-plugin'
12
12
  apply plugin: 'org.jetbrains.kotlin.plugin.compose'
13
13
 
14
14
  group = 'host.exp.exponent'
15
- version = '7.0.12'
15
+ version = '7.0.13'
16
16
 
17
17
  expoModule {
18
18
  canBePublished false
@@ -22,7 +22,7 @@ android {
22
22
  namespace "expo.modules.devmenu"
23
23
  defaultConfig {
24
24
  versionCode 10
25
- versionName '7.0.12'
25
+ versionName '7.0.13'
26
26
  }
27
27
  buildFeatures {
28
28
  compose true
@@ -235,6 +235,7 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
235
235
  val currentReactInstance = currentReactInstance.get() ?: return
236
236
  val appInfo = AppInfo.getAppInfo(currentReactInstance)
237
237
  bindingView.viewModel.updateAppInfo(appInfo)
238
+ bindingView.viewModel.updateCustomItems(registeredCallbacks)
238
239
  }
239
240
 
240
241
  inline fun withBindingView(
@@ -412,5 +413,13 @@ object DevMenuManager : DevMenuManagerInterface, LifecycleEventListener {
412
413
  setUpReactInstance(newReactInstance)
413
414
  }
414
415
  }
416
+
417
+ fun refreshCustomItems() {
418
+ delegateActivity?.let { activity ->
419
+ withBindingView(activity) { bindingView ->
420
+ bindingView.viewModel.updateCustomItems(registeredCallbacks)
421
+ }
422
+ }
423
+ }
415
424
  //endregion
416
425
  }
@@ -12,6 +12,10 @@ sealed class DevMenuAction(val shouldCloseMenu: Boolean = false) {
12
12
  data class ToggleFastRefresh(val newValue: Boolean) : DevMenuAction(shouldCloseMenu = false)
13
13
  object OpenReactNativeDevMenu : DevMenuAction(shouldCloseMenu = true)
14
14
  object FinishOnboarding : DevMenuAction(shouldCloseMenu = false)
15
+ data class TriggerCustomCallback(
16
+ val name: String,
17
+ val shouldCollapse: Boolean
18
+ ) : DevMenuAction(shouldCloseMenu = shouldCollapse)
15
19
  }
16
20
 
17
21
  typealias DevMenuActionHandler = (DevMenuAction) -> Unit
@@ -10,8 +10,14 @@ data class DevMenuState(
10
10
  val isOpen: Boolean = false,
11
11
  val devToolsSettings: DevToolsSettings = DevToolsSettings(),
12
12
  val isOnboardingFinished: Boolean = false,
13
- val showFab: Boolean = DevMenuPreferencesHandle.showFab
13
+ val showFab: Boolean = DevMenuPreferencesHandle.showFab,
14
+ val customItems: List<CustomItem> = emptyList()
14
15
  ) {
16
+ data class CustomItem(
17
+ val name: String,
18
+ val shouldCollapse: Boolean
19
+ )
20
+
15
21
  data class AppInfo(
16
22
  val appName: String,
17
23
  val hostUrl: String,
@@ -9,7 +9,8 @@ class DevMenuViewModel : ViewModel() {
9
9
  private val menuPreferences = DevMenuPreferencesHandle
10
10
  private val _state = mutableStateOf(
11
11
  DevMenuState(
12
- devToolsSettings = DevMenuManager.getDevSettings()
12
+ devToolsSettings = DevMenuManager.getDevSettings(),
13
+ customItems = mapCallbacks(DevMenuManager.registeredCallbacks)
13
14
  )
14
15
  )
15
16
 
@@ -38,6 +39,10 @@ class DevMenuViewModel : ViewModel() {
38
39
  )
39
40
  }
40
41
 
42
+ fun updateCustomItems(callbacks: List<DevMenuManager.Callback>) {
43
+ _state.value = _state.value.copy(customItems = mapCallbacks(callbacks))
44
+ }
45
+
41
46
  private fun closeMenu() {
42
47
  _state.value = _state.value.copy(isOpen = false)
43
48
  }
@@ -69,6 +74,14 @@ class DevMenuViewModel : ViewModel() {
69
74
  DevMenuManager.getSettings()?.isOnboardingFinished = true
70
75
  _state.value = _state.value.copy(isOnboardingFinished = true)
71
76
  }
77
+ is DevMenuAction.TriggerCustomCallback -> {
78
+ sendEventToDelegateBridge("registeredCallbackFired", action.name)
79
+ }
72
80
  }
73
81
  }
82
+
83
+ companion object {
84
+ private fun mapCallbacks(callbacks: List<DevMenuManager.Callback>) =
85
+ callbacks.map { DevMenuState.CustomItem(name = it.name, shouldCollapse = it.shouldCollapse) }
86
+ }
74
87
  }
@@ -0,0 +1,30 @@
1
+ package expo.modules.devmenu.compose.ui
2
+
3
+ import androidx.compose.foundation.layout.Arrangement
4
+ import androidx.compose.foundation.layout.Column
5
+ import androidx.compose.runtime.Composable
6
+ import expo.modules.devmenu.compose.DevMenuState
7
+ import expo.modules.devmenu.compose.newtheme.NewAppTheme
8
+ import expo.modules.devmenu.compose.primitives.NewText
9
+ import expo.modules.devmenu.compose.primitives.Spacer
10
+
11
+ @Composable
12
+ fun CustomItemsSection(
13
+ items: List<DevMenuState.CustomItem>,
14
+ onItemClick: (DevMenuState.CustomItem) -> Unit
15
+ ) {
16
+ Section.Header("CUSTOM MENU ITEMS")
17
+
18
+ Spacer(NewAppTheme.spacing.`3`)
19
+
20
+ Column(verticalArrangement = Arrangement.spacedBy(NewAppTheme.spacing.`2`)) {
21
+ items.forEach { item ->
22
+ NewMenuButton(
23
+ content = {
24
+ NewText(text = item.name)
25
+ },
26
+ onClick = { onItemClick(item) }
27
+ )
28
+ }
29
+ }
30
+ }
@@ -71,6 +71,7 @@ fun DevMenuBottomSheet(
71
71
  DevMenuScreen(
72
72
  appInfo = appInfo,
73
73
  devToolsSettings = state.devToolsSettings,
74
+ customItems = state.customItems,
74
75
  shouldShowOnboarding = shouldShowOnboarding.value,
75
76
  showFab = state.showFab,
76
77
  onAction = wrappedOnAction
@@ -22,6 +22,7 @@ import expo.modules.devmenu.compose.primitives.Spacer
22
22
  fun DevMenuScreen(
23
23
  appInfo: DevMenuState.AppInfo,
24
24
  devToolsSettings: DevToolsSettings,
25
+ customItems: List<DevMenuState.CustomItem> = emptyList(),
25
26
  shouldShowOnboarding: Boolean = false,
26
27
  showFab: Boolean = false,
27
28
  onAction: DevMenuActionHandler = {}
@@ -61,6 +62,17 @@ fun DevMenuScreen(
61
62
 
62
63
  Spacer(NewAppTheme.spacing.`5`)
63
64
 
65
+ if (customItems.isNotEmpty()) {
66
+ CustomItemsSection(
67
+ items = customItems,
68
+ onItemClick = { item ->
69
+ onAction(DevMenuAction.TriggerCustomCallback(item.name, item.shouldCollapse))
70
+ }
71
+ )
72
+
73
+ Spacer(NewAppTheme.spacing.`5`)
74
+ }
75
+
64
76
  ToolsSection(onAction, devToolsSettings, showFab)
65
77
 
66
78
  Box(modifier = Modifier.padding(vertical = NewAppTheme.spacing.`6`)) {
@@ -39,6 +39,8 @@ class DevMenuModule : Module() {
39
39
  }
40
40
  DevMenuManager.registeredCallbacks.add(DevMenuManager.Callback(name, shouldCollapse))
41
41
  }
42
+
43
+ DevMenuManager.refreshCustomItems()
42
44
  }
43
45
 
44
46
  OnDestroy {
@@ -117,4 +117,7 @@ object DevMenuManager : DevMenuManagerInterface {
117
117
  fun toggleFab() {
118
118
  throw IllegalStateException(DEV_MENU_IS_NOT_AVAILABLE)
119
119
  }
120
+ fun refreshCustomItems() {
121
+ throw IllegalStateException(DEV_MENU_IS_NOT_AVAILABLE)
122
+ }
120
123
  }
@@ -30,7 +30,11 @@ struct HeaderView: View {
30
30
  } label: {
31
31
  ZStack {
32
32
  Circle()
33
+ #if os(tvOS)
34
+ .fill(Color.expoSystemGray4.opacity(0.2))
35
+ #else
33
36
  .fill(Color.expoSystemGray6)
37
+ #endif
34
38
  .frame(width: 36, height: 36)
35
39
 
36
40
  Image(systemName: "xmark")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-dev-menu",
3
- "version": "7.0.12",
3
+ "version": "7.0.13",
4
4
  "description": "Expo/React Native module with the developer menu.",
5
5
  "main": "build/DevMenu.js",
6
6
  "types": "build/DevMenu.d.ts",
@@ -52,5 +52,5 @@
52
52
  "peerDependencies": {
53
53
  "expo": "*"
54
54
  },
55
- "gitHead": "6523053d0d997d2a21f580d2752b2f873c122038"
55
+ "gitHead": "1ab3b9621b78b77b81049ebf06149753a1e0898c"
56
56
  }