diff --git a/dapps/pos-app/.env.example b/dapps/pos-app/.env.example index 3d0380f2..deac4661 100644 --- a/dapps/pos-app/.env.example +++ b/dapps/pos-app/.env.example @@ -1,3 +1,4 @@ EXPO_PUBLIC_PROJECT_ID="" EXPO_PUBLIC_SENTRY_DSN="" -SENTRY_AUTH_TOKEN="" \ No newline at end of file +SENTRY_AUTH_TOKEN="" +EXPO_PUBLIC_VARIANT="default" # or "polygon" \ No newline at end of file diff --git a/dapps/pos-app/README.md b/dapps/pos-app/README.md index fdfec630..ba007c40 100644 --- a/dapps/pos-app/README.md +++ b/dapps/pos-app/README.md @@ -36,6 +36,42 @@ npm run ios ``` +## App Variants + +This app supports building different variants using environment configuration files. This is useful for maintaining multiple versions of the app (e.g., production, staging, or different client configurations). + +### Available Variants + +- **Default**: Uses `.env.default` configuration +- **Variant**: Uses `.env.variant` configuration + +### Setting Up Variants + +1. Create your variant environment files: + + ```bash + cp .env .env.default + cp .env .env.variant + ``` + +2. Update each environment file with the appropriate configuration values for that variant. + +### Building Variants + +To build a specific variant for Android: + +**Default variant:** +```bash +npm run android:build:default +``` + +**Variant build:** +```bash +npm run android:build:variant +``` + +> **Note**: Each build command will temporarily copy the corresponding `.env.*` file to `.env` before building. + ## Production Releases For production Android releases, you'll need the actual `secrets.properties` file and keystore. Get these from the mobile team or 1Password. diff --git a/dapps/pos-app/app/_layout.tsx b/dapps/pos-app/app/_layout.tsx index 75884156..03f83f86 100644 --- a/dapps/pos-app/app/_layout.tsx +++ b/dapps/pos-app/app/_layout.tsx @@ -42,6 +42,7 @@ import { WagmiProvider } from "wagmi"; Sentry.init({ dsn: process.env.EXPO_PUBLIC_SENTRY_DSN, sendDefaultPii: false, + environment: process.env.EXPO_PUBLIC_VARIANT ?? "default", // Enable Logs enableLogs: false, diff --git a/dapps/pos-app/app/amount.tsx b/dapps/pos-app/app/amount.tsx index 05966ee1..d318d710 100644 --- a/dapps/pos-app/app/amount.tsx +++ b/dapps/pos-app/app/amount.tsx @@ -1,5 +1,6 @@ import { Button } from "@/components/button"; import { NumericKeyboard } from "@/components/numeric-keyboard"; +import { SecondaryLogo } from "@/components/secondary-logo"; import { ThemedText } from "@/components/themed-text"; import { BorderRadius, Spacing } from "@/constants/spacing"; import { useTheme } from "@/hooks/use-theme-color"; @@ -141,6 +142,7 @@ export default function AmountScreen() { {isValid ? `Charge $${formatAmount(watchAmount)}` : "Enter amount"} + ); } @@ -174,4 +176,7 @@ const styles = StyleSheet.create({ alignItems: "center", borderRadius: BorderRadius["5"], }, + secondaryLogo: { + marginTop: Spacing["spacing-5"], + }, }); diff --git a/dapps/pos-app/app/index.tsx b/dapps/pos-app/app/index.tsx index 1f10c383..aaee605f 100644 --- a/dapps/pos-app/app/index.tsx +++ b/dapps/pos-app/app/index.tsx @@ -1,4 +1,5 @@ import { Button } from "@/components/button"; +import { SecondaryLogo } from "@/components/secondary-logo"; import { ThemedText } from "@/components/themed-text"; import { BorderRadius, Spacing } from "@/constants/spacing"; import { useTheme } from "@/hooks/use-theme-color"; @@ -55,6 +56,7 @@ export default function HomeScreen() { /> Settings + ); } diff --git a/dapps/pos-app/app/payment-success.tsx b/dapps/pos-app/app/payment-success.tsx index 72058423..9d69b732 100644 --- a/dapps/pos-app/app/payment-success.tsx +++ b/dapps/pos-app/app/payment-success.tsx @@ -10,10 +10,12 @@ import Animated, { import { useSafeAreaInsets } from "react-native-safe-area-context"; import { Button } from "@/components/button"; +import { SecondaryLogo } from "@/components/secondary-logo"; import { ThemedText } from "@/components/themed-text"; import { BorderRadius, Spacing } from "@/constants/spacing"; import { useDisableBackButton } from "@/hooks/use-disable-back-button"; import { useTheme } from "@/hooks/use-theme-color"; +import { isVariant } from "@/utils/misc"; import { resetNavigation } from "@/utils/navigation"; import { StatusBar } from "expo-status-bar"; import { useColorScheme } from "@/hooks/use-color-scheme"; @@ -29,11 +31,14 @@ const finalScale = Math.ceil(diagonalLength / initialCircleSize) + 2; export default function PaymentSuccessScreen() { useDisableBackButton(); - const Theme = useTheme(); + const Theme = useTheme(isVariant() ? "light" : undefined); const params = useLocalSearchParams(); const { top } = useSafeAreaInsets(); const colorScheme = useColorScheme(); const { amount } = params; + const backgroundColor = isVariant() + ? Theme["polygon-payment-success"] + : Theme["bg-payment-success"]; const circleScale = useSharedValue(1); const contentOpacity = useSharedValue(0); @@ -65,7 +70,7 @@ export default function PaymentSuccessScreen() { style={[ styles.circle, { - backgroundColor: Theme["text-success"], + backgroundColor, width: initialCircleSize, height: initialCircleSize, borderRadius: initialCircleSize / 2, @@ -125,6 +130,10 @@ export default function PaymentSuccessScreen() { + @@ -176,4 +185,8 @@ const styles = StyleSheet.create({ fontSize: 18, lineHeight: 20, }, + secondaryLogo: { + marginTop: Spacing["spacing-5"], + alignSelf: "center", + }, }); diff --git a/dapps/pos-app/assets/images/polygon_logo.png b/dapps/pos-app/assets/images/polygon_logo.png new file mode 100644 index 00000000..a51d411e Binary files /dev/null and b/dapps/pos-app/assets/images/polygon_logo.png differ diff --git a/dapps/pos-app/components/secondary-logo.tsx b/dapps/pos-app/components/secondary-logo.tsx new file mode 100644 index 00000000..36aed9d6 --- /dev/null +++ b/dapps/pos-app/components/secondary-logo.tsx @@ -0,0 +1,34 @@ +import { Spacing } from "@/constants/spacing"; +import { useTheme } from "@/hooks/use-theme-color"; +import { isVariant } from "@/utils/misc"; +import { useAssets } from "expo-asset"; +import { Image } from "expo-image"; +import { ImageStyle, StyleProp, StyleSheet } from "react-native"; + +interface SecondaryLogoProps { + tintColor?: string; + style?: StyleProp; +} + +export function SecondaryLogo({ tintColor, style }: SecondaryLogoProps) { + const Theme = useTheme(); + const [assets] = useAssets([require("@/assets/images/polygon_logo.png")]); + const showLogo = isVariant(); + const _tintColor = tintColor ?? Theme["text-secondary"]; + + return showLogo ? ( + + ) : null; +} + +const styles = StyleSheet.create({ + logo: { + marginTop: Spacing["spacing-2"], + width: 92, + height: 18, + }, +}); diff --git a/dapps/pos-app/constants/theme.ts b/dapps/pos-app/constants/theme.ts index ed6ccd18..e6889880 100644 --- a/dapps/pos-app/constants/theme.ts +++ b/dapps/pos-app/constants/theme.ts @@ -32,6 +32,7 @@ export const Colors = { "bg-success": "rgba(48, 164, 107, 0.2)", "bg-error": "rgba(223, 74, 52, 0.2)", "bg-warning": "rgba(243, 161, 63, 0.2)", + "bg-payment-success": "#30A46B", // Text colors "text-primary": "#202020", @@ -52,6 +53,10 @@ export const Colors = { "border-success": "#30A46B", "border-error": "#DF4A34", "border-warning": "#F3A13F", + + // Custom colors + "polygon-payment-success": "#6C00F6", + "polygon-payment-success-header": "#FFFFFF", }, dark: { // Foreground colors @@ -84,6 +89,7 @@ export const Colors = { "bg-success": "rgba(48, 164, 107, 0.2)", "bg-error": "rgba(223, 74, 52, 0.2)", "bg-warning": "rgba(243, 161, 63, 0.2)", + "bg-payment-success": "#30A46B", // Text colors "text-primary": "#FFFFFF", @@ -104,6 +110,10 @@ export const Colors = { "border-success": "#30A46B", "border-error": "#DF4A34", "border-warning": "#F3A13F", + + // Custom colors + "polygon-payment-success": "#6C00F6", + "polygon-payment-success-header": "#FFFFFF", }, }; diff --git a/dapps/pos-app/package.json b/dapps/pos-app/package.json index e9c0563f..08d24d04 100644 --- a/dapps/pos-app/package.json +++ b/dapps/pos-app/package.json @@ -7,6 +7,8 @@ "android": "expo run:android", "android:pos": "expo run:android --port 8082", "android:build": "cd android && ./gradlew assembleRelease", + "android:build:default": "[ -f .env.default ] && cp .env.default .env && npm run android:build || (echo '.env.default not found. Please create it before running this script.' && exit 1)", + "android:build:variant": "[ -f .env.variant ] && cp .env.variant .env && npm run android:build || (echo '.env.variant not found. Please create it before running this script.' && exit 1)", "prebuild": "expo prebuild", "postprebuild": "node scripts/setup-secrets.js", "postinstall": "patch-package", diff --git a/dapps/pos-app/utils/misc.ts b/dapps/pos-app/utils/misc.ts index 98e51798..2c33c752 100644 --- a/dapps/pos-app/utils/misc.ts +++ b/dapps/pos-app/utils/misc.ts @@ -8,3 +8,7 @@ export const getDeviceIdentifier = async () => { return "unknown"; } }; + +export const isVariant = () => { + return process.env.EXPO_PUBLIC_VARIANT === "polygon" || false; +}; diff --git a/dapps/pos-app/utils/navigation.ts b/dapps/pos-app/utils/navigation.ts index a14f6155..99515dc8 100644 --- a/dapps/pos-app/utils/navigation.ts +++ b/dapps/pos-app/utils/navigation.ts @@ -1,5 +1,6 @@ import { Colors } from "@/constants/theme"; import { Href, router } from "expo-router"; +import { isVariant } from "./misc"; export const shouldCenterHeaderTitle = (routeName: string) => { return routeName === "index" || routeName === "payment-success"; @@ -8,13 +9,20 @@ export const shouldCenterHeaderTitle = (routeName: string) => { export const getHeaderBackgroundColor = ( routeName: string, ): keyof typeof Colors.light | keyof typeof Colors.dark => { - return routeName === "payment-success" ? "text-success" : "bg-primary"; + if (routeName === "payment-success") { + return isVariant() ? "polygon-payment-success" : "text-success"; + } + return "bg-primary"; }; export const getHeaderTintColor = ( routeName: string, ): keyof typeof Colors.light | keyof typeof Colors.dark => { - return routeName === "payment-success" ? "text-invert" : "text-primary"; + if (routeName === "payment-success") { + return isVariant() ? "polygon-payment-success-header" : "text-invert"; + } + + return "text-primary"; }; export const resetNavigation = (href?: Href) => {