Ver código fonte

- android permissions
- rebrand script
- readme file
- better thme handling
- SVG back button

Alex Trofimov 10 meses atrás
pai
commit
4fa8cac1d5

+ 19 - 18
README.md

@@ -1,6 +1,7 @@
-# Welcome to your Expo app 👋
+# UNACMS Mobile Apps 👋
 
-This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
+This is source code of UNA mobile apps for iOS and Android based on [Expo](https://expo.dev).
+Mobile apps need to have [Nexus](https://unacms.com/view-product/nexus) UNA app installed.
 
 ## Get started
 
@@ -10,20 +11,27 @@ This is an [Expo](https://expo.dev) project created with [`create-expo-app`](htt
    npm install
    ```
 
-2. Start the app
+2. Create native iOS and Android project files
 
    ```bash
-   npx expo start
+   npx expo prebuild
    ```
 
-In the output, you'll find options to open the app in a
+3. Run iOS app
 
-- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
-- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
-- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
-- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
+   ```bash
+   npm run ios
+   ```
+   or Android app
+   ```bash
+   npm run android
+   ```
 
-You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
+## Rebranding
+
+- Edit `constants/setings.ts` to specify your domain and other settings.  
+- Edit `constants/theme.ts` to specify custom colors.
+- Edit images in `assets/images` to change app icons and app name.
 
 ## Get a fresh project
 
@@ -37,14 +45,7 @@ This command will move the starter code to the **app-example** directory and cre
 
 ## Learn more
 
-To learn more about developing your project with Expo, look at the following resources:
+To learn more about extending this project with Expo, look at the following resources:
 
 - [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
 - [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
-
-## Join the community
-
-Join our community of developers creating universal apps.
-
-- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
-- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.

+ 7 - 2
app.json

@@ -1,7 +1,7 @@
 {
   "expo": {
-    "name": "una-mobile-app",
-    "slug": "una-mobile-app",
+    "name": "UNACMS",
+    "slug": "unacms",
     "version": "3.0.0",
     "orientation": "portrait",
     "icon": "./assets/images/icon.png",
@@ -30,6 +30,11 @@
         "backgroundImage": "./assets/images/android-icon-background.png",
         "monochromeImage": "./assets/images/android-icon-monochrome.png"
       },
+      "permissions": [
+        "CAMERA",
+        "ACCESS_FINE_LOCATION",
+        "ACCESS_COARSE_LOCATION"
+      ],
       "edgeToEdgeEnabled": true,
       "predictiveBackGestureEnabled": false
     },

+ 1 - 6
components/bottom-tabs.tsx

@@ -14,7 +14,6 @@ type Props = {
 
 export default function BottomTabs({ webViewRef, data }: Props) {
   const iconColor = useThemeColor({}, 'icon');
-  const borderColor = useThemeColor({}, 'border');
 
   const runFuncJS = (js: string) => {
     webViewRef.current?.injectJavaScript(`
@@ -42,7 +41,7 @@ export default function BottomTabs({ webViewRef, data }: Props) {
   };
 
   return (
-    <ThemedView style={[{ borderColor }, styles.container]}>
+    <ThemedView style={styles.container}>
 
       <HapticTab onPress={() => runFuncJS("bx_mobile_apps_show_main_menu")}>
         <Bars size={24} color={iconColor} />
@@ -82,10 +81,6 @@ const styles = StyleSheet.create({
     justifyContent: "space-around",
     alignItems: "center",
     borderTopWidth: 1,
-    /*
-    borderColor: "#ddd",
-    backgroundColor: "#fff",
-    */
   },
   tab: {
     justifyContent: "center",

+ 10 - 5
components/header.tsx

@@ -2,6 +2,7 @@ import { ThemedText } from '@/components/themed-text';
 import { ThemedView } from '@/components/themed-view';
 import { useThemeColor } from '@/hooks/use-theme-color';
 import { ActivityIndicator, Pressable, StyleSheet } from "react-native";
+import { Back } from "./icons";
 
 type Props = {
   title: string;
@@ -11,17 +12,21 @@ type Props = {
 };
 
 export default function Header({ title, loading, canGoBack, onBack }: Props) {
-  const borderColor = useThemeColor({}, 'border');
+  let iconColor = useThemeColor({}, 'icon');
+  let iconColorDisabled = useThemeColor({}, 'iconDisabled');
+  if (!canGoBack)
+     iconColor = iconColorDisabled;
+
   return (
-    <ThemedView style={[{ borderColor }, styles.container]}>
+    <ThemedView style={styles.container}>
       <Pressable
         onPress={onBack}
         disabled={!canGoBack}
         style={styles.back}
       >
-        <ThemedText style={[styles.backText, !canGoBack && styles.disabled]}>
-          ←
-        </ThemedText>
+        <ThemedView style={styles.backText}>
+          <Back size={24} color={iconColor} />
+        </ThemedView>
       </Pressable>
 
       <ThemedText numberOfLines={1} style={styles.title}>

+ 5 - 3
components/themed-view.tsx

@@ -4,10 +4,12 @@ import { View, type ViewProps } from 'react-native';
 export type ThemedViewProps = ViewProps & {
   lightColor?: string;
   darkColor?: string;
+  lightBgColor?: string;
+  darkBgColor?: string;
 };
 
-export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
+export function ThemedView({ style, lightColor, darkColor, lightBgColor, darkBgColor, ...otherProps }: ThemedViewProps) {
   const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
-
-  return <View style={[{ backgroundColor }, style]} {...otherProps} />;
+  const borderColor = useThemeColor({ light: lightBgColor, dark: darkBgColor }, 'border');
+  return <View style={[{ backgroundColor, borderColor }, style]} {...otherProps} />;
 }

+ 1 - 1
constants/settings.ts

@@ -1,6 +1,6 @@
 
 // base URL for the UNA mobile app
-export const UNA_URL = "http://hihi.com/una";
+export const UNA_URL = "https://ci.una.io/test";
 
 // OneSignal App ID for push notifications
 export const ONESIGNAL_APP_ID = "";

+ 2 - 1
constants/theme.ts

@@ -1,6 +1,5 @@
 /**
  * Below are the colors that are used in the app. The colors are defined in the light and dark mode.
- * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
  */
 
 import { Platform } from 'react-native';
@@ -14,6 +13,7 @@ export const Colors = {
     background: '#fff',
     tint: tintColorLight,
     icon: '#687076',
+    iconDisabled: '#ccc',
     tabIconDefault: '#687076',
     tabIconSselected: tintColorLight,
     border: "#ddd",
@@ -23,6 +23,7 @@ export const Colors = {
     background: '#1f2837',
     tint: tintColorDark,
     icon: '#9BA1A6',
+    iconDisabled: '#333',
     tabIconDefault: '#9BA1A6',
     tabIconSelected: tintColorDark,
     border: "#36404e",

+ 60 - 0
scripts/rebrand.js

@@ -0,0 +1,60 @@
+#!/usr/bin/env node
+
+/**
+ * This script is used to rebrand the app to a different UNACMS website.
+ */
+
+const fs = require("fs");
+const path = require("path");
+const readline = require("readline");
+
+const root = process.cwd();
+const settingsPath = path.join(root, "constants", "settings.ts");
+
+const rl = readline.createInterface({
+  input: process.stdin,
+  output: process.stdout,
+});
+
+rl.question(
+  "Your UNA site URL: ",
+  (answer) => {
+    const userInput = answer.trim().replace(/\/$/, "").toLowerCase();
+
+    // Simple URL validation (domain or https://...)
+    const urlPattern = /^(https?:\/\/)?([a-z0-9-]+\.)+[a-z]{2,}$/i;
+    if (!userInput || !urlPattern.test(userInput)) {
+      console.log("❌ Invalid input. Please enter a valid URL.");
+      rl.close();
+      return;
+    }
+
+    const newUrl = userInput.startsWith("http")
+      ? userInput
+      : `https://${userInput}`;
+
+    // Read settings.ts
+    fs.readFile(settingsPath, "utf8", (err, data) => {
+      if (err) {
+        console.error(`❌ Failed to read settings.ts: ${err.message}`);
+        rl.close();
+        return;
+      }
+
+      // Replace the UNA_URL line
+      const updated = data.replace(
+        /export const UNA_URL\s*=\s*["'`].*?["'`];/,
+        `export const UNA_URL = "${newUrl}";`
+      );
+
+      fs.writeFile(settingsPath, updated, "utf8", (err) => {
+        if (err) {
+          console.error(`❌ Failed to write settings.ts: ${err.message}`);
+        } else {
+          console.log(`✅ UNA_URL updated to: ${newUrl}`);
+        }
+        rl.close();
+      });
+    });
+  }
+);

+ 0 - 112
scripts/reset-project.js

@@ -1,112 +0,0 @@
-#!/usr/bin/env node
-
-/**
- * This script is used to reset the project to a blank state.
- * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file.
- * You can remove the `reset-project` script from package.json and safely delete this file after running it.
- */
-
-const fs = require("fs");
-const path = require("path");
-const readline = require("readline");
-
-const root = process.cwd();
-const oldDirs = ["app", "components", "hooks", "constants", "scripts"];
-const exampleDir = "app-example";
-const newAppDir = "app";
-const exampleDirPath = path.join(root, exampleDir);
-
-const indexContent = `import { Text, View } from "react-native";
-
-export default function Index() {
-  return (
-    <View
-      style={{
-        flex: 1,
-        justifyContent: "center",
-        alignItems: "center",
-      }}
-    >
-      <Text>Edit app/index.tsx to edit this screen.</Text>
-    </View>
-  );
-}
-`;
-
-const layoutContent = `import { Stack } from "expo-router";
-
-export default function RootLayout() {
-  return <Stack />;
-}
-`;
-
-const rl = readline.createInterface({
-  input: process.stdin,
-  output: process.stdout,
-});
-
-const moveDirectories = async (userInput) => {
-  try {
-    if (userInput === "y") {
-      // Create the app-example directory
-      await fs.promises.mkdir(exampleDirPath, { recursive: true });
-      console.log(`📁 /${exampleDir} directory created.`);
-    }
-
-    // Move old directories to new app-example directory or delete them
-    for (const dir of oldDirs) {
-      const oldDirPath = path.join(root, dir);
-      if (fs.existsSync(oldDirPath)) {
-        if (userInput === "y") {
-          const newDirPath = path.join(root, exampleDir, dir);
-          await fs.promises.rename(oldDirPath, newDirPath);
-          console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`);
-        } else {
-          await fs.promises.rm(oldDirPath, { recursive: true, force: true });
-          console.log(`❌ /${dir} deleted.`);
-        }
-      } else {
-        console.log(`➡️ /${dir} does not exist, skipping.`);
-      }
-    }
-
-    // Create new /app directory
-    const newAppDirPath = path.join(root, newAppDir);
-    await fs.promises.mkdir(newAppDirPath, { recursive: true });
-    console.log("\n📁 New /app directory created.");
-
-    // Create index.tsx
-    const indexPath = path.join(newAppDirPath, "index.tsx");
-    await fs.promises.writeFile(indexPath, indexContent);
-    console.log("📄 app/index.tsx created.");
-
-    // Create _layout.tsx
-    const layoutPath = path.join(newAppDirPath, "_layout.tsx");
-    await fs.promises.writeFile(layoutPath, layoutContent);
-    console.log("📄 app/_layout.tsx created.");
-
-    console.log("\n✅ Project reset complete. Next steps:");
-    console.log(
-      `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${
-        userInput === "y"
-          ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.`
-          : ""
-      }`
-    );
-  } catch (error) {
-    console.error(`❌ Error during script execution: ${error.message}`);
-  }
-};
-
-rl.question(
-  "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ",
-  (answer) => {
-    const userInput = answer.trim().toLowerCase() || "y";
-    if (userInput === "y" || userInput === "n") {
-      moveDirectories(userInput).finally(() => rl.close());
-    } else {
-      console.log("❌ Invalid input. Please enter 'Y' or 'N'.");
-      rl.close();
-    }
-  }
-);

+ 1 - 2
utils/android-permissins-request.ts

@@ -9,9 +9,8 @@ export async function requestAndroidPermissions() {
     try {
         const permissions = [
             PermissionsAndroid.PERMISSIONS.CAMERA,
-            PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
             PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
-            PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
+            PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION
         ];
 
         const granted = await PermissionsAndroid.requestMultiple(permissions);