Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions apps/mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,30 @@ Build and run the local iOS dev client:
vp run ios:dev
```

If your Xcode account only has a Personal Team, use a bundle identifier you control and opt into the
reduced-capability local build. Personal Team builds omit the widget extension, push entitlement, and
native Sign in with Apple entitlement; builds without this opt-in are unchanged.

```bash
T3CODE_IOS_PERSONAL_TEAM=1 \
T3CODE_IOS_PERSONAL_TEAM_BUNDLE_ID=com.example.t3code.dev \
vp run ios:dev
```

Build and install a self-contained Release app that does not need Metro:

```bash
vp run ios:release
```

The Personal Team equivalent also needs a unique bundle identifier:

```bash
T3CODE_IOS_PERSONAL_TEAM=1 \
T3CODE_IOS_PERSONAL_TEAM_BUNDLE_ID=com.example.t3code \
vp run ios:release
```

Build and run the local iOS preview app:

```bash
Expand Down
59 changes: 41 additions & 18 deletions apps/mobile/app.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,20 @@ const repoEnv = loadRepoEnv();
Object.assign(process.env, repoEnv);

const APP_VARIANT = resolveAppVariant(repoEnv.APP_VARIANT);
const isIosPersonalTeamBuild = repoEnv.T3CODE_IOS_PERSONAL_TEAM === "1";

const personalTeamBundleIdentifier = repoEnv.T3CODE_IOS_PERSONAL_TEAM_BUNDLE_ID?.trim();
const IOS_BUNDLE_IDENTIFIER_PATTERN = /^[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)+$/;

if (
isIosPersonalTeamBuild &&
(!personalTeamBundleIdentifier ||
!IOS_BUNDLE_IDENTIFIER_PATTERN.test(personalTeamBundleIdentifier))
) {
throw new Error(
"T3CODE_IOS_PERSONAL_TEAM_BUNDLE_ID must be a reverse-DNS identifier such as com.example.t3code when T3CODE_IOS_PERSONAL_TEAM=1.",
);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const VARIANT_CONFIG: Record<
AppVariant,
Expand Down Expand Up @@ -54,6 +68,27 @@ function resolveAppVariant(value: string | undefined): AppVariant {
}

const variant = VARIANT_CONFIG[APP_VARIANT];
const iosBundleIdentifier =
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
isIosPersonalTeamBuild && personalTeamBundleIdentifier
? personalTeamBundleIdentifier
: variant.iosBundleIdentifier;

const widgetsPlugin = [
"expo-widgets",
{
bundleIdentifier: `${iosBundleIdentifier}.widgets`,
groupIdentifier: `group.${iosBundleIdentifier}`,
enablePushNotifications: true,
widgets: [
{
name: "AgentActivity",
displayName: "Agent Activity",
description: "Shows the current state of active T3 Code agents.",
supportedFamilies: ["systemSmall", "systemMedium", "accessoryRectangular"],
},
],
},
] satisfies NonNullable<ExpoConfig["plugins"]>[number];

const config: ExpoConfig = {
name: variant.appName,
Expand All @@ -76,7 +111,7 @@ const config: ExpoConfig = {
ios: {
icon: variant.iosIcon,
supportsTablet: true,
bundleIdentifier: variant.iosBundleIdentifier,
bundleIdentifier: iosBundleIdentifier,
infoPlist: {
NSAppTransportSecurity: {
NSAllowsArbitraryLoads: true,
Expand All @@ -102,9 +137,10 @@ const config: ExpoConfig = {
},
plugins: [
"expo-router",
"expo-asset",
"expo-font",
"expo-secure-store",
["@clerk/expo", { theme: "./clerk-theme.json" }],
["@clerk/expo", { theme: "./clerk-theme.json", appleSignIn: !isIosPersonalTeamBuild }],
"expo-web-browser",
[
"expo-camera",
Expand Down Expand Up @@ -139,26 +175,13 @@ const config: ExpoConfig = {
},
Comment thread
bmdavis419 marked this conversation as resolved.
},
],
[
"expo-widgets",
{
bundleIdentifier: `${variant.iosBundleIdentifier}.widgets`,
groupIdentifier: `group.${variant.iosBundleIdentifier}`,
enablePushNotifications: true,
widgets: [
{
name: "AgentActivity",
displayName: "Agent Activity",
description: "Shows the current state of active T3 Code agents.",
supportedFamilies: ["systemSmall", "systemMedium", "accessoryRectangular"],
},
],
},
],
...(!isIosPersonalTeamBuild ? [widgetsPlugin] : []),
"./plugins/withAndroidCleartextTraffic.cjs",
...(isIosPersonalTeamBuild ? ["./plugins/withoutIosPersonalTeamCapabilities.cjs"] : []),
],
extra: {
appVariant: APP_VARIANT,
iosPersonalTeamBuild: isIosPersonalTeamBuild,
relay: {
url: repoEnv.T3CODE_RELAY_URL ?? null,
},
Expand Down
40 changes: 21 additions & 19 deletions apps/mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"ios:dev": "APP_VARIANT=development EXPO_NO_GIT_STATUS=1 expo prebuild --clean --platform ios && expo run:ios",
"ios:preview": "APP_VARIANT=preview EXPO_NO_GIT_STATUS=1 expo prebuild --clean --platform ios && expo run:ios",
"ios:prod": "APP_VARIANT=production EXPO_NO_GIT_STATUS=1 expo prebuild --clean --platform ios && expo run:ios",
"ios:release": "APP_VARIANT=production EXPO_NO_GIT_STATUS=1 expo prebuild --clean --platform ios && expo run:ios --configuration Release --no-bundler",
"eas:ios:dev": "eas build --profile development -p ios",
"eas:ios:preview": "eas build --profile preview -p ios",
"eas:ios:preview:dev": "eas build --profile preview:dev -p ios",
Expand All @@ -43,7 +44,7 @@
"@clerk/expo": "catalog:",
"@effect/atom-react": "catalog:",
"@expo-google-fonts/dm-sans": "^0.4.2",
"@expo/ui": "~56.0.8",
"@expo/ui": "~56.0.18",
"@legendapp/list": "3.2.0",
"@noble/curves": "catalog:",
"@noble/hashes": "catalog:",
Expand All @@ -59,41 +60,42 @@
"@t3tools/mobile-review-diff-native": "file:./modules/t3-review-diff",
"@t3tools/mobile-terminal-native": "file:./modules/t3-terminal",
"@t3tools/shared": "workspace:*",
"@tabler/icons-react-native": "^3.44.0",
"clsx": "^2.1.1",
"diff": "8.0.3",
"effect": "catalog:",
"expo": "^56.0.0",
"expo-asset": "~56.0.15",
"expo-auth-session": "~56.0.12",
"expo-build-properties": "~56.0.15",
"expo-camera": "~56.0.7",
"expo-clipboard": "~56.0.3",
"expo-constants": "~56.0.16",
"expo": "^56.0.12",
"expo-asset": "~56.0.17",
"expo-auth-session": "~56.0.14",
"expo-build-properties": "~56.0.19",
"expo-camera": "~56.0.8",
"expo-clipboard": "~56.0.4",
"expo-constants": "~56.0.18",
"expo-crypto": "~56.0.4",
"expo-dev-client": "~56.0.16",
"expo-file-system": "~56.0.7",
"expo-font": "~56.0.5",
"expo-dev-client": "~56.0.20",
"expo-file-system": "~56.0.8",
"expo-font": "~56.0.7",
"expo-glass-effect": "~56.0.4",
"expo-haptics": "~56.0.3",
"expo-image-picker": "~56.0.14",
"expo-linking": "~56.0.12",
"expo-image-picker": "~56.0.18",
"expo-linking": "~56.0.14",
"expo-network": "~56.0.5",
"expo-notifications": "~56.0.14",
"expo-notifications": "~56.0.18",
"expo-paste-input": "^0.1.15",
"expo-router": "~56.2.7",
"expo-router": "~56.2.11",
"expo-secure-store": "~56.0.4",
"expo-splash-screen": "~56.0.10",
"expo-symbols": "~56.0.5",
"expo-updates": "~56.0.17",
"expo-symbols": "~56.0.6",
"expo-updates": "~56.0.19",
"expo-web-browser": "~56.0.5",
"expo-widgets": "~56.0.15",
"expo-widgets": "~56.0.19",
"punycode": "^2.3.1",
"react": "19.2.3",
"react-dom": "19.2.3",
"react-native": "0.85.3",
"react-native-gesture-handler": "~2.31.1",
"react-native-image-viewing": "^0.2.2",
"react-native-keyboard-controller": "1.21.7",
"react-native-keyboard-controller": "1.21.6",
"react-native-nitro-markdown": "^0.5.0",
"react-native-nitro-modules": "0.35.9",
"react-native-reanimated": "4.3.1",
Expand Down
10 changes: 10 additions & 0 deletions apps/mobile/plugins/withoutIosPersonalTeamCapabilities.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
const { withEntitlementsPlist } = require("expo/config-plugins");

module.exports = function withoutIosPersonalTeamCapabilities(config) {
return withEntitlementsPlist(config, (modConfig) => {
delete modConfig.modResults["aps-environment"];
delete modConfig.modResults["com.apple.developer.applesignin"];
delete modConfig.modResults["com.apple.security.application-groups"];
return modConfig;
});
};
2 changes: 1 addition & 1 deletion apps/mobile/src/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
DMSans_700Bold,
useFonts,
} from "@expo-google-fonts/dm-sans";
import { RegistryContext } from "@effect/atom-react";
import { usePathname } from "expo-router";
import Stack from "expo-router/stack";
import { useCallback } from "react";
Expand All @@ -18,7 +19,6 @@ import { LoadingScreen } from "../components/LoadingScreen";

import { useWorkspaceState } from "../state/workspace";
import { useThreadOutboxDrain } from "../state/use-thread-outbox-drain";
import { RegistryContext } from "@effect/atom-react";
import { appAtomRegistry } from "../state/atom-registry";
import { CloudAuthProvider } from "../features/cloud/CloudAuthProvider";
import {
Expand Down
1 change: 1 addition & 0 deletions apps/mobile/src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export default function HomeRouteScreen() {
<>
<HomeHeader
environments={environments}
searchQuery={searchQuery}
selectedEnvironmentId={selectedEnvironmentId}
projectSortOrder={listOptions.projectSortOrder}
threadSortOrder={listOptions.threadSortOrder}
Expand Down
20 changes: 16 additions & 4 deletions apps/mobile/src/app/settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
squashAtomCommandFailure,
} from "@t3tools/client-runtime/state/runtime";
import { AppText as Text } from "../../components/AppText";
import { supportsAgentAwarenessPush } from "../../features/agent-awareness/capabilities";
import { setLiveActivityUpdatesEnabled } from "../../features/agent-awareness/liveActivityPreferences";
import { requestAgentNotificationPermission } from "../../features/agent-awareness/notificationPermissions";
import { refreshAgentAwarenessRegistration } from "../../features/agent-awareness/remoteRegistration";
Expand Down Expand Up @@ -74,6 +75,7 @@ function LocalSettingsRouteScreen() {
}

function ConfiguredSettingsRouteScreen() {
Comment thread
bmdavis419 marked this conversation as resolved.
const agentAwarenessPushAvailable = supportsAgentAwarenessPush();
const insets = useSafeAreaInsets();
const { push } = useRouter();
const { expand: expandClerkSheet } = useClerkSettingsSheetDetent();
Expand Down Expand Up @@ -361,17 +363,27 @@ function ConfiguredSettingsRouteScreen() {
<SettingsSwitchRow
icon="bell.badge"
label="Device Notifications"
disabled={notificationStatus === "checking" || notificationStatus === "unsupported"}
value={notificationStatus === "enabled"}
disabled={
!agentAwarenessPushAvailable ||
notificationStatus === "checking" ||
notificationStatus === "unsupported"
}
value={agentAwarenessPushAvailable && notificationStatus === "enabled"}
onValueChange={handleDeviceNotificationsChange}
/>
<SettingsSwitchRow
disabled={
!isLoaded || liveActivityStatus === "checking" || liveActivityStatus === "linking"
!agentAwarenessPushAvailable ||
!isLoaded ||
liveActivityStatus === "checking" ||
liveActivityStatus === "linking"
}
icon="bolt.circle"
label="Live Activity Updates"
value={liveActivityStatus === "enabled" || liveActivityStatus === "linking"}
value={
agentAwarenessPushAvailable &&
(liveActivityStatus === "enabled" || liveActivityStatus === "linking")
}
onValueChange={handleLiveActivitiesChange}
/>
</SettingsSection>
Expand Down
Loading
Loading