update
@@ -0,0 +1,76 @@
|
||||
node_modules/
|
||||
.expo/
|
||||
dist/
|
||||
npm-debug.*
|
||||
*.jks
|
||||
*.p8
|
||||
*.p12
|
||||
*.key
|
||||
*.mobileprovision
|
||||
*.orig.*
|
||||
web-build/
|
||||
|
||||
# macOS
|
||||
.DS_Store
|
||||
|
||||
# @generated expo-cli sync-b25d41054229aa64f1468014f243adfae8268af2
|
||||
# The following patterns were generated by expo-cli
|
||||
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
.cxx/
|
||||
|
||||
# node.js
|
||||
#
|
||||
node_modules/
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# BUCK
|
||||
buck-out/
|
||||
\.buckd/
|
||||
*.keystore
|
||||
!debug.keystore
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
|
||||
# CocoaPods
|
||||
/ios/Pods/
|
||||
|
||||
# Expo
|
||||
.expo/
|
||||
web-build/
|
||||
dist/
|
||||
|
||||
# @end expo-cli
|
||||
@@ -0,0 +1,27 @@
|
||||
*.jks
|
||||
*.key
|
||||
*.mobileprovision
|
||||
*.p12
|
||||
*.p8
|
||||
.DS_Store
|
||||
.expo
|
||||
.npmignore
|
||||
.vscode
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
yarn-error.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
build/
|
||||
web-build/
|
||||
HelloWorld.xcworkspace
|
||||
Podfile.lock
|
||||
ios/Pods
|
||||
ios/.xcode.env.local
|
||||
android/.build
|
||||
android/app/build
|
||||
android/.gradle
|
||||
ios/.xcode.env.local
|
||||
|
||||
# Exclude tarballs generated by `npm pack`
|
||||
/*.tgz
|
||||
@@ -0,0 +1,139 @@
|
||||
import React, {
|
||||
useState,
|
||||
useCallback,
|
||||
useEffect,
|
||||
setGlobal,
|
||||
useGlobal,
|
||||
} from "reactn";
|
||||
import { GestureHandlerRootView } from "react-native-gesture-handler";
|
||||
import { useFonts } from "expo-font";
|
||||
import * as SplashScreen from "expo-splash-screen";
|
||||
import { StatusBar } from "expo-status-bar";
|
||||
import { NavigationContainer, DefaultTheme } from "@react-navigation/native";
|
||||
import moment from "moment";
|
||||
import "moment/locale/fr";
|
||||
import "@expo/metro-runtime";
|
||||
import { PortalProvider } from "@gorhom/portal";
|
||||
|
||||
import { MainStack, Routes } from "./src/navigation";
|
||||
import { Palette } from "./src/styles";
|
||||
|
||||
import { navigationRef, reset } from "./src/navigation/NavigationService";
|
||||
|
||||
import initialGlobalState from "./src/config/initialGlobalState";
|
||||
|
||||
import firebase from "./src/config/firebase";
|
||||
import Providers from "./src/providers";
|
||||
import AppLayout from "./src/layouts/AppLayout";
|
||||
import {
|
||||
Inter_400Regular,
|
||||
Inter_500Medium,
|
||||
Inter_600SemiBold,
|
||||
Inter_700Bold,
|
||||
} from "@expo-google-fonts/inter";
|
||||
|
||||
console.disableYellowBox = true;
|
||||
console.reportErrorsAsExceptions = false;
|
||||
|
||||
moment.locale("fr");
|
||||
|
||||
setGlobal(initialGlobalState);
|
||||
|
||||
SplashScreen.preventAutoHideAsync();
|
||||
|
||||
const App = () => {
|
||||
const [currentUID, setCurrentUID] = useGlobal("currentUID");
|
||||
const [, setCurrentUserRoles] = useGlobal("currentUserRoles");
|
||||
|
||||
const [appIsReady, setAppIsReady] = useState(false);
|
||||
|
||||
const [isInitializing, setInitializing] = useState(true);
|
||||
|
||||
const [loaded] = useFonts({
|
||||
NewYorkSemibold: require("./src/assets/fonts/NewYork-Semibold.ttf"),
|
||||
OpenSansRegular: require("./src/assets/fonts/OpenSans-Regular.ttf"),
|
||||
InterRegular: Inter_400Regular,
|
||||
InterMedium: Inter_500Medium,
|
||||
InterSemiBold: Inter_600SemiBold,
|
||||
InterBold: Inter_700Bold,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (loaded) {
|
||||
setAppIsReady(true);
|
||||
}
|
||||
}, [loaded]);
|
||||
|
||||
const onLayoutRootView = useCallback(async () => {
|
||||
if (appIsReady) {
|
||||
await SplashScreen.hideAsync();
|
||||
}
|
||||
}, [appIsReady, loaded]);
|
||||
|
||||
useEffect(() => {
|
||||
const subscriber = firebase.auth().onAuthStateChanged(onAuthStateChanged);
|
||||
return subscriber;
|
||||
}, []);
|
||||
|
||||
const onAuthStateChanged = async (user) => {
|
||||
if (isInitializing) {
|
||||
setInitializing(false);
|
||||
}
|
||||
|
||||
if (user?.uid) {
|
||||
setCurrentUID(user.uid);
|
||||
|
||||
const idTokenResult = await user.getIdTokenResult();
|
||||
setCurrentUserRoles(idTokenResult?.claims?.roles || []);
|
||||
} else {
|
||||
setCurrentUID(null);
|
||||
setGlobal(initialGlobalState);
|
||||
reset({
|
||||
index: 0,
|
||||
routes: [{ name: Routes.Splash }],
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (!loaded || isInitializing) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<StatusBar style="light" />
|
||||
|
||||
<GestureHandlerRootView
|
||||
onLayout={onLayoutRootView}
|
||||
style={[{ flex: 1, backgroundColor: Palette.darkPurple }]}
|
||||
>
|
||||
<PortalProvider>
|
||||
<Providers>
|
||||
<AppLayout currentUID={currentUID}>
|
||||
<NavigationContainer
|
||||
theme={{
|
||||
...DefaultTheme,
|
||||
colors: {
|
||||
...DefaultTheme.colors,
|
||||
background: Palette.darkPurple,
|
||||
},
|
||||
}}
|
||||
ref={navigationRef}
|
||||
documentTitle={{
|
||||
formatter: (options) =>
|
||||
options?.title
|
||||
? `${options?.title} - minuit.starter`
|
||||
: "minuit.starter",
|
||||
}}
|
||||
>
|
||||
<MainStack />
|
||||
</NavigationContainer>
|
||||
</AppLayout>
|
||||
</Providers>
|
||||
</PortalProvider>
|
||||
</GestureHandlerRootView>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
@@ -0,0 +1,16 @@
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Android/IntelliJ
|
||||
#
|
||||
build/
|
||||
.idea
|
||||
.gradle
|
||||
local.properties
|
||||
*.iml
|
||||
*.hprof
|
||||
.cxx/
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
@@ -0,0 +1,179 @@
|
||||
apply plugin: "com.android.application"
|
||||
apply plugin: "org.jetbrains.kotlin.android"
|
||||
apply plugin: "com.facebook.react"
|
||||
|
||||
def projectRoot = rootDir.getAbsoluteFile().getParentFile().getAbsolutePath()
|
||||
|
||||
/**
|
||||
* This is the configuration block to customize your React Native Android app.
|
||||
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
||||
*/
|
||||
react {
|
||||
entryFile = file(["node", "-e", "require('expo/scripts/resolveAppEntry')", projectRoot, "android", "absolute"].execute(null, rootDir).text.trim())
|
||||
reactNativeDir = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
||||
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc"
|
||||
codegenDir = new File(["node", "--print", "require.resolve('@react-native/codegen/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().getAbsoluteFile()
|
||||
|
||||
// Use Expo CLI to bundle the app, this ensures the Metro config
|
||||
// works correctly with Expo projects.
|
||||
cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
|
||||
bundleCommand = "export:embed"
|
||||
|
||||
/* Folders */
|
||||
// The root of your project, i.e. where "package.json" lives. Default is '../..'
|
||||
// root = file("../../")
|
||||
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
|
||||
// reactNativeDir = file("../../node_modules/react-native")
|
||||
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
|
||||
// codegenDir = file("../../node_modules/@react-native/codegen")
|
||||
|
||||
/* Variants */
|
||||
// The list of variants to that are debuggable. For those we're going to
|
||||
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
||||
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
||||
// debuggableVariants = ["liteDebug", "prodDebug"]
|
||||
|
||||
/* Bundling */
|
||||
// A list containing the node command and its flags. Default is just 'node'.
|
||||
// nodeExecutableAndArgs = ["node"]
|
||||
|
||||
//
|
||||
// The path to the CLI configuration file. Default is empty.
|
||||
// bundleConfig = file(../rn-cli.config.js)
|
||||
//
|
||||
// The name of the generated asset file containing your JS bundle
|
||||
// bundleAssetName = "MyApplication.android.bundle"
|
||||
//
|
||||
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
||||
// entryFile = file("../js/MyApplication.android.js")
|
||||
//
|
||||
// A list of extra flags to pass to the 'bundle' commands.
|
||||
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
||||
// extraPackagerArgs = []
|
||||
|
||||
/* Hermes Commands */
|
||||
// The hermes compiler command to run. By default it is 'hermesc'
|
||||
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
||||
//
|
||||
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||
// hermesFlags = ["-O", "-output-source-map"]
|
||||
|
||||
/* Autolinking */
|
||||
autolinkLibrariesWithApp()
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
||||
*/
|
||||
def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInReleaseBuilds') ?: false).toBoolean()
|
||||
|
||||
/**
|
||||
* The preferred build flavor of JavaScriptCore (JSC)
|
||||
*
|
||||
* For example, to use the international variant, you can use:
|
||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||
*
|
||||
* The international variant includes ICU i18n library and necessary data
|
||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||
* give correct results when using with locales other than en-US. Note that
|
||||
* this variant is about 6MiB larger per architecture than default.
|
||||
*/
|
||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||
|
||||
android {
|
||||
ndkVersion rootProject.ext.ndkVersion
|
||||
|
||||
buildToolsVersion rootProject.ext.buildToolsVersion
|
||||
compileSdk rootProject.ext.compileSdkVersion
|
||||
|
||||
namespace 'com.minuit.starter'
|
||||
defaultConfig {
|
||||
applicationId 'com.minuit.starter'
|
||||
minSdkVersion rootProject.ext.minSdkVersion
|
||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||
versionCode 1
|
||||
versionName "2024.04.1"
|
||||
}
|
||||
signingConfigs {
|
||||
debug {
|
||||
storeFile file('debug.keystore')
|
||||
storePassword 'android'
|
||||
keyAlias 'androiddebugkey'
|
||||
keyPassword 'android'
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
debug {
|
||||
signingConfig signingConfigs.debug
|
||||
}
|
||||
release {
|
||||
// Caution! In production, you need to generate your own keystore file.
|
||||
// see https://reactnative.dev/docs/signed-apk-android.
|
||||
signingConfig signingConfigs.debug
|
||||
shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
|
||||
minifyEnabled enableProguardInReleaseBuilds
|
||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||
crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true)
|
||||
}
|
||||
}
|
||||
packagingOptions {
|
||||
jniLibs {
|
||||
useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false)
|
||||
}
|
||||
}
|
||||
androidResources {
|
||||
ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:!CVS:!thumbs.db:!picasa.ini:!*~'
|
||||
}
|
||||
}
|
||||
|
||||
// Apply static values from `gradle.properties` to the `android.packagingOptions`
|
||||
// Accepts values in comma delimited lists, example:
|
||||
// android.packagingOptions.pickFirsts=/LICENSE,**/picasa.ini
|
||||
["pickFirsts", "excludes", "merges", "doNotStrip"].each { prop ->
|
||||
// Split option: 'foo,bar' -> ['foo', 'bar']
|
||||
def options = (findProperty("android.packagingOptions.$prop") ?: "").split(",");
|
||||
// Trim all elements in place.
|
||||
for (i in 0..<options.size()) options[i] = options[i].trim();
|
||||
// `[] - ""` is essentially `[""].filter(Boolean)` removing all empty strings.
|
||||
options -= ""
|
||||
|
||||
if (options.length > 0) {
|
||||
println "android.packagingOptions.$prop += $options ($options.length)"
|
||||
// Ex: android.packagingOptions.pickFirsts += '**/SCCS/**'
|
||||
options.each {
|
||||
android.packagingOptions[prop] += it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// The version of react-native is set by the React Native Gradle Plugin
|
||||
implementation("com.facebook.react:react-android")
|
||||
|
||||
def isGifEnabled = (findProperty('expo.gif.enabled') ?: "") == "true";
|
||||
def isWebpEnabled = (findProperty('expo.webp.enabled') ?: "") == "true";
|
||||
def isWebpAnimatedEnabled = (findProperty('expo.webp.animated') ?: "") == "true";
|
||||
|
||||
if (isGifEnabled) {
|
||||
// For animated gif support
|
||||
implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}")
|
||||
}
|
||||
|
||||
if (isWebpEnabled) {
|
||||
// For webp support
|
||||
implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}")
|
||||
if (isWebpAnimatedEnabled) {
|
||||
// Animated webp support
|
||||
implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}")
|
||||
}
|
||||
}
|
||||
|
||||
if (hermesEnabled.toBoolean()) {
|
||||
implementation("com.facebook.react:hermes-android")
|
||||
} else {
|
||||
implementation jscFlavor
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
apply plugin: 'com.google.firebase.crashlytics'
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "943006074419",
|
||||
"project_id": "minuitcloud",
|
||||
"storage_bucket": "minuitcloud.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:943006074419:android:1cf962aa044ea020658546",
|
||||
"android_client_info": {
|
||||
"package_name": "com.minuit.starter"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "943006074419-bal1qb28ssgh0v2ta1fhj1vl8a9mb7tp.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyA13RHqqFcx0tu6qja0shDomazoOk7wBw0"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "943006074419-bal1qb28ssgh0v2ta1fhj1vl8a9mb7tp.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
},
|
||||
{
|
||||
"client_id": "943006074419-h43c7p48nhn5f4td4oimv4dq7b1p3s29.apps.googleusercontent.com",
|
||||
"client_type": 2,
|
||||
"ios_info": {
|
||||
"bundle_id": "com.minuit.starter",
|
||||
"app_store_id": "1661696886"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# react-native-reanimated
|
||||
-keep class com.swmansion.reanimated.** { *; }
|
||||
-keep class com.facebook.react.turbomodule.** { *; }
|
||||
|
||||
# Add any project specific keep options here:
|
||||
@@ -0,0 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
|
||||
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:usesCleartextTraffic" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,37 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools">
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="https"/>
|
||||
</intent>
|
||||
</queries>
|
||||
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true">
|
||||
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
|
||||
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
|
||||
<activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode" android:launchMode="singleTask" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.App.SplashScreen" android:exported="true" android:screenOrientation="portrait">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="minuit"/>
|
||||
<data android:scheme="com.minuit.starter"/>
|
||||
<data android:scheme="exp+minuitstarter"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
</application>
|
||||
</manifest>
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.minuit.starter
|
||||
import expo.modules.splashscreen.SplashScreenManager
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
|
||||
import com.facebook.react.ReactActivity
|
||||
import com.facebook.react.ReactActivityDelegate
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled
|
||||
import com.facebook.react.defaults.DefaultReactActivityDelegate
|
||||
|
||||
import expo.modules.ReactActivityDelegateWrapper
|
||||
|
||||
class MainActivity : ReactActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// Set the theme to AppTheme BEFORE onCreate to support
|
||||
// coloring the background, status bar, and navigation bar.
|
||||
// This is required for expo-splash-screen.
|
||||
// setTheme(R.style.AppTheme);
|
||||
// @generated begin expo-splashscreen - expo prebuild (DO NOT MODIFY) sync-f3ff59a738c56c9a6119210cb55f0b613eb8b6af
|
||||
SplashScreenManager.registerOnActivity(this)
|
||||
// @generated end expo-splashscreen
|
||||
super.onCreate(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the main component registered from JavaScript. This is used to schedule
|
||||
* rendering of the component.
|
||||
*/
|
||||
override fun getMainComponentName(): String = "main"
|
||||
|
||||
/**
|
||||
* Returns the instance of the [ReactActivityDelegate]. We use [DefaultReactActivityDelegate]
|
||||
* which allows you to enable New Architecture with a single boolean flags [fabricEnabled]
|
||||
*/
|
||||
override fun createReactActivityDelegate(): ReactActivityDelegate {
|
||||
return ReactActivityDelegateWrapper(
|
||||
this,
|
||||
BuildConfig.IS_NEW_ARCHITECTURE_ENABLED,
|
||||
object : DefaultReactActivityDelegate(
|
||||
this,
|
||||
mainComponentName,
|
||||
fabricEnabled
|
||||
){})
|
||||
}
|
||||
|
||||
/**
|
||||
* Align the back button behavior with Android S
|
||||
* where moving root activities to background instead of finishing activities.
|
||||
* @see <a href="https://developer.android.com/reference/android/app/Activity#onBackPressed()">onBackPressed</a>
|
||||
*/
|
||||
override fun invokeDefaultOnBackPressed() {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R) {
|
||||
if (!moveTaskToBack(false)) {
|
||||
// For non-root activities, use the default implementation to finish them.
|
||||
super.invokeDefaultOnBackPressed()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Use the default back button implementation on Android S
|
||||
// because it's doing more than [Activity.moveTaskToBack] in fact.
|
||||
super.invokeDefaultOnBackPressed()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.minuit.starter
|
||||
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
|
||||
import com.facebook.react.PackageList
|
||||
import com.facebook.react.ReactApplication
|
||||
import com.facebook.react.ReactNativeHost
|
||||
import com.facebook.react.ReactPackage
|
||||
import com.facebook.react.ReactHost
|
||||
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
|
||||
import com.facebook.react.defaults.DefaultReactNativeHost
|
||||
import com.facebook.react.soloader.OpenSourceMergedSoMapping
|
||||
import com.facebook.soloader.SoLoader
|
||||
|
||||
import expo.modules.ApplicationLifecycleDispatcher
|
||||
import expo.modules.ReactNativeHostWrapper
|
||||
|
||||
class MainApplication : Application(), ReactApplication {
|
||||
|
||||
override val reactNativeHost: ReactNativeHost = ReactNativeHostWrapper(
|
||||
this,
|
||||
object : DefaultReactNativeHost(this) {
|
||||
override fun getPackages(): List<ReactPackage> {
|
||||
val packages = PackageList(this).packages
|
||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||
// packages.add(new MyReactNativePackage());
|
||||
return packages
|
||||
}
|
||||
|
||||
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
|
||||
|
||||
override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG
|
||||
|
||||
override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
|
||||
override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
|
||||
}
|
||||
)
|
||||
|
||||
override val reactHost: ReactHost
|
||||
get() = ReactNativeHostWrapper.createReactHost(applicationContext, reactNativeHost)
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
SoLoader.init(this, OpenSourceMergedSoMapping)
|
||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||
load()
|
||||
}
|
||||
ApplicationLifecycleDispatcher.onApplicationCreate(this)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(newConfig: Configuration) {
|
||||
super.onConfigurationChanged(newConfig)
|
||||
ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig)
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,6 @@
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@color/splashscreen_background"/>
|
||||
<item>
|
||||
<bitmap android:gravity="center" android:src="@drawable/splashscreen_logo"/>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2014 The Android Open Source Project
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<inset xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:insetLeft="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetRight="@dimen/abc_edit_text_inset_horizontal_material"
|
||||
android:insetTop="@dimen/abc_edit_text_inset_top_material"
|
||||
android:insetBottom="@dimen/abc_edit_text_inset_bottom_material"
|
||||
>
|
||||
|
||||
<selector>
|
||||
<!--
|
||||
This file is a copy of abc_edit_text_material (https://bit.ly/3k8fX7I).
|
||||
The item below with state_pressed="false" and state_focused="false" causes a NullPointerException.
|
||||
NullPointerException:tempt to invoke virtual method 'android.graphics.drawable.Drawable android.graphics.drawable.Drawable$ConstantState.newDrawable(android.content.res.Resources)'
|
||||
|
||||
<item android:state_pressed="false" android:state_focused="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
|
||||
For more info, see https://bit.ly/3CdLStv (react-native/pull/29452) and https://bit.ly/3nxOMoR.
|
||||
-->
|
||||
<item android:state_enabled="false" android:drawable="@drawable/abc_textfield_default_mtrl_alpha"/>
|
||||
<item android:drawable="@drawable/abc_textfield_activated_mtrl_alpha"/>
|
||||
</selector>
|
||||
|
||||
</inset>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/iconBackground"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/iconBackground"/>
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
</adaptive-icon>
|
||||
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 7.8 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 7.2 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 8.9 KiB |
|
After Width: | Height: | Size: 9.9 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1 @@
|
||||
<resources/>
|
||||
@@ -0,0 +1,7 @@
|
||||
<resources>
|
||||
<color name="splashscreen_background">#0F0C14</color>
|
||||
<color name="iconBackground">#FFFFFF</color>
|
||||
<color name="colorPrimary">#023c69</color>
|
||||
<color name="colorPrimaryDark">#0F0C14</color>
|
||||
<color name="activityBackground">#0F0C14</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,5 @@
|
||||
<resources>
|
||||
<string name="app_name">minuit.starter</string>
|
||||
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
|
||||
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,20 @@
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
<item name="android:editTextStyle">@style/ResetEditText</item>
|
||||
<item name="android:editTextBackground">@drawable/rn_edit_text_material</item>
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="android:statusBarColor">#0F0C14</item>
|
||||
<item name="android:windowBackground">@color/activityBackground</item>
|
||||
</style>
|
||||
<style name="ResetEditText" parent="@android:style/Widget.EditText">
|
||||
<item name="android:padding">0dp</item>
|
||||
<item name="android:textColorHint">#c8c8c8</item>
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
|
||||
<item name="windowSplashScreenBackground">@color/splashscreen_background</item>
|
||||
<item name="android:windowBackground">@drawable/splashscreen_logo</item>
|
||||
<item name="postSplashScreenTheme">@style/AppTheme</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -0,0 +1,43 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '35.0.0'
|
||||
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '24')
|
||||
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '35')
|
||||
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34')
|
||||
kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.25'
|
||||
|
||||
ndkVersion = "26.1.10909125"
|
||||
}
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:3.0.2'
|
||||
classpath 'com.google.gms:google-services:4.4.1'
|
||||
classpath('com.android.tools.build:gradle')
|
||||
classpath('com.facebook.react:react-native-gradle-plugin')
|
||||
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
|
||||
}
|
||||
}
|
||||
|
||||
apply plugin: "com.facebook.react.rootproject"
|
||||
|
||||
allprojects {
|
||||
repositories {
|
||||
maven {
|
||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
||||
url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
|
||||
}
|
||||
maven {
|
||||
// Android JSC is installed from npm
|
||||
url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist'))
|
||||
}
|
||||
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://www.jitpack.io' }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
# Project-wide Gradle settings.
|
||||
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m
|
||||
org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
||||
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
|
||||
# Enable AAPT2 PNG crunching
|
||||
android.enablePngCrunchInReleaseBuilds=true
|
||||
|
||||
# Use this property to specify which architecture you want to build.
|
||||
# You can also override it from the CLI using
|
||||
# ./gradlew <task> -PreactNativeArchitectures=x86_64
|
||||
reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
||||
|
||||
# Use this property to enable support to the new architecture.
|
||||
# This will allow you to use TurboModules and the Fabric render in
|
||||
# your application. You should enable this flag either if you want
|
||||
# to write custom TurboModules/Fabric components OR use libraries that
|
||||
# are providing them.
|
||||
newArchEnabled=false
|
||||
|
||||
# Use this property to enable or disable the Hermes JS engine.
|
||||
# If set to false, you will be using JSC instead.
|
||||
hermesEnabled=true
|
||||
|
||||
# Enable GIF support in React Native images (~200 B increase)
|
||||
expo.gif.enabled=true
|
||||
# Enable webp support in React Native images (~85 KB increase)
|
||||
expo.webp.enabled=true
|
||||
# Enable animated webp support (~3.4 MB increase)
|
||||
# Disabled by default because iOS doesn't support animated webp
|
||||
expo.webp.animated=false
|
||||
|
||||
# Enable network inspector
|
||||
EX_DEV_CLIENT_NETWORK_INSPECTOR=true
|
||||
|
||||
# Use legacy packaging to compress native libraries in the resulting APK.
|
||||
expo.useLegacyPackaging=false
|
||||
|
||||
android.compileSdkVersion=35
|
||||
android.targetSdkVersion=35
|
||||
android.buildToolsVersion=35.0.0
|
||||
android.extraMavenRepos=[{"url":"../../node_modules/@notifee/react-native/android/libs"}]
|
||||
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -0,0 +1,252 @@
|
||||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
||||
' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
||||
# and any embedded shellness will be escaped.
|
||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
||||
# treated as '${Hostname}' itself on the command line.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
||||
@@ -0,0 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -0,0 +1,38 @@
|
||||
pluginManagement {
|
||||
includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile().toString())
|
||||
}
|
||||
plugins { id("com.facebook.react.settings") }
|
||||
|
||||
extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
|
||||
if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') {
|
||||
ex.autolinkLibrariesFromCommand()
|
||||
} else {
|
||||
def command = [
|
||||
'node',
|
||||
'--no-warnings',
|
||||
'--eval',
|
||||
'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
|
||||
'react-native-config',
|
||||
'--json',
|
||||
'--platform',
|
||||
'android'
|
||||
].toList()
|
||||
ex.autolinkLibrariesFromCommand(command)
|
||||
}
|
||||
}
|
||||
|
||||
rootProject.name = 'minuit.starter'
|
||||
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
reactAndroidLibs {
|
||||
from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle");
|
||||
useExpoModules()
|
||||
|
||||
include ':app'
|
||||
includeBuild(new File(["node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim()).getParentFile())
|
||||
@@ -0,0 +1,108 @@
|
||||
{
|
||||
"expo": {
|
||||
"owner": "minuitagency",
|
||||
"slug": "minuitstarter",
|
||||
"name": "minuit.starter",
|
||||
"version": "2024.04.1",
|
||||
"scheme": "minuit",
|
||||
"orientation": "portrait",
|
||||
"icon": "./assets/alternateIcons/4.png",
|
||||
"backgroundColor": "#0F0C14",
|
||||
"jsEngine": "hermes",
|
||||
"notification": {
|
||||
"iosDisplayInForeground": true
|
||||
},
|
||||
"updates": {
|
||||
"fallbackToCacheTimeout": 0
|
||||
},
|
||||
"packagerOpts": {
|
||||
"sourceExts": ["js", "json", "ts", "tsx", "jsx", "vue"]
|
||||
},
|
||||
"splash": {
|
||||
"image": "./assets/splash.png",
|
||||
"tabletImage": "./assets/tabletSplash.png",
|
||||
"resizeMode": "contain",
|
||||
"backgroundColor": "#0F0C14"
|
||||
},
|
||||
"ios": {
|
||||
"googleServicesFile": "./config/GoogleService-Info.plist",
|
||||
"supportsTablet": true,
|
||||
"requireFullScreen": true,
|
||||
"userInterfaceStyle": "dark",
|
||||
"associatedDomains": ["applinks:minuit.starter"],
|
||||
"bundleIdentifier": "com.minuit.starter",
|
||||
"infoPlist": {
|
||||
"UISupportedInterfaceOrientations": [
|
||||
"UIInterfaceOrientationPortrait",
|
||||
"UIInterfaceOrientationPortraitUpsideDown"
|
||||
],
|
||||
"UISupportedInterfaceOrientations~ipad": [
|
||||
"UIInterfaceOrientationLandscapeLeft",
|
||||
"UIInterfaceOrientationLandscapeRight"
|
||||
],
|
||||
"LSApplicationQueriesSchemes": ["itms-apps", "minuit"]
|
||||
},
|
||||
"config": {
|
||||
"usesNonExemptEncryption": false
|
||||
},
|
||||
"entitlements": {
|
||||
"aps-environment": "development"
|
||||
},
|
||||
"appleTeamId": "W2QZ9CTMYJ"
|
||||
},
|
||||
"android": {
|
||||
"googleServicesFile": "./config/google-services.json",
|
||||
"adaptiveIcon": {
|
||||
"foregroundImage": "./assets/adaptive-icon.png",
|
||||
"backgroundColor": "#FFFFFF"
|
||||
},
|
||||
"package": "com.minuit.starter",
|
||||
"permissions": ["android.permission.RECORD_AUDIO"]
|
||||
},
|
||||
"web": {
|
||||
"bundler": "metro"
|
||||
},
|
||||
"plugins": [
|
||||
"react-native-compressor",
|
||||
"@react-native-firebase/crashlytics",
|
||||
"@react-native-firebase/app",
|
||||
[
|
||||
"expo-image-picker",
|
||||
{
|
||||
"photosPermission": "Nous avons besoin d'accéder à votre galerie pour vous permettre d'ajouter des photos à vos tâches.",
|
||||
"cameraPermission": "Nous avons besoin d'accéder à votre appareil photo pour vous permettre de prendre des photos de vos tâches.",
|
||||
"microphonePermission": "Nous avons besoin d'accéder à votre microphone pour vous permettre d'enregistrer des messages vocaux pour vos tâches."
|
||||
}
|
||||
],
|
||||
[
|
||||
"expo-build-properties",
|
||||
{
|
||||
"ios": {
|
||||
"deploymentTarget": "15.1",
|
||||
"useFrameworks": "static"
|
||||
},
|
||||
"android": {
|
||||
"extraMavenRepos": [
|
||||
"../../node_modules/@notifee/react-native/android/libs"
|
||||
],
|
||||
"compileSdkVersion": 35,
|
||||
"targetSdkVersion": 35,
|
||||
"buildToolsVersion": "35.0.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
[
|
||||
"expo-document-picker",
|
||||
{
|
||||
"iCloudContainerEnvironment": "Production"
|
||||
}
|
||||
],
|
||||
"expo-font"
|
||||
],
|
||||
"extra": {
|
||||
"eas": {
|
||||
"projectId": "00bf388b-0a08-49fb-8d3c-85bd14e2feac"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 576 KiB |
|
After Width: | Height: | Size: 500 KiB |
|
After Width: | Height: | Size: 521 KiB |
|
After Width: | Height: | Size: 781 KiB |
|
After Width: | Height: | Size: 237 KiB |
|
After Width: | Height: | Size: 5.8 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,10 @@
|
||||
module.exports = function (api) {
|
||||
api.cache(true);
|
||||
return {
|
||||
presets: ["babel-preset-expo"],
|
||||
plugins: [
|
||||
"@babel/plugin-proposal-export-namespace-from",
|
||||
"react-native-reanimated/plugin",
|
||||
],
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,251 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const parser = require("@babel/parser");
|
||||
const traverse = require("@babel/traverse").default;
|
||||
|
||||
const entryFile = path.resolve(__dirname, "index.js");
|
||||
const srcDir = path.resolve(__dirname, "src");
|
||||
|
||||
// Extensions de fichiers à analyser
|
||||
const scriptExtensions = [".js", ".jsx", ".ts", ".tsx", ".web.js"];
|
||||
const imageExtensions = [".png", ".jpg", ".jpeg", ".gif", ".svg"];
|
||||
|
||||
// Fonction pour obtenir tous les fichiers dans un dossier avec des extensions spécifiques
|
||||
function getAllFiles(dir, extensions, fileList = []) {
|
||||
const files = fs.readdirSync(dir);
|
||||
files.forEach(function (file) {
|
||||
const filePath = path.join(dir, file);
|
||||
const stat = fs.statSync(filePath);
|
||||
if (stat.isDirectory()) {
|
||||
getAllFiles(filePath, extensions, fileList);
|
||||
} else if (extensions.includes(path.extname(file))) {
|
||||
fileList.push(filePath);
|
||||
}
|
||||
});
|
||||
return fileList;
|
||||
}
|
||||
|
||||
// Vérifie si un chemin est un fichier valide
|
||||
function isValidFile(filePath) {
|
||||
return fs.existsSync(filePath) && fs.statSync(filePath).isFile();
|
||||
}
|
||||
|
||||
// Résout le chemin de l'import en un fichier valide
|
||||
function resolveImport(filePath, importPath) {
|
||||
let importedFile = path.resolve(path.dirname(filePath), importPath);
|
||||
|
||||
// Liste des extensions à tester, y compris .web.js
|
||||
const allExtensions = scriptExtensions;
|
||||
|
||||
// Si le chemin n'a pas d'extension, essayer avec différentes extensions
|
||||
if (!path.extname(importedFile)) {
|
||||
for (const ext of allExtensions) {
|
||||
if (isValidFile(importedFile + ext)) {
|
||||
return importedFile + ext;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Si un fichier avec l'extension actuelle existe, le retourner
|
||||
if (isValidFile(importedFile)) {
|
||||
return importedFile;
|
||||
}
|
||||
|
||||
// Si le chemin est un répertoire, chercher un index avec les extensions
|
||||
if (fs.existsSync(importedFile) && fs.statSync(importedFile).isDirectory()) {
|
||||
const indexFiles = allExtensions.map((ext) => "index" + ext);
|
||||
for (const indexFile of indexFiles) {
|
||||
const indexFilePath = path.join(importedFile, indexFile);
|
||||
if (isValidFile(indexFilePath)) {
|
||||
return indexFilePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Aucun fichier valide trouvé
|
||||
return null;
|
||||
}
|
||||
|
||||
// Analyse des fichiers pour trouver les imports
|
||||
function getDependencies(
|
||||
filePath,
|
||||
visitedFiles = new Set(),
|
||||
usedDependencies = new Set(),
|
||||
usedFiles = new Set(),
|
||||
usedAssets = new Set()
|
||||
) {
|
||||
if (visitedFiles.has(filePath))
|
||||
return { usedDependencies, usedFiles, usedAssets };
|
||||
visitedFiles.add(filePath);
|
||||
usedFiles.add(filePath);
|
||||
|
||||
const content = fs.readFileSync(filePath, "utf-8");
|
||||
let ast;
|
||||
try {
|
||||
ast = parser.parse(content, {
|
||||
sourceType: "module",
|
||||
plugins: ["jsx", "typescript", "classProperties", "dynamicImport"],
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(`Erreur lors de l'analyse du fichier ${filePath}:`, error);
|
||||
return { usedDependencies, usedFiles, usedAssets };
|
||||
}
|
||||
|
||||
traverse(ast, {
|
||||
ImportDeclaration({ node }) {
|
||||
const importPath = node.source.value;
|
||||
handleImport(
|
||||
filePath,
|
||||
importPath,
|
||||
visitedFiles,
|
||||
usedDependencies,
|
||||
usedFiles,
|
||||
usedAssets
|
||||
);
|
||||
},
|
||||
CallExpression({ node }) {
|
||||
if (
|
||||
node.callee.type === "Import" ||
|
||||
(node.callee.name === "require" && node.arguments.length)
|
||||
) {
|
||||
const importArg = node.arguments[0];
|
||||
if (importArg && importArg.type === "StringLiteral") {
|
||||
const importPath = importArg.value;
|
||||
handleImport(
|
||||
filePath,
|
||||
importPath,
|
||||
visitedFiles,
|
||||
usedDependencies,
|
||||
usedFiles,
|
||||
usedAssets
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Vérifier si un fichier .web.js correspondant existe
|
||||
if (filePath.endsWith(".js")) {
|
||||
const webFilePath = filePath.replace(/\.js$/, ".web.js");
|
||||
if (isValidFile(webFilePath)) {
|
||||
getDependencies(
|
||||
webFilePath,
|
||||
visitedFiles,
|
||||
usedDependencies,
|
||||
usedFiles,
|
||||
usedAssets
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return { usedDependencies, usedFiles, usedAssets };
|
||||
}
|
||||
|
||||
// Fonction pour gérer les imports
|
||||
function handleImport(
|
||||
filePath,
|
||||
importPath,
|
||||
visitedFiles,
|
||||
usedDependencies,
|
||||
usedFiles,
|
||||
usedAssets
|
||||
) {
|
||||
if (importPath.startsWith(".")) {
|
||||
// Chemin relatif
|
||||
const resolvedPath = resolveImport(filePath, importPath);
|
||||
if (resolvedPath) {
|
||||
const ext = path.extname(resolvedPath);
|
||||
if (scriptExtensions.includes(ext)) {
|
||||
getDependencies(
|
||||
resolvedPath,
|
||||
visitedFiles,
|
||||
usedDependencies,
|
||||
usedFiles,
|
||||
usedAssets
|
||||
);
|
||||
} else if (imageExtensions.includes(ext)) {
|
||||
usedAssets.add(resolvedPath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Dépendance de node_modules
|
||||
const dep = importPath.split("/")[0];
|
||||
usedDependencies.add(dep);
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
// Étape 1: Obtenir tous les fichiers utilisés
|
||||
const allScriptFiles = getAllFiles(srcDir, scriptExtensions);
|
||||
const allAssetFiles = getAllFiles(srcDir, imageExtensions);
|
||||
|
||||
const { usedDependencies, usedFiles, usedAssets } =
|
||||
getDependencies(entryFile);
|
||||
|
||||
// Étape 2: Supprimer les fichiers scripts inutilisés
|
||||
const unusedScriptFiles = allScriptFiles.filter(
|
||||
(file) => !usedFiles.has(file)
|
||||
);
|
||||
|
||||
unusedScriptFiles.forEach((file) => {
|
||||
fs.unlinkSync(file);
|
||||
console.log(`Fichier script supprimé: ${file}`);
|
||||
});
|
||||
|
||||
// Étape 3: Supprimer les images non utilisées
|
||||
const unusedAssetFiles = allAssetFiles.filter(
|
||||
(file) => !usedAssets.has(file)
|
||||
);
|
||||
|
||||
unusedAssetFiles.forEach((file) => {
|
||||
fs.unlinkSync(file);
|
||||
console.log(`Fichier image supprimé: ${file}`);
|
||||
});
|
||||
|
||||
// Étape 4: Optimiser les images utilisées sans perte de qualité
|
||||
async function optimizeImages(usedAssets) {
|
||||
// Import dynamique des modules ES
|
||||
const imagemin = (await import("imagemin")).default;
|
||||
const imageminOptipng = (await import("imagemin-optipng")).default;
|
||||
const imageminJpegtran = (await import("imagemin-jpegtran")).default;
|
||||
const imageminGifsicle = (await import("imagemin-gifsicle")).default;
|
||||
const imageminSvgo = (await import("imagemin-svgo")).default;
|
||||
|
||||
for (const file of usedAssets) {
|
||||
const ext = path.extname(file).toLowerCase();
|
||||
const plugins = [];
|
||||
|
||||
if (ext === ".png") {
|
||||
plugins.push(imageminOptipng({ optimizationLevel: 3 }));
|
||||
} else if (ext === ".jpg" || ext === ".jpeg") {
|
||||
plugins.push(imageminJpegtran({ progressive: true }));
|
||||
} else if (ext === ".gif") {
|
||||
plugins.push(imageminGifsicle({ optimizationLevel: 3 }));
|
||||
} else if (ext === ".svg") {
|
||||
plugins.push(imageminSvgo());
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const optimized = await imagemin([file], {
|
||||
destination: path.dirname(file),
|
||||
plugins: plugins,
|
||||
});
|
||||
|
||||
if (optimized && optimized.length > 0) {
|
||||
console.log(`Image optimisée: ${file}`);
|
||||
} else {
|
||||
console.log(`Image déjà optimisée ou non optimisable: ${file}`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(
|
||||
`Erreur lors de l'optimisation de l'image ${file}:`,
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await optimizeImages(usedAssets);
|
||||
})();
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CLIENT_ID</key>
|
||||
<string>943006074419-h43c7p48nhn5f4td4oimv4dq7b1p3s29.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.943006074419-h43c7p48nhn5f4td4oimv4dq7b1p3s29</string>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyAkl2RAzUCYTQCcYhSeij6-tXNk6z7121Y</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>943006074419</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.minuit.starter</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>minuitcloud</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>minuitcloud.appspot.com</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:943006074419:ios:5deb4cce66c04c09658546</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"project_info": {
|
||||
"project_number": "943006074419",
|
||||
"project_id": "minuitcloud",
|
||||
"storage_bucket": "minuitcloud.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:943006074419:android:1cf962aa044ea020658546",
|
||||
"android_client_info": {
|
||||
"package_name": "com.minuit.starter"
|
||||
}
|
||||
},
|
||||
"oauth_client": [
|
||||
{
|
||||
"client_id": "943006074419-bal1qb28ssgh0v2ta1fhj1vl8a9mb7tp.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
}
|
||||
],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "AIzaSyA13RHqqFcx0tu6qja0shDomazoOk7wBw0"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": [
|
||||
{
|
||||
"client_id": "943006074419-bal1qb28ssgh0v2ta1fhj1vl8a9mb7tp.apps.googleusercontent.com",
|
||||
"client_type": 3
|
||||
},
|
||||
{
|
||||
"client_id": "943006074419-h43c7p48nhn5f4td4oimv4dq7b1p3s29.apps.googleusercontent.com",
|
||||
"client_type": 2,
|
||||
"ios_info": {
|
||||
"bundle_id": "com.minuit.starter",
|
||||
"app_store_id": "1661696886"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"cli": {
|
||||
"version": ">= 7.2.0",
|
||||
"appVersionSource": "remote",
|
||||
"promptToConfigurePushNotifications": true
|
||||
},
|
||||
"build": {
|
||||
"production": {
|
||||
"ios": {
|
||||
"buildConfiguration": "Release",
|
||||
"autoIncrement": "buildNumber"
|
||||
}
|
||||
},
|
||||
"preview": {
|
||||
"distribution": "internal",
|
||||
"ios": {
|
||||
"simulator": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"submit": {
|
||||
"production": {
|
||||
"ios": {
|
||||
"appleId": "letawny@gmail.com",
|
||||
"ascAppId": "1661696886",
|
||||
"appleTeamId": "MW8MX2T3ZL"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"functions": {
|
||||
"ignore": [
|
||||
"node_modules",
|
||||
".git",
|
||||
"firebase-debug.log",
|
||||
"firebase-debug.*.log"
|
||||
],
|
||||
"predeploy": []
|
||||
},
|
||||
"react-native": {
|
||||
"crashlytics_debug_enabled": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
# GOOGLE GENAI
|
||||
GOOGLE_GENAI_API_KEY=...
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
node_modules/
|
||||
@@ -0,0 +1,113 @@
|
||||
[debug] [2024-11-29T15:16:33.623Z] ----------------------------------------------------------------------
|
||||
[debug] [2024-11-29T15:16:33.625Z] Command: /Users/theo/.nvm/versions/node/v18.17.0/bin/node /Users/theo/.nvm/versions/node/v18.17.0/bin/firebase emulators:start
|
||||
[debug] [2024-11-29T15:16:33.625Z] CLI Version: 13.18.0
|
||||
[debug] [2024-11-29T15:16:33.626Z] Platform: darwin
|
||||
[debug] [2024-11-29T15:16:33.626Z] Node Version: v18.17.0
|
||||
[debug] [2024-11-29T15:16:33.628Z] Time: Fri Nov 29 2024 16:16:33 GMT+0100 (Central European Standard Time)
|
||||
[debug] [2024-11-29T15:16:33.629Z] ----------------------------------------------------------------------
|
||||
[debug]
|
||||
[debug] [2024-11-29T15:16:33.795Z] Object "" in "firebase.json" has unknown property: {"additionalProperty":"react-native"}
|
||||
[debug] [2024-11-29T15:16:33.804Z] > command requires scopes: ["email","openid","https://www.googleapis.com/auth/cloudplatformprojects.readonly","https://www.googleapis.com/auth/firebase","https://www.googleapis.com/auth/cloud-platform"]
|
||||
[debug] [2024-11-29T15:16:33.805Z] > authorizing via signed-in user (letawny@gmail.com)
|
||||
[info] i emulators: Starting emulators: functions {"metadata":{"emulator":{"name":"hub"},"message":"Starting emulators: functions"}}
|
||||
[debug] [2024-11-29T15:16:35.338Z] [logging] Logging Emulator only supports listening on one address (127.0.0.1). Not listening on ::1
|
||||
[debug] [2024-11-29T15:16:35.339Z] assigned listening specs for emulators {"user":{"hub":[{"address":"127.0.0.1","family":"IPv4","port":4400},{"address":"::1","family":"IPv6","port":4400}],"ui":[{"address":"127.0.0.1","family":"IPv4","port":4000},{"address":"::1","family":"IPv6","port":4000}],"logging":[{"address":"127.0.0.1","family":"IPv4","port":4500}]},"metadata":{"message":"assigned listening specs for emulators"}}
|
||||
[debug] [2024-11-29T15:16:35.347Z] [hub] writing locator at /var/folders/xw/v22l0r3n74lfydgqkyv8br5c0000gp/T/hub-starsclick.json
|
||||
[debug] [2024-11-29T15:16:36.711Z] [functions] Functions Emulator only supports listening on one address (127.0.0.1). Not listening on ::1
|
||||
[debug] [2024-11-29T15:16:36.712Z] [eventarc] Eventarc Emulator only supports listening on one address (127.0.0.1). Not listening on ::1
|
||||
[debug] [2024-11-29T15:16:36.712Z] [tasks] Cloud Tasks Emulator only supports listening on one address (127.0.0.1). Not listening on ::1
|
||||
[debug] [2024-11-29T15:16:36.713Z] late-assigned ports for functions and eventarc emulators {"user":{"hub":[{"address":"127.0.0.1","family":"IPv4","port":4400},{"address":"::1","family":"IPv6","port":4400}],"ui":[{"address":"127.0.0.1","family":"IPv4","port":4000},{"address":"::1","family":"IPv6","port":4000}],"logging":[{"address":"127.0.0.1","family":"IPv4","port":4500}],"functions":[{"address":"127.0.0.1","family":"IPv4","port":5001}],"eventarc":[{"address":"127.0.0.1","family":"IPv4","port":9299}],"tasks":[{"address":"127.0.0.1","family":"IPv4","port":9499}]},"metadata":{"message":"late-assigned ports for functions and eventarc emulators"}}
|
||||
[warn] ⚠ functions: The following emulators are not running, calls to these services from the Functions emulator will affect production: auth, firestore, database, hosting, pubsub, storage, dataconnect {"metadata":{"emulator":{"name":"functions"},"message":"The following emulators are not running, calls to these services from the Functions emulator will affect production: \u001b[1mauth, firestore, database, hosting, pubsub, storage, dataconnect\u001b[22m"}}
|
||||
[debug] [2024-11-29T15:16:36.719Z] defaultcredentials: writing to file /Users/theo/.config/firebase/letawny_gmail_com_application_default_credentials.json
|
||||
[debug] [2024-11-29T15:16:36.725Z] Setting GAC to /Users/theo/.config/firebase/letawny_gmail_com_application_default_credentials.json {"metadata":{"emulator":{"name":"functions"},"message":"Setting GAC to /Users/theo/.config/firebase/letawny_gmail_com_application_default_credentials.json"}}
|
||||
[debug] [2024-11-29T15:16:36.728Z] > refreshing access token with scopes: []
|
||||
[debug] [2024-11-29T15:16:36.730Z] >>> [apiv2][query] POST https://www.googleapis.com/oauth2/v3/token [none]
|
||||
[debug] [2024-11-29T15:16:36.730Z] >>> [apiv2][body] POST https://www.googleapis.com/oauth2/v3/token [omitted]
|
||||
[debug] [2024-11-29T15:16:36.876Z] <<< [apiv2][status] POST https://www.googleapis.com/oauth2/v3/token 200
|
||||
[debug] [2024-11-29T15:16:36.878Z] <<< [apiv2][body] POST https://www.googleapis.com/oauth2/v3/token [omitted]
|
||||
[debug] [2024-11-29T15:16:36.900Z] >>> [apiv2][query] GET https://firebase.googleapis.com/v1beta1/projects/starsclick/adminSdkConfig [none]
|
||||
[debug] [2024-11-29T15:16:37.423Z] <<< [apiv2][status] GET https://firebase.googleapis.com/v1beta1/projects/starsclick/adminSdkConfig 200
|
||||
[debug] [2024-11-29T15:16:37.423Z] <<< [apiv2][body] GET https://firebase.googleapis.com/v1beta1/projects/starsclick/adminSdkConfig {"projectId":"starsclick","storageBucket":"starsclick.appspot.com","locationId":"europe-west"}
|
||||
[debug] [2024-11-29T15:16:37.462Z] Ignoring unsupported arg: auto_download {"metadata":{"emulator":{"name":"ui"},"message":"Ignoring unsupported arg: auto_download"}}
|
||||
[debug] [2024-11-29T15:16:37.463Z] Ignoring unsupported arg: port {"metadata":{"emulator":{"name":"ui"},"message":"Ignoring unsupported arg: port"}}
|
||||
[debug] [2024-11-29T15:16:37.463Z] Starting Emulator UI with command {"binary":"node","args":["/Users/theo/.cache/firebase/emulators/ui-v1.13.0/server/server.mjs"],"optionalArgs":[],"joinArgs":false,"shell":false} {"metadata":{"emulator":{"name":"ui"},"message":"Starting Emulator UI with command {\"binary\":\"node\",\"args\":[\"/Users/theo/.cache/firebase/emulators/ui-v1.13.0/server/server.mjs\"],\"optionalArgs\":[],\"joinArgs\":false,\"shell\":false}"}}
|
||||
[info] i ui: Emulator UI logging to ui-debug.log {"metadata":{"emulator":{"name":"ui"},"message":"Emulator UI logging to \u001b[1mui-debug.log\u001b[22m"}}
|
||||
[debug] [2024-11-29T15:16:37.616Z] Web / API server started at 127.0.0.1:4000
|
||||
{"metadata":{"emulator":{"name":"ui"},"message":"Web / API server started at 127.0.0.1:4000\n"}}
|
||||
[debug] [2024-11-29T15:16:37.616Z] Web / API server started at ::1:4000
|
||||
{"metadata":{"emulator":{"name":"ui"},"message":"Web / API server started at ::1:4000\n"}}
|
||||
[info] i functions: Watching "/Users/theo/starsclick/functions" for Cloud Functions... {"metadata":{"emulator":{"name":"functions"},"message":"Watching \"/Users/theo/starsclick/functions\" for Cloud Functions..."}}
|
||||
[debug] [2024-11-29T15:16:37.683Z] Validating nodejs source
|
||||
[warn] ⚠ functions: package.json indicates an outdated version of firebase-functions. Please upgrade using npm install --save firebase-functions@latest in your functions directory.
|
||||
[warn] ⚠ functions: Please note that there will be breaking changes when you upgrade.
|
||||
[debug] [2024-11-29T15:16:38.518Z] > [functions] package.json contents: {
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase emulators:start --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18"
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/preset-env": "^7.25.8",
|
||||
"@babel/preset-react": "^7.25.7",
|
||||
"@babel/register": "^7.25.7",
|
||||
"@genkit-ai/google-cloud": "^0.9.4",
|
||||
"@genkit-ai/googleai": "^0.9.4",
|
||||
"@genkit-ai/vertexai": "^0.9.4",
|
||||
"@react-pdf/renderer": "^2.3.0",
|
||||
"axios": "^1.6.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"firebase-admin": "^11.8.0",
|
||||
"firebase-functions": "^4.3.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"stripe": "^16.1.0",
|
||||
"react": "^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"firebase-functions-test": "^3.1.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
[debug] [2024-11-29T15:16:38.518Z] Building nodejs source
|
||||
[debug] [2024-11-29T15:16:38.518Z] Failed to find version of module node: reached end of search path /Users/theo/starsclick/functions/node_modules
|
||||
[info] ✔ functions: Using node@18 from host.
|
||||
[info] i functions: Loaded environment variables from .env.
|
||||
[debug] [2024-11-29T15:16:38.522Z] Could not find functions.yaml. Must use http discovery
|
||||
[debug] [2024-11-29T15:16:38.537Z] Found firebase-functions binary at '/Users/theo/starsclick/functions/node_modules/.bin/firebase-functions'
|
||||
[info] Serving at port 8113
|
||||
|
||||
[debug] [2024-11-29T15:16:41.014Z] Got response from /__/functions.yaml {"endpoints":{"users-userUpdateListener":{"availableMemoryMb":null,"timeoutSeconds":null,"minInstances":null,"maxInstances":null,"ingressSettings":null,"concurrency":null,"serviceAccountEmail":null,"vpc":null,"platform":"gcfv2","labels":{},"eventTrigger":{"eventType":"google.cloud.firestore.document.v1.written","eventFilters":{"database":"(default)","namespace":"(default)"},"eventFilterPathPatterns":{"document":"users/{userID}"},"retry":false},"entryPoint":"users.userUpdateListener"},"users-deleteAccount":{"availableMemoryMb":null,"timeoutSeconds":null,"minInstances":null,"maxInstances":null,"ingressSettings":null,"concurrency":null,"serviceAccountEmail":null,"vpc":null,"platform":"gcfv2","labels":{},"callableTrigger":{},"entryPoint":"users.deleteAccount"},"payments-createCheckoutSession":{"availableMemoryMb":null,"timeoutSeconds":null,"minInstances":null,"maxInstances":null,"ingressSettings":null,"concurrency":null,"serviceAccountEmail":null,"vpc":null,"platform":"gcfv2","labels":{},"callableTrigger":{},"entryPoint":"payments.createCheckoutSession"},"payments-getPremiumStatus":{"availableMemoryMb":null,"timeoutSeconds":null,"minInstances":null,"maxInstances":null,"ingressSettings":null,"concurrency":null,"serviceAccountEmail":null,"vpc":null,"platform":"gcfv2","labels":{},"callableTrigger":{},"entryPoint":"payments.getPremiumStatus"},"payments-createStripeCustomerPortalSession":{"availableMemoryMb":null,"timeoutSeconds":null,"minInstances":null,"maxInstances":null,"ingressSettings":null,"concurrency":null,"serviceAccountEmail":null,"vpc":null,"platform":"gcfv2","labels":{},"callableTrigger":{},"entryPoint":"payments.createStripeCustomerPortalSession"},"documents-generateDocumentFromTemplate":{"availableMemoryMb":null,"timeoutSeconds":null,"minInstances":null,"maxInstances":null,"ingressSettings":null,"concurrency":null,"serviceAccountEmail":null,"vpc":null,"platform":"gcfv2","labels":{},"callableTrigger":{},"entryPoint":"documents.generateDocumentFromTemplate"},"documents-testDocumentGeneration":{"platform":"gcfv1","availableMemoryMb":512,"timeoutSeconds":null,"minInstances":null,"maxInstances":null,"ingressSettings":null,"serviceAccountEmail":null,"vpc":null,"httpsTrigger":{},"entryPoint":"documents.testDocumentGeneration"}},"specVersion":"v1alpha1","requiredAPIs":[]}
|
||||
[info] ✔ functions: Loaded functions definitions from source: users.userUpdateListener, users.deleteAccount, payments.createCheckoutSession, payments.getPremiumStatus, payments.createStripeCustomerPortalSession, documents.generateDocumentFromTemplate, documents.testDocumentGeneration. {"metadata":{"emulator":{"name":"functions"},"message":"Loaded functions definitions from source: users.userUpdateListener, users.deleteAccount, payments.createCheckoutSession, payments.getPremiumStatus, payments.createStripeCustomerPortalSession, documents.generateDocumentFromTemplate, documents.testDocumentGeneration."}}
|
||||
[info] i functions[us-central1-users-userUpdateListener]: function ignored because the firestore emulator does not exist or is not running. {"metadata":{"emulator":{"name":"functions"},"message":"function ignored because the firestore emulator does not exist or is not running."}}
|
||||
[info] ✔ functions[us-central1-users-deleteAccount]: http function initialized (http://127.0.0.1:5001/starsclick/us-central1/users-deleteAccount). {"metadata":{"emulator":{"name":"functions"},"message":"\u001b[1mhttp\u001b[22m function initialized (http://127.0.0.1:5001/starsclick/us-central1/users-deleteAccount)."}}
|
||||
[info] ✔ functions[us-central1-payments-createCheckoutSession]: http function initialized (http://127.0.0.1:5001/starsclick/us-central1/payments-createCheckoutSession). {"metadata":{"emulator":{"name":"functions"},"message":"\u001b[1mhttp\u001b[22m function initialized (http://127.0.0.1:5001/starsclick/us-central1/payments-createCheckoutSession)."}}
|
||||
[info] ✔ functions[us-central1-payments-getPremiumStatus]: http function initialized (http://127.0.0.1:5001/starsclick/us-central1/payments-getPremiumStatus). {"metadata":{"emulator":{"name":"functions"},"message":"\u001b[1mhttp\u001b[22m function initialized (http://127.0.0.1:5001/starsclick/us-central1/payments-getPremiumStatus)."}}
|
||||
[info] ✔ functions[us-central1-payments-createStripeCustomerPortalSession]: http function initialized (http://127.0.0.1:5001/starsclick/us-central1/payments-createStripeCustomerPortalSession). {"metadata":{"emulator":{"name":"functions"},"message":"\u001b[1mhttp\u001b[22m function initialized (http://127.0.0.1:5001/starsclick/us-central1/payments-createStripeCustomerPortalSession)."}}
|
||||
[info] ✔ functions[us-central1-documents-generateDocumentFromTemplate]: http function initialized (http://127.0.0.1:5001/starsclick/us-central1/documents-generateDocumentFromTemplate). {"metadata":{"emulator":{"name":"functions"},"message":"\u001b[1mhttp\u001b[22m function initialized (http://127.0.0.1:5001/starsclick/us-central1/documents-generateDocumentFromTemplate)."}}
|
||||
[info] ✔ functions[us-central1-documents-testDocumentGeneration]: http function initialized (http://127.0.0.1:5001/starsclick/us-central1/documents-testDocumentGeneration). {"metadata":{"emulator":{"name":"functions"},"message":"\u001b[1mhttp\u001b[22m function initialized (http://127.0.0.1:5001/starsclick/us-central1/documents-testDocumentGeneration)."}}
|
||||
[info]
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ ✔ All emulators ready! It is now safe to connect your app. │
|
||||
│ i View Emulator UI at http://127.0.0.1:4000/ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
|
||||
┌───────────┬────────────────┬─────────────────────────────────┐
|
||||
│ Emulator │ Host:Port │ View in Emulator UI │
|
||||
├───────────┼────────────────┼─────────────────────────────────┤
|
||||
│ Functions │ 127.0.0.1:5001 │ http://127.0.0.1:4000/functions │
|
||||
└───────────┴────────────────┴─────────────────────────────────┘
|
||||
Emulator Hub running at 127.0.0.1:4400
|
||||
Other reserved ports: 4500
|
||||
|
||||
Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
const admin = require("firebase-admin");
|
||||
|
||||
const serviceAccount = require("./serviceAccountProd.json");
|
||||
|
||||
admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount),
|
||||
});
|
||||
|
||||
let db = admin.firestore();
|
||||
exports.db = db;
|
||||
|
||||
|
||||
const { vertexAI, gemini15Flash } = require("@genkit-ai/vertexai");
|
||||
const { genkit } = require("genkit");
|
||||
|
||||
require("dotenv").config();
|
||||
|
||||
require("@babel/register")({
|
||||
presets: ["@babel/preset-env", "@babel/preset-react"],
|
||||
});
|
||||
|
||||
// configure a Genkit instance
|
||||
exports.genkitInstance = genkit({
|
||||
plugins: [vertexAI({ projectId: "starsclick", location: "us-central1" })],
|
||||
logLevel: "debug",
|
||||
enableTracingAndMetrics: true,
|
||||
model: gemini15Flash, // set default model
|
||||
});
|
||||
|
||||
|
||||
exports.refsList = {
|
||||
users: db.collection("users"),
|
||||
documents: db.collection("documents"),
|
||||
logs: db.collection("logs"),
|
||||
};
|
||||
|
||||
exports.users = require("./src/users");
|
||||
exports.payments = require("./src/payments");
|
||||
exports.documents = require("./src/documents");
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "functions",
|
||||
"description": "Cloud Functions for Firebase",
|
||||
"scripts": {
|
||||
"serve": "firebase emulators:start --only functions",
|
||||
"shell": "firebase functions:shell",
|
||||
"start": "npm run shell",
|
||||
"deploy": "firebase deploy --only functions",
|
||||
"logs": "firebase functions:log"
|
||||
},
|
||||
"engines": {
|
||||
"node": "18"
|
||||
},
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/preset-env": "^7.25.8",
|
||||
"@babel/preset-react": "^7.25.7",
|
||||
"@babel/register": "^7.25.7",
|
||||
"@genkit-ai/google-cloud": "^0.9.4",
|
||||
"@genkit-ai/googleai": "^0.9.4",
|
||||
"@genkit-ai/vertexai": "^0.9.4",
|
||||
"@react-pdf/renderer": "^2.3.0",
|
||||
"axios": "^1.6.2",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dotenv": "^16.0.3",
|
||||
"firebase-admin": "^11.8.0",
|
||||
"firebase-functions": "^4.3.1",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.45",
|
||||
"stripe": "^16.1.0",
|
||||
"react": "^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"firebase-functions-test": "^3.1.0"
|
||||
},
|
||||
"private": true
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
import React from "react";
|
||||
import { Document, Page, Text, View, Font, Image } from "@react-pdf/renderer";
|
||||
import { presidentSignatureBase64 } from "../data/presidentSignatureBase64";
|
||||
import { RenderCompanyLogo, SignatureForm, Styles } from "./SharedElements";
|
||||
|
||||
Font.register({
|
||||
family: "Helvetica",
|
||||
fonts: [
|
||||
{ src: "https://fonts.gstatic.com/s/helvetica/v15/Helvetica-Regular.ttf" },
|
||||
{
|
||||
src: "https://fonts.gstatic.com/s/helvetica/v15/Helvetica-Bold.ttf",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const Contract = ({
|
||||
domiciliationData = {},
|
||||
appConfig = {},
|
||||
contractFields = {},
|
||||
} = {}) => {
|
||||
const { company } = appConfig;
|
||||
|
||||
const {
|
||||
intro_contrat,
|
||||
raison_sociale,
|
||||
forme_juridique,
|
||||
qualite,
|
||||
civilite,
|
||||
prenoms,
|
||||
nom_de_jeune_fille_si_madame,
|
||||
nom,
|
||||
adresse,
|
||||
code_postal,
|
||||
ville,
|
||||
adresse_de_domiciliation,
|
||||
date_souhaitee_du_debut_de_la_domiciliation,
|
||||
} = contractFields;
|
||||
|
||||
const sections = [
|
||||
{
|
||||
title: "Contrat de domiciliation",
|
||||
content: <RenderCompanyLogo logoBase64={company?.logoBase64} />,
|
||||
},
|
||||
{
|
||||
title: null,
|
||||
content: (
|
||||
<Text style={Styles.textParagraph}>{`
|
||||
Entre les soussignés :
|
||||
${intro_contrat}
|
||||
Ci-après désignée « le domiciliataire »
|
||||
D’une part et,
|
||||
La société ${raison_sociale} ${forme_juridique} représentée par son ${qualite} , ${civilite} ${prenoms} ${nom_de_jeune_fille_si_madame} ${nom} demeurant ${adresse} ${code_postal} ${ville}.
|
||||
Ci-après désignée « Le domicilié »
|
||||
Il a été arrêté et convenu ce qui suit :
|
||||
Article préliminaire : consentement des parties
|
||||
Le Domicilié déclare avoir pris connaissance de tous les documents afférents à la présente (annexes, autorisation du pli du courrier et attestation comptable) et déclare donner acceptation expresse pour la totalité des clauses figurants dans les documents.
|
||||
|
||||
Article 1 : OBJET DU CONTRAT
|
||||
Le présent contrat a pour objet la domiciliation du siège social du Domicilié conformément aux dispositions des articles R123-167 à R 123-171 du Code de Commerce.
|
||||
|
||||
Article 2 : OBLIGATION DES PARTIES
|
||||
2.1 Obligations du domiciliataire
|
||||
2.1.1 Obligations légales.
|
||||
Conformément aux dispositions législatives des articles R 123-167 à R 123-171 du code de Commerce, le Domiciliataire s’engage, pendant toute la durée du présent contrat, à :
|
||||
• Etre immatriculé au Registre du Commerce et des Sociétés ou au Répertoire des Métiers, durant l’occupation des locaux.
|
||||
• Mettre à la disposition du Domicilié des locaux dotés d’une pièce destinée à assurer la confidentialité nécessaire et à permettre une réunion régulière des organes de direction, d’administration ou de surveillance de l’entreprise ainsi que la tenue, la conservation et consultation des livres, registres et documents prescrits par la loi (Décret N°85.1280 du 5 décembre 1985, Article 2.6.1 modifié par Décret 2007-750 du 9 mai 2007).
|
||||
• Détenir, pour le Domicilié, un dossier contenant les pièces justificatives relatives au domicile de son représentant légal, ses coordonnées téléphoniques ainsi que les lieux d’activité et de détention des documents comptables lorsqu’ils ne sont pas conservés chez elle.
|
||||
• Informer le greffier du tribunal, à l’expiration du contrat ou en cas de résiliation anticipée de celui-ci, de la cessation de la domiciliation de l’entreprise dans ses locaux.
|
||||
• Communiquer aux huissiers de justice, munis d’un titre exécutoire, les renseignements propres à joindre le domicilié.
|
||||
• Fournir, chaque trimestre, au centre des impôts et aux organismes de recouvrement des cotisations et contributions de sécurité sociale compétents, une liste des personnes qui se sont domiciliées dans ses locaux au cours de cette période ou qui ont mis fin à leur domiciliation ainsi que chaque année, une liste des personnes domiciliées au 1er
|
||||
|
||||
2.1.2 Prestations
|
||||
Le domiciliataire s’engage à fournir au Domicilié les prestations de services suivantes :
|
||||
Domiciliation commerciale.
|
||||
Cette prestation permet au domicilié de fixer son siège social dans les locaux du Domiciliataire situés au ${adresse_de_domiciliation}.
|
||||
Domiciliation postale.
|
||||
La prestation sus évoquée permet uniquement au Domicilié de recevoir son courrier (recommandé et pli spécial avec autorisation) , sans pour autant en faire une utilisation dans sa communication commerciale qui serait susceptible de publicité mensongère ou trompeuse, le tout conformément aux dispositions législatives en vigueur et notamment l’article L-121-1 du Code de la consommation.
|
||||
La mise à disposition des locaux.
|
||||
Cette prestation permet au domicilié de bénéficier de locaux dotés d’une pièce destinée à assurer la confidentialité nécessaire et y permettre une réunion régulière des organes chargés de la direction, de l’administration ou de la surveillance de l’entreprise ainsi que la tenue, la conservation et la consultation des livres, registres et documents prescrits par les lois et règlements. Cette prestation est soumise à une rémunération supplémentaire proposée sur devis.
|
||||
|
||||
2.2 Obligations du Domicilié
|
||||
2.2.1. Obligations générales
|
||||
Dès la signature du présent contrat et durant toute la durée de celui-ci, le domicilié s’engage à :
|
||||
• Justifier de son inscription au Registre du Commerce ou des Métiers ou toutes autres administrations compétentes dans les trois mois qui suivent la date d’engagement de la domiciliation, faute de quoi le Domiciliataire se réserve le droit de commander les documents justificatifs au frais du domicilié, au coût de 20 € HT (Vingt euros hors-taxes) pour le K-bis et de 40 € HT (quarante euros hors-taxes) pour les statuts.
|
||||
• certifier l’exactitude des renseignements fournis à l’appui de la signature du contrat avec le Domiciliataire, certifier ne pas être en situation de liquidation de biens, redressement judiciaire en ce qui concerne l’entreprise ou les entreprises qu’il dirige, que ces établissements soient l’objet ou non dudit contrat, certifier ne pas être à titre personnel frappé de faillite personnelle ou d’interdiction de gérer, attester l’exactitude de tous les renseignements fournis au Domiciliataire tant en ce qui concerne son état civil que l’entreprise représentée.
|
||||
Dans tous les cas, le Domicilié sera responsable de ses dettes après son départ du ${adresse_de_domiciliation}.
|
||||
Tout renseignement fourni par le Domicilié pourra être communiqué sur demande aux représentants des organismes officiels et le domicilié en donne dès à présent son accord.
|
||||
• Utiliser effectivement et exclusivement les locaux, soit comme siège de l’entreprise, soit si le siège est situé à l’étranger comme agence, succursale ou représentation ;
|
||||
• Tenir informé le Domiciliataire de toute modification concernant son activité ;
|
||||
En cas de changement, soit d’adresse, soit d’état civil personnel, soit de dénomination sociale, soit de nom commercial, soit de sigle (afin d’éviter les homonymes), soit de forme juridique ou d’objet, soit de dirigeant, soit de l’utilisateur des prestations fournies au titre du présent contrat, le Domicilié devra fournir tous les documents afférents à ces modifications au Domiciliataire et présenter son successeur ou le nouvel utilisateur, avant de déclarer tout changement auprès du Greffe du Tribunal de Commerce ou de la Chambre des Métiers ou de toute autre administration compétente.
|
||||
• Déclarer tout changement statutaire et/ou relatif à sa forme juridique et son objet, ainsi qu’au nom et au domicile personnel des personnes ayant le pouvoir de l’engager à titre habituel ; donner mandat au Domiciliataire, qui l’accepte, de recevoir en son nom toute notification. Faute d’exécution d’une telle obligation, le domicilié s’acquittera dun règlement de 30 € HT (trente euros hors-taxes) correspondant aux frais de recherches des informations à fournir.
|
||||
Les colis et plis spéciaux (DHL, CHRONOPOST, UPS, ...) ne seront acceptés qu’après signature d’un pouvoir de réception, aux conditions indiquées sur ledit document.
|
||||
Le Domicilié s’engage à signer la procuration postale fournie avec le contrat au bénéfice du Domiciliataire afin de pouvoir réceptionner en son nom ou au nom de son entreprise les envois recommandés.
|
||||
2.2.2. Obligations particulières relatives à la formule choisie par le domicilié
|
||||
- Le domicilié s’engage à respecter les modalités d’exécution et obligations contractuelles qui figurent en annexe du présent contrat.
|
||||
- Lesdites annexes reflètent l’offre à laquelle le domicilié a souscrit et doivent être considérées comme partie intégrante du présent contrat et doivent être à cet égard signées par les parties.
|
||||
2.2.3. Redevance
|
||||
En contrepartie des obligations auxquelles le Domiciliataire sest engagé à l’article 2.1, le domicilié s’engage à régler une redevance mensuelle dont le montant figure en annexe conformément à l’offre choisie. Ladite redevance est payable par prélèvement bancaire automatique, dit “ mandat SEPA “ le dix du mois en cours. Elle couvre les différentes prestations décrites dans chacune des offres à l’exception de la mise à disposition des locaux qui fait l’objet d’une tarification supplémentaire et des éventuels frais de réexpédition.
|
||||
Lesdits frais de réexpédition du courrier prévus dans certaines offres seront facturés en sus au Domicilié et payables par prélèvement bancaire automatique, dit “ Mandat SEPA “ le dix du mois en cours. Une facture dématérialisée sera adressée mensuellement au gérant du Domicilié.
|
||||
En cas d’anomalie ou erreur de prélèvement, ou en cas de règlement autre que le prélèvement mensuel ou le règlement de l’offre à l’année, la somme forfaitaire mensuelle sera majorée au tarif bancaire en vigueur par incident ou erreur commise, au titre des frais administratifs.
|
||||
Pour les règlements par chèque, prélèvement ou carte bancaire remis à l’encaissement et impayés, des frais au tarif bancaire en vigueur seront imputés automatiquement sur la prochaine facture de domiciliation.
|
||||
Le Domicilié donne dès à présent son accord pour une révision chaque année du tarif mensuel des prestations de services définies à l’Article 2.1.2. en fonction du taux d’inflation (indice IPC) ou de l’évolution des services auxquels il souscrit, ainsi que pour le mode de règlement proposé par le Domiciliataire.
|
||||
Le domicilié sera tenu informé de la révision du tarif mensuel des prestations de services, un mois avant ladite révision.
|
||||
Ce contrat est ferme et définitif à la signature et aucun remboursement même partiel ne saurait être revendiqué par le domicilié.
|
||||
|
||||
Article 3 : Accès à l’espace et utilisation des locaux
|
||||
Sauf autorisation exceptionnelle mentionnée dans les conditions particulières ou par avenant à ce présent contrat, le Domicilié n’aura en aucun cas le droit de sous louer ou donner accès à l’espace à un tiers, hors prestations permises par le présent contrat.
|
||||
|
||||
Article 4 : Durée du contrat
|
||||
Le présent contratest consenti pour une durée minimale de 3 mois à compter du ${date_souhaitee_du_debut_de_la_domiciliation}.
|
||||
4.1. Renouvellement
|
||||
Le présent contrat peut être renouvelé, par tacite reconduction, sauf résiliation notifiée par l’une ou l’autre des parties par lettre recommandée avec accusé de réception et expédiée au moins 30 (trente) jours avant le terme annuel.
|
||||
4.2. Résiliation
|
||||
Avant le terme de ce préavis, le Domicilié devra impérativement, pour valider sa résiliation définitive de contrat, adresser par courrier, une photocopie de son nouveau Kbis ou tout document remis par l’administration compétente, justifiant le transfert de siège social ou de sa radiation.
|
||||
En l’absence de ce justificatif, le Domiciliataire se réserve le droit de poursuivre le contrat de domiciliation dans les termes initiaux.
|
||||
L’adresse de correspondance est :
|
||||
I DOM YOU ${adresse_de_domiciliation}
|
||||
À l’expiration du présent contrat ou en cas de résiliation de celui-ci, le Domiciliataire s’engage à informer le greffe du Tribunal de Commerce de Paris de la cessation de la domiciliation.
|
||||
Au terme de ce contrat, le courrier sera refusé par le Domiciliataire avec l’annotation « N’habite Pas à l’Adresse Indiquée ».
|
||||
|
||||
Article 5 : Dépôt de garantie
|
||||
Le Domicilié verse, à la date de signature du présent contrat, à titre de dépôt de garantie, un montant correspondant à 3mois de redevance, en sus du mois correspondant à l’offre souscrite.
|
||||
Le dépôt de garantie sera encaissé par le Domiciliataire dès sa remise par le Domicilié.
|
||||
À chaque réajustement de la redevance, le dépôt de garantie sera diminué ou majoré de manière à toujours correspondre à 3 mois de redevance, hors taxes.
|
||||
Ce dépôt de garantie est destiné, en cas de résiliation du contrat, au paiement des sommes dues pour la fourniture des services, l’exécution parfaite des clauses du présent contrat et des sommes dues par le Domicilié dont le Domiciliataire pourrait être rendu responsable.
|
||||
En effet, ce dépôt de garantie pourra être utilisé soit pour défaut de règlement, soit pour la conséquence de dommages occasionnés par le Domicilié ou ses commettants.
|
||||
Ledit dépôt de garantie ne saurait en aucun cas dispenser le Domicilié de payer toutes les redevances jusqu’au terme prévu.
|
||||
Dans l’hypothèse où le dépôt de garantie versé par le Domicilié est inférieur au montant des remises en état opérées par le Domiciliataire au terme du contrat, le Domicilié s’engage à rembourser au Domiciliataire la différence entre la valeur des remises en état justifiée sur facture et le dépôt de garantie.
|
||||
À la fin du contrat, ce dépôt de garantie sera remboursé déduction faite des sommes qui pourraient être dues au Domiciliataire.
|
||||
Les sommes versées à titre de dépôt de garantie ne sauraient produire d’intérêt au profit du Domicilié.
|
||||
|
||||
Article 6 : Responsabilité des Parties
|
||||
Dans la limite maximale admise par le droit français, le Domiciliataire décline toute responsabilité à l’égard du Domicilié en raison de la perte ou d’un dommage subi par le client en relation avec le présent contrat, avec les prestations, le ou les espaces, à moins que la perte ou le dommage ne résulte d’un acte intentionnel ou d’une négligence du Domiciliataire.
|
||||
Le Domiciliataire décline toute responsabilité en raison de la perte résultant d’un manquement relatif à la fourniture d’une prestation par suite d’une panne mécanique, d’une grève, de la déchéance des droits du Domiciliataire sur les espaces ou pour toute autre raison à moins que le Domiciliataire n’ait agi intentionnellement ou par négligence.
|
||||
En tout état de cause, le Domiciliataire ne sera responsable d’une perte ou dun dommage que si le Domicilié len avise par écrit et lui octroie un délai qui ne peut être inférieur à 20 (vingt) jours pour y remédier.
|
||||
Si le Domicilié considère que le Domiciliataire a failli dans la fourniture des prestations prévues à l’article 2.1.2 des conditions particulières du contrat, le Domicilié devra en aviser le Domiciliataire par écrit et lui octroyer un délai qui ne peut être inférieur à 20 (vingt) jours afin que le Domiciliataire puisse y remédier.
|
||||
En tout état de cause, seuls les préjudices directs pourront permettre l’engagement de la responsabilité des parties, étant précisé que pour ce qui est de la responsabilité du Domiciliataire, cette dernière ne pourra être supérieure au total du montant des sommes encaissées par ce dernier et payées par le Domicilié dans le cadre de l’exécution du contrat.
|
||||
|
||||
Article 7 : Assurance
|
||||
Le Domicilié est responsable du matériel qu’il entrepose ${adresse_de_domiciliation}
|
||||
Le Domiciliataire ne pourra être tenu responsable d’un vol dans les locaux.
|
||||
Il est donc vivement conseillé au Domicilié de s’assurer pour son activité professionnelle (assurance civile professionnelle) et pour les espaces qu’il occupe (assurance multirisques bureaux).
|
||||
|
||||
Article 8 : Élection de domicile
|
||||
Les parties font élection de domicile au ${adresse_de_domiciliation}.
|
||||
|
||||
Article 9 : Différend - Attribution de juridiction
|
||||
Tout litige pouvant survenir entre les parties à l’occasion de l’exécution du présent contrat devra être porté devant le Tribunal de Commerce de Paris.
|
||||
|
||||
Article 10 : Confidentialité
|
||||
Les parties s’engagent à traiter comme confidentielles toutes informations qu’elles seraient amenées à obtenir dans le cadre de ce contrat.`}</Text>
|
||||
),
|
||||
},
|
||||
SignatureForm(),
|
||||
{
|
||||
title: "Pour le responsable",
|
||||
content: (
|
||||
<>
|
||||
<Image
|
||||
src={`data:image/png;base64,${presidentSignatureBase64}`}
|
||||
style={{ width: 200 }}
|
||||
/>
|
||||
</>
|
||||
),
|
||||
},
|
||||
].filter(({ condition = true }) => condition);
|
||||
|
||||
return (
|
||||
<Document>
|
||||
<Page style={Styles.body}>
|
||||
{sections.map((section, index) => (
|
||||
<View key={index} style={Styles.section}>
|
||||
{section.title && (
|
||||
<Text style={Styles.sectionTitle}>{section.title}</Text>
|
||||
)}
|
||||
{section.content}
|
||||
</View>
|
||||
))}
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
};
|
||||
|
||||
export default Contract;
|
||||
@@ -0,0 +1,206 @@
|
||||
import React from "react";
|
||||
import { Document, Page, Text, View, Font, Image } from "@react-pdf/renderer";
|
||||
import moment from "moment-timezone";
|
||||
import {
|
||||
DisplayCustomerInformation,
|
||||
formatPrice,
|
||||
RenderCompanyLogo,
|
||||
RenderTermsOfSale,
|
||||
Styles,
|
||||
} from "./SharedElements";
|
||||
|
||||
Font.register({
|
||||
family: "Helvetica",
|
||||
fonts: [
|
||||
{ src: "https://fonts.gstatic.com/s/helvetica/v15/Helvetica-Regular.ttf" },
|
||||
{
|
||||
src: "https://fonts.gstatic.com/s/helvetica/v15/Helvetica-Bold.ttf",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const Invoice = ({
|
||||
itemList = [],
|
||||
billingDetails = {},
|
||||
|
||||
number = "-",
|
||||
|
||||
paymentTimestamp = null,
|
||||
creationTimestamp = new Date().toLocaleDateString("fr-FR"),
|
||||
|
||||
appConfig = {},
|
||||
}) => {
|
||||
const { company } = appConfig;
|
||||
|
||||
const subTotalNoTax = itemList.reduce(
|
||||
(sum, item) => sum + (item?.unitPrice || 0) * (item?.quantity || 0),
|
||||
0
|
||||
);
|
||||
|
||||
let taxList = {};
|
||||
|
||||
itemList.forEach((item) => {
|
||||
const { taxBreakdown = [] } = item;
|
||||
taxBreakdown.forEach((tax) => {
|
||||
const { type, amount, displayName, percentageDecimal = null } = tax;
|
||||
if (type) {
|
||||
taxList[type] = {
|
||||
displayName,
|
||||
percentageDecimal,
|
||||
amount: (taxList?.[type]?.amount || 0) + amount,
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const montantTotalTTC =
|
||||
subTotalNoTax +
|
||||
Object.values(taxList).reduce((sum, tax) => sum + tax.amount, 0);
|
||||
|
||||
const taxExemptionLegalString = "";
|
||||
|
||||
const sections = [
|
||||
{
|
||||
title: `Facture n°${number}`,
|
||||
content: <RenderCompanyLogo logoBase64={company?.logoBase64} />,
|
||||
},
|
||||
{
|
||||
title: null,
|
||||
content: (
|
||||
<View style={Styles.headerRow}>
|
||||
<View style={Styles.leftColumn}>
|
||||
{[
|
||||
`Date d'émission: ${moment(creationTimestamp)
|
||||
.tz("Europe/Paris")
|
||||
.format("DD/MM/YYYY à HH:mm")}`,
|
||||
paymentTimestamp
|
||||
? `Date de paiement: ${moment(paymentTimestamp)
|
||||
.tz("Europe/Paris")
|
||||
.format("DD/MM/YYYY à HH:mm")}`
|
||||
: null,
|
||||
"Statut: payée",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.map((line, index) => (
|
||||
<Text key={index} style={Styles.textParagraph}>
|
||||
{line}
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<DisplayCustomerInformation billingDetails={billingDetails} />
|
||||
</View>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Détails des prestations",
|
||||
content: (
|
||||
<View style={Styles.table}>
|
||||
<View style={Styles.tableRow}>
|
||||
<Text style={Styles.tableColHeaderLeft}>Description</Text>
|
||||
<Text style={Styles.tableColHeaderRight}>Prix HT</Text>
|
||||
</View>
|
||||
{itemList.map((item, index) => (
|
||||
<View key={index} style={Styles.tableRow}>
|
||||
<View>
|
||||
<Text style={Styles.tableColLeft}>{item.description}</Text>
|
||||
</View>
|
||||
<Text style={Styles.tableColRight}>
|
||||
{formatPrice({ amount: item.unitPrice * item.quantity })}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
<View style={Styles.summaryRow}>
|
||||
<Text style={Styles.summaryLabel}>Sous-total HT :</Text>
|
||||
<Text style={Styles.summaryValue}>
|
||||
{formatPrice({ amount: subTotalNoTax })}
|
||||
</Text>
|
||||
</View>
|
||||
{Object.values(taxList)?.map((itemTax, index) => (
|
||||
<View key={index} style={Styles.summaryRow}>
|
||||
<Text style={Styles.summaryLabel}>
|
||||
{itemTax?.displayName}{" "}
|
||||
{itemTax?.percentageDecimal
|
||||
? `(${itemTax?.percentageDecimal}%)`
|
||||
: ""}{" "}
|
||||
:
|
||||
</Text>
|
||||
<Text style={Styles.summaryValue}>
|
||||
{formatPrice({ amount: itemTax.amount })}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
<View style={Styles.summaryRow}>
|
||||
<Text style={Styles.summaryLabel}>Total TTC :</Text>
|
||||
<Text style={Styles.summaryValue}>
|
||||
{formatPrice({ amount: montantTotalTTC })}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Mentions légales",
|
||||
content: (
|
||||
<Text style={Styles.textParagraph}>
|
||||
{`En cas de retard de paiement, une pénalité de 3 fois le taux d'intérêt légal sera appliquée, à laquelle s'ajoutera une indemnité forfaitaire pour frais de recouvrement de 40€. ${taxExemptionLegalString}`}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
RenderTermsOfSale(),
|
||||
].filter(({ content }) => content);
|
||||
|
||||
return (
|
||||
<Document>
|
||||
<Page style={Styles.body}>
|
||||
{sections.map((section, index) => (
|
||||
<View key={index} style={Styles.section}>
|
||||
{section.title && (
|
||||
<Text style={Styles.sectionTitle}>{section.title}</Text>
|
||||
)}
|
||||
{section.content}
|
||||
</View>
|
||||
))}
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
};
|
||||
|
||||
// Liste des taux de TVA standard pour les pays de l'UE
|
||||
const euVatRates = {
|
||||
AT: 0.2, // Autriche
|
||||
BE: 0.21, // Belgique
|
||||
BG: 0.2, // Bulgarie
|
||||
HR: 0.25, // Croatie
|
||||
CY: 0.19, // Chypre
|
||||
CZ: 0.21, // République tchèque
|
||||
DK: 0.25, // Danemark
|
||||
EE: 0.2, // Estonie
|
||||
FI: 0.24, // Finlande
|
||||
FR: 0.2, // France
|
||||
DE: 0.19, // Allemagne
|
||||
GR: 0.24, // Grèce
|
||||
HU: 0.27, // Hongrie
|
||||
IE: 0.23, // Irlande
|
||||
IT: 0.22, // Italie
|
||||
LV: 0.21, // Lettonie
|
||||
LT: 0.21, // Lituanie
|
||||
LU: 0.17, // Luxembourg
|
||||
MT: 0.18, // Malte
|
||||
NL: 0.21, // Pays-Bas
|
||||
PL: 0.23, // Pologne
|
||||
PT: 0.23, // Portugal
|
||||
RO: 0.19, // Roumanie
|
||||
SK: 0.2, // Slovaquie
|
||||
SI: 0.22, // Slovénie
|
||||
ES: 0.21, // Espagne
|
||||
SE: 0.25, // Suède
|
||||
};
|
||||
|
||||
// Fonction pour vérifier si le pays fait partie de l'UE
|
||||
function isCustomerFromEurope({ countryCode }) {
|
||||
return euVatRates.hasOwnProperty(countryCode);
|
||||
}
|
||||
|
||||
export default Invoice;
|
||||
@@ -0,0 +1,199 @@
|
||||
import React from "react";
|
||||
import { Document, Page, Text, View, Font, Image } from "@react-pdf/renderer";
|
||||
import {
|
||||
DisplayCustomerInformation,
|
||||
formatPrice,
|
||||
RenderCompanyInformation,
|
||||
RenderCompanyLogo,
|
||||
RenderTermsOfSale,
|
||||
SignatureForm,
|
||||
Styles,
|
||||
} from "./SharedElements";
|
||||
import moment from "moment-timezone";
|
||||
|
||||
Font.register({
|
||||
family: "Helvetica",
|
||||
fonts: [
|
||||
{ src: "https://fonts.gstatic.com/s/helvetica/v15/Helvetica-Regular.ttf" },
|
||||
{
|
||||
src: "https://fonts.gstatic.com/s/helvetica/v15/Helvetica-Bold.ttf",
|
||||
fontWeight: "bold",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const Quote = ({
|
||||
itemList = [],
|
||||
project = {},
|
||||
billingDetails = {},
|
||||
|
||||
quoteNumber = "-",
|
||||
creationTimestamp = new Date(),
|
||||
|
||||
appConfig = {},
|
||||
|
||||
frequentlyAskedQuestion = [],
|
||||
planning = [],
|
||||
|
||||
paymentTerms = "",
|
||||
}) => {
|
||||
const { summary = "" } = project;
|
||||
const { dailyRate = {}, company } = appConfig;
|
||||
|
||||
const dailyRateEuro = dailyRate?.amount || 500;
|
||||
|
||||
const TVA_RATE = 0.2;
|
||||
const sousTotalHT = itemList.reduce(
|
||||
(sum, phase) => sum + phase.totalNumberOfDay * dailyRateEuro,
|
||||
0
|
||||
);
|
||||
const tva = sousTotalHT * TVA_RATE;
|
||||
const montantTotalTTC = sousTotalHT + tva;
|
||||
|
||||
const sections = [
|
||||
{
|
||||
title: `Devis n°${quoteNumber}`,
|
||||
content: <RenderCompanyLogo logoBase64={company?.logoBase64} />,
|
||||
},
|
||||
{
|
||||
title: null,
|
||||
content: (
|
||||
<View style={Styles.headerRow}>
|
||||
<View style={Styles.leftColumn}>
|
||||
<Text style={Styles.textParagraph}>
|
||||
Date d'émission:{" "}
|
||||
{moment(creationTimestamp)
|
||||
.tz("Europe/Paris")
|
||||
.format("DD/MM/YYYY à HH:mm")}
|
||||
{"\n"}
|
||||
Période de validité: 60 jours
|
||||
</Text>
|
||||
</View>
|
||||
|
||||
{Object.keys(billingDetails).length > 0 && (
|
||||
<DisplayCustomerInformation billingDetails={billingDetails} />
|
||||
)}
|
||||
</View>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Résumé du Projet",
|
||||
content: <Text style={Styles.textParagraph}>{summary}</Text>,
|
||||
condition: summary.length > 0,
|
||||
},
|
||||
{
|
||||
title: "Détails des Prestations",
|
||||
content: (
|
||||
<View style={Styles.table}>
|
||||
<View style={Styles.tableRow}>
|
||||
<Text style={Styles.tableColHeaderLeft}>Description</Text>
|
||||
<Text style={Styles.tableColHeaderRight}>Prix HT</Text>
|
||||
</View>
|
||||
{itemList.map((phase, index) => (
|
||||
<View key={index} style={Styles.tableRow}>
|
||||
<View>
|
||||
<Text style={Styles.tableColLeft}>{phase.title}</Text>
|
||||
<Text style={Styles.tableColLeft}>{phase.description}</Text>
|
||||
<Text style={Styles.tableColLeft}>
|
||||
Nombre de jours: {phase?.totalNumberOfDay || 0}
|
||||
</Text>
|
||||
</View>
|
||||
<Text style={Styles.tableColRight}>
|
||||
{formatPrice({
|
||||
amount: phase.totalNumberOfDay * dailyRateEuro,
|
||||
})}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
<View style={Styles.summaryRow}>
|
||||
<Text style={Styles.summaryLabel}>Sous-total HT :</Text>
|
||||
<Text style={Styles.summaryValue}>
|
||||
{formatPrice({ amount: sousTotalHT })}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={Styles.summaryRow}>
|
||||
<Text style={Styles.summaryLabel}>TVA (20%) :</Text>
|
||||
<Text style={Styles.summaryValue}>
|
||||
{formatPrice({ amount: tva })}
|
||||
</Text>
|
||||
</View>
|
||||
<View style={Styles.summaryRow}>
|
||||
<Text style={Styles.summaryLabel}>Total TTC :</Text>
|
||||
<Text style={Styles.summaryValue}>
|
||||
{formatPrice({ amount: montantTotalTTC })}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
condition: itemList.length > 0,
|
||||
},
|
||||
RenderCompanyInformation({ company }),
|
||||
{
|
||||
title: "Modalités de paiement",
|
||||
content: (
|
||||
<Text style={Styles.textParagraph}>
|
||||
{paymentTerms ||
|
||||
"Acompte de 40% à la signature du devis, 30% après livraison du design et solde à la livraison."}
|
||||
</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "Questions fréquentes",
|
||||
content: (
|
||||
<View>
|
||||
{frequentlyAskedQuestion.map((faq, index) => (
|
||||
<View key={index}>
|
||||
<Text style={Styles.textParagraph}>{faq.question}</Text>
|
||||
<Text style={{ ...Styles.textParagraph, opacity: 0.5 }}>
|
||||
{faq.answer}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
),
|
||||
condition: frequentlyAskedQuestion.length > 0,
|
||||
},
|
||||
{
|
||||
title: "Planning prévisionnel",
|
||||
content: (
|
||||
<View>
|
||||
<Text style={Styles.textParagraph}>
|
||||
Planning estimatif des semaines de travail, sujet à modification et
|
||||
ajustement périodique.
|
||||
</Text>
|
||||
|
||||
{planning.map((event, index) => (
|
||||
<View key={index}>
|
||||
<Text style={Styles.textParagraph}>
|
||||
{`${event?.weekNumberStart} - ${event?.weekNumberEnd}: ${event.title}`}
|
||||
</Text>
|
||||
<Text style={{ ...Styles.textParagraph, opacity: 0.5 }}>
|
||||
{event.description}
|
||||
</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
),
|
||||
condition: planning.length > 0,
|
||||
},
|
||||
RenderTermsOfSale(),
|
||||
SignatureForm(),
|
||||
].filter(({ condition = true }) => condition);
|
||||
|
||||
return (
|
||||
<Document>
|
||||
<Page style={Styles.body}>
|
||||
{sections.map((section, index) => (
|
||||
<View key={index} style={Styles.section}>
|
||||
{section.title && (
|
||||
<Text style={Styles.sectionTitle}>{section.title}</Text>
|
||||
)}
|
||||
{section.content}
|
||||
</View>
|
||||
))}
|
||||
</Page>
|
||||
</Document>
|
||||
);
|
||||
};
|
||||
|
||||
export default Quote;
|
||||
@@ -0,0 +1,147 @@
|
||||
import React from "react";
|
||||
import { Text, View, Image, StyleSheet } from "@react-pdf/renderer";
|
||||
import numbro from "numbro";
|
||||
|
||||
const DisplayCustomerInformation = ({ billingDetails } = {}) => {
|
||||
return (
|
||||
<View style={Styles.rightColumn}>
|
||||
{[
|
||||
`Client: ${billingDetails.legalName || "NC"}`,
|
||||
billingDetails.billingAddress,
|
||||
// `${billingDetails.zipCode}, ${billingDetails.city}, ${billingDetails.countryCode}`,
|
||||
`TVA: ${billingDetails.taxIdentificationNumber || "NC"}`,
|
||||
`SIREN: ${billingDetails.SIREN || "NC"}`,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.map((line, index) => (
|
||||
<Text key={index} style={Styles.textParagraph}>
|
||||
{line}
|
||||
</Text>
|
||||
))}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
const RenderCompanyLogo = ({ logoBase64 } = {}) => {
|
||||
return (
|
||||
<Image src={`data:image/png;base64,${logoBase64}`} style={{ width: 200 }} />
|
||||
);
|
||||
};
|
||||
|
||||
const RenderTermsOfSale = () => {
|
||||
return {
|
||||
title: "Conditions générales de vente",
|
||||
content: <Text style={Styles.textParagraph}>{"ICI TES CGV"}</Text>,
|
||||
};
|
||||
};
|
||||
|
||||
const RenderCompanyInformation = ({ company }) => {
|
||||
return {
|
||||
title: "Informations du prestataire",
|
||||
content: (
|
||||
<Text style={Styles.textParagraph}>
|
||||
{`${company?.name}, ${company?.address}, ${company?.postalCode || "-"}, ${company?.city || "-"},\nSIRET: ${company?.identifier}, RCS: ${company?.city} ${company?.RCS}, TVA: ${company?.taxIdentifier || "-"}.\nPour toute question, vous pourrez nous contacter au ${company?.phoneNumber} ou à l'adresse ${company?.supportEmail}.`}
|
||||
</Text>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const SignatureForm = () => {
|
||||
return {
|
||||
title: "Signature du client",
|
||||
content: (
|
||||
<>
|
||||
<Text style={Styles.textParagraph}>
|
||||
Fait à {"{{Ville de signature;font_size=12}}"}, le
|
||||
{" {{Date;type=datenow;font_size=12}}"}
|
||||
</Text>
|
||||
|
||||
<Text style={Styles.textParagraph}>
|
||||
Nom et prénom du signataire:{" "}
|
||||
{"{{Nom et Prénom du signataire;font_size=12}}"}
|
||||
</Text>
|
||||
|
||||
<Text style={Styles.textParagraph}>
|
||||
Fonction du signataire: {"{{Fonction du signataire;font_size=12}}"}
|
||||
</Text>
|
||||
|
||||
<Text style={Styles.textParagraph}>
|
||||
Téléphone: {"{{type=phone;required=true;font_size=12}}"}
|
||||
</Text>
|
||||
|
||||
<Text style={Styles.textParagraph}>Signature:</Text>
|
||||
|
||||
<View style={Styles.signatureBox}>
|
||||
<Text>{"{{Sign;type=signature;width=200;height=50}}"}</Text>
|
||||
</View>
|
||||
</>
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
const formatPrice = ({ amount = 0, average = false }) =>
|
||||
`${numbro(amount || 0).format({
|
||||
spaceSeparated: true,
|
||||
thousandSeparated: true,
|
||||
average,
|
||||
mantissa: average ? 1 : 2,
|
||||
})} €`;
|
||||
|
||||
const Styles = StyleSheet.create({
|
||||
body: {
|
||||
padding: 40,
|
||||
fontFamily: "Helvetica",
|
||||
fontSize: 12,
|
||||
lineHeight: 1.5,
|
||||
color: "#000",
|
||||
},
|
||||
section: { marginBottom: 30 },
|
||||
sectionTitle: { fontSize: 14, opacity: 0.5, marginBottom: 5 },
|
||||
textParagraph: { marginBottom: 5 },
|
||||
headerRow: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
leftColumn: { width: "50%", textAlign: "left" },
|
||||
rightColumn: { width: "50%", textAlign: "right" },
|
||||
table: { marginBottom: 20 },
|
||||
tableRow: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "space-between",
|
||||
paddingVertical: 10,
|
||||
borderBottomWidth: 1,
|
||||
borderBottomColor: "#ccc",
|
||||
},
|
||||
tableColHeaderLeft: { width: "70%", fontWeight: "bold", textAlign: "left" },
|
||||
tableColHeaderRight: { width: "30%", fontWeight: "bold", textAlign: "right" },
|
||||
tableColLeft: { width: "70%", textAlign: "left" },
|
||||
tableColRight: { width: "30%", textAlign: "right" },
|
||||
summaryRow: {
|
||||
flexDirection: "row",
|
||||
justifyContent: "flex-end",
|
||||
marginTop: 10,
|
||||
},
|
||||
summaryLabel: {
|
||||
width: "70%",
|
||||
textAlign: "right",
|
||||
paddingRight: 10,
|
||||
fontWeight: "bold",
|
||||
},
|
||||
summaryValue: { width: "30%", textAlign: "right" },
|
||||
signatureBox: {
|
||||
width: 200,
|
||||
height: 50,
|
||||
borderWidth: 1,
|
||||
borderStyle: "solid",
|
||||
},
|
||||
});
|
||||
|
||||
export {
|
||||
Styles,
|
||||
formatPrice,
|
||||
DisplayCustomerInformation,
|
||||
RenderTermsOfSale,
|
||||
RenderCompanyLogo,
|
||||
RenderCompanyInformation,
|
||||
SignatureForm,
|
||||
};
|
||||
@@ -0,0 +1,95 @@
|
||||
const admin = require("firebase-admin");
|
||||
const moment = require("moment");
|
||||
const { onCall, HttpsError } = require("firebase-functions/v2/https");
|
||||
const functions = require("firebase-functions/v1");
|
||||
|
||||
const { generateDocument } = require("./utils/helpers.js");
|
||||
const { logoBase64 } = require("./data/logoBase64.js");
|
||||
|
||||
const documentTemplateList = {
|
||||
QUOTE: {
|
||||
path: "./components/Quote.js",
|
||||
},
|
||||
INVOICE: {
|
||||
path: "./components/Invoice.js",
|
||||
},
|
||||
CONTRACT: {
|
||||
path: "./components/Contract.js",
|
||||
},
|
||||
};
|
||||
|
||||
const appConfig = {
|
||||
company: {
|
||||
logoBase64,
|
||||
name: "SAS StarsClick",
|
||||
address: "8 rue de la Paix",
|
||||
postalCode: "75002",
|
||||
city: "Paris",
|
||||
identifier: "123456789",
|
||||
RCS: "123 456 789",
|
||||
taxIdentifier: "FR 12 345 678 912",
|
||||
phoneNumber: "01 23 45 67 89",
|
||||
supportEmail: "contact@starsclick.fr",
|
||||
},
|
||||
};
|
||||
|
||||
exports.generateDocumentFromTemplate = onCall(
|
||||
async ({ auth = {}, data = {} }) => {
|
||||
try {
|
||||
const { type, documentData = {} } = data;
|
||||
|
||||
const { path } = documentTemplateList[type] || {};
|
||||
|
||||
if (!path) {
|
||||
throw new Error("Invalid document type");
|
||||
}
|
||||
|
||||
const { default: DocumentImport } = await import(path);
|
||||
|
||||
const { downloadURL } = await generateDocument({
|
||||
template: DocumentImport,
|
||||
data: {
|
||||
...documentData,
|
||||
creationTimestamp: moment().format("DD/MM/YYYY"),
|
||||
},
|
||||
filePath: `documents/${moment().format("DD-MM-YYYY-HH-mm-ss")}.pdf`,
|
||||
});
|
||||
|
||||
console.log(`PDF URL: ${downloadURL}`);
|
||||
|
||||
return { documentUrl: downloadURL };
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
exports.testDocumentGeneration = functions
|
||||
.runWith({ memory: "512MB" })
|
||||
.https.onRequest(async (req, res) => {
|
||||
try {
|
||||
// const { type } = req.query;
|
||||
|
||||
const type = "INVOICE";
|
||||
|
||||
const { path } = documentTemplateList[type] || {};
|
||||
|
||||
if (!path) {
|
||||
throw new Error("Invalid document type");
|
||||
}
|
||||
|
||||
const { default: DocumentTemplate } = await import(path);
|
||||
|
||||
const { downloadURL } = await generateDocument({
|
||||
template: DocumentTemplate,
|
||||
data: {
|
||||
appConfig,
|
||||
},
|
||||
filePath: `documents/${moment().format("DD-MM-YYYY-HH-mm-ss")}.pdf`,
|
||||
});
|
||||
|
||||
console.log(`PDF URL: ${downloadURL}`);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,163 @@
|
||||
const functions = require("firebase-functions");
|
||||
|
||||
const { refsList } = require("..");
|
||||
const { onCall } = require("firebase-functions/v2/https");
|
||||
|
||||
const isStripeProd = false;
|
||||
|
||||
const stripe = require("stripe")(isStripeProd ? "sk_live_..." : "sk_test_...");
|
||||
|
||||
const baseURL = isStripeProd ? "https://.../" : "https://.../";
|
||||
|
||||
exports.createCheckoutSession = onCall(async ({ auth = {}, data = {} }) => {
|
||||
try {
|
||||
const { userID, productList = [] } = data || {};
|
||||
|
||||
console.log("productList", productList);
|
||||
|
||||
if (!userID) {
|
||||
throw new Error("userID is required");
|
||||
}
|
||||
|
||||
const currentUserRef = refsList.users.doc(userID);
|
||||
const userData = (await currentUserRef.get())?.data() || null;
|
||||
|
||||
let stripeCustomerID;
|
||||
const { firstName } = userData || {};
|
||||
|
||||
let updatedCustomerData = {
|
||||
name: firstName,
|
||||
email: auth.token.email,
|
||||
};
|
||||
|
||||
if (userData?.stripeCustomerID) {
|
||||
stripeCustomerID = userData.stripeCustomerID;
|
||||
} else {
|
||||
const customer = await stripe.customers.create({
|
||||
...updatedCustomerData,
|
||||
});
|
||||
stripeCustomerID = customer.id;
|
||||
await currentUserRef.update({ stripeCustomerID });
|
||||
}
|
||||
|
||||
await stripe.customers.update(stripeCustomerID, updatedCustomerData);
|
||||
|
||||
let line_items = productList.map((product) => ({
|
||||
price: product.priceID,
|
||||
quantity: product.quantity,
|
||||
}));
|
||||
|
||||
const hasSubscription = productList.some((product) => product.isRenewable);
|
||||
|
||||
let checkoutObject = {
|
||||
line_items: line_items,
|
||||
mode: hasSubscription ? "subscription" : "payment", // Use "subscription" to support both one-time and subscription items
|
||||
ui_mode: "embedded",
|
||||
return_url: `${baseURL}explore`,
|
||||
customer: stripeCustomerID,
|
||||
invoice_creation: {
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
|
||||
const session = await stripe.checkout.sessions.create(checkoutObject);
|
||||
|
||||
return session;
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
throw new functions.https.HttpsError("permission-denied", e.message);
|
||||
}
|
||||
});
|
||||
|
||||
exports.getPremiumStatus = onCall(async ({ auth = {}, data = {} }) => {
|
||||
// Vérifiez l'authentification de l'utilisateur
|
||||
if (!auth) {
|
||||
throw new functions.https.HttpsError(
|
||||
"unauthenticated",
|
||||
"The function must be called while authenticated."
|
||||
);
|
||||
}
|
||||
|
||||
const uid = auth.uid;
|
||||
|
||||
try {
|
||||
// Récupérer l'ID client Stripe depuis Firestore (supposons que vous l'avez stocké précédemment)
|
||||
const userDoc = await refsList.users.doc(uid).get();
|
||||
const stripeCustomerID = userDoc.data()?.stripeCustomerID || null;
|
||||
|
||||
if (!stripeCustomerID) {
|
||||
throw new functions.https.HttpsError(
|
||||
"not-found",
|
||||
"Stripe customer ID not found."
|
||||
);
|
||||
}
|
||||
|
||||
// Récupérer les abonnements de l'utilisateur
|
||||
const subscriptions = await stripe.subscriptions.list({
|
||||
customer: stripeCustomerID,
|
||||
status: "all",
|
||||
});
|
||||
|
||||
// Récupérer les achats de produits (Invoices)
|
||||
const invoices = await stripe.invoices.list({
|
||||
customer: stripeCustomerID,
|
||||
});
|
||||
|
||||
console.log("subscriptions", subscriptions);
|
||||
console.log("invoices", invoices);
|
||||
|
||||
return {
|
||||
subscriptions: subscriptions.data,
|
||||
invoices: invoices.data,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error fetching Stripe details:", error);
|
||||
return {
|
||||
subscriptions: [],
|
||||
invoices: [],
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
exports.createStripeCustomerPortalSession = onCall(
|
||||
async ({ auth = {}, data = {} }) => {
|
||||
// Vérifiez l'authentification de l'utilisateur
|
||||
if (!auth) {
|
||||
throw new functions.https.HttpsError(
|
||||
"unauthenticated",
|
||||
"The function must be called while authenticated."
|
||||
);
|
||||
}
|
||||
|
||||
const uid = auth.uid;
|
||||
|
||||
try {
|
||||
// Récupérer l'ID client Stripe depuis Firestore (supposons que vous l'avez stocké précédemment)
|
||||
const userDoc = await refsList.users.doc(uid).get();
|
||||
const stripeCustomerID = userDoc.data().stripeCustomerID;
|
||||
|
||||
if (!stripeCustomerID) {
|
||||
throw new functions.https.HttpsError(
|
||||
"not-found",
|
||||
"Stripe customer ID not found."
|
||||
);
|
||||
}
|
||||
|
||||
// Créer une session de portail client
|
||||
const session = await stripe.billingPortal.sessions.create({
|
||||
customer: stripeCustomerID,
|
||||
return_url: baseURL, // Remplacez par l'URL de retour de votre application
|
||||
});
|
||||
|
||||
return {
|
||||
url: session.url,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error("Error creating Stripe customer portal session:", error);
|
||||
throw new functions.https.HttpsError(
|
||||
"internal",
|
||||
"Unable to create Stripe customer portal session."
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,66 @@
|
||||
const admin = require("firebase-admin");
|
||||
const { onCall, HttpsError } = require("firebase-functions/v2/https");
|
||||
const { onDocumentWritten } = require("firebase-functions/v2/firestore");
|
||||
|
||||
const { refsList } = require("..");
|
||||
|
||||
exports.userUpdateListener = onDocumentWritten(
|
||||
"users/{userID}",
|
||||
async (userSnap) => {
|
||||
try {
|
||||
const { userID } = userSnap.params;
|
||||
|
||||
const currentData = userSnap?.data?.after?.data() || null;
|
||||
const previousData = userSnap?.data?.before?.data() || null;
|
||||
|
||||
if (!currentData) {
|
||||
await admin.auth().deleteUser(userID);
|
||||
}
|
||||
|
||||
if (!previousData) {
|
||||
} else if (!!previousData && !!currentData) {
|
||||
const { email = null } = currentData;
|
||||
|
||||
try {
|
||||
if (previousData?.email && previousData?.email !== email && email) {
|
||||
await admin.auth().updateUser(userID, {
|
||||
email,
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
exports.deleteAccount = onCall(async ({ auth = {}, data = {} }) => {
|
||||
try {
|
||||
if (!auth.uid) {
|
||||
throw new HttpsError(
|
||||
"unauthenticated",
|
||||
"Vous devez être connecté pour effectuer cette action."
|
||||
);
|
||||
}
|
||||
|
||||
const userDoc = await refsList.users.doc(auth.uid).get();
|
||||
|
||||
if (!userDoc.exists) {
|
||||
throw new HttpsError("not-found", "L'utilisateur n'existe pas.");
|
||||
}
|
||||
|
||||
await admin.auth().deleteUser(auth.uid);
|
||||
await userDoc.ref.delete();
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
throw new HttpsError(
|
||||
"internal",
|
||||
"Une erreur s'est produite lors de la suppression de votre compte."
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,154 @@
|
||||
const { Timestamp } = require("firebase-admin/firestore");
|
||||
const admin = require("firebase-admin");
|
||||
const React = require("react");
|
||||
|
||||
const { refsList, genkitInstance } = require("../..");
|
||||
|
||||
exports.getCompanyDataFromUserID = async ({ userID = null }) => {
|
||||
try {
|
||||
if (!userID) {
|
||||
throw new Error("No userID provided");
|
||||
}
|
||||
|
||||
const companySnapshot =
|
||||
(await refsList.companies.where("mainOwnerID", "==", userID).get())
|
||||
.docs?.[0] || null;
|
||||
|
||||
if (!companySnapshot) {
|
||||
throw new Error("Company not found");
|
||||
}
|
||||
|
||||
const companyData =
|
||||
{ ...companySnapshot.data(), companyID: companySnapshot.id } || null;
|
||||
|
||||
return companyData;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const logNewAction = async ({
|
||||
description = "",
|
||||
userID = null,
|
||||
isSuccess = true,
|
||||
}) => {
|
||||
try {
|
||||
if (!description?.length) {
|
||||
throw new Error("Missing required fields");
|
||||
}
|
||||
|
||||
await refsList.logs.add({
|
||||
isSuccess,
|
||||
description,
|
||||
userID,
|
||||
timestamp: Timestamp.now(),
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
exports.logNewAction = logNewAction;
|
||||
|
||||
// Fonction pour générer et uploader le PDF
|
||||
exports.generateDocument = async ({ template, data, filePath }) => {
|
||||
try {
|
||||
// Générer le PDF en buffer
|
||||
// Import dynamique de Quote en ESM
|
||||
const { pdf } = await import("@react-pdf/renderer");
|
||||
|
||||
// Générer le PDF en buffer
|
||||
const buffer = await pdf(
|
||||
React.createElement(template, {
|
||||
...data,
|
||||
})
|
||||
).toBuffer();
|
||||
|
||||
// Uploader le PDF dans Firebase Storage
|
||||
const bucket = admin.storage().bucket("gs://starsclick.appspot.com/");
|
||||
const file = bucket.file(filePath);
|
||||
|
||||
console.log(`Uploading PDF to Firebase Storage as ${filePath}...`);
|
||||
|
||||
const chunks = [];
|
||||
buffer.on("data", (chunk) => chunks.push(chunk));
|
||||
buffer.on("end", async () => {
|
||||
const pdfBuffer = Buffer.concat(chunks);
|
||||
|
||||
await file.save(pdfBuffer, {
|
||||
metadata: {
|
||||
contentType: "application/pdf",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Générer une URL signée
|
||||
const expiresAt = Date.now() + 1000 * 60 * 60 * 24 * 7; // Expire dans 7 jours
|
||||
const [url] = await file.getSignedUrl({
|
||||
action: "read",
|
||||
expires: expiresAt,
|
||||
});
|
||||
|
||||
console.log(`PDF uploaded to Firebase Storage as ${filePath}.`);
|
||||
console.log(`PDF URL: ${url}`);
|
||||
|
||||
return { downloadURL: url };
|
||||
} catch (error) {
|
||||
console.error("Error generating or uploading PDF:", error);
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
exports.generateAI = async ({ prompt = "", schema, config = {} }) => {
|
||||
if (prompt?.length < 1) {
|
||||
throw new Error(
|
||||
"Vous devez spécifier un prompt pour effectuer cette action."
|
||||
);
|
||||
}
|
||||
|
||||
let attempts = 0;
|
||||
let result;
|
||||
|
||||
while (attempts < 3) {
|
||||
try {
|
||||
const { output } = await genkitInstance.generate({
|
||||
system:
|
||||
"Voici un prompt, le contenu généré doit toujours être en français: ",
|
||||
prompt,
|
||||
config: {
|
||||
...config,
|
||||
},
|
||||
output: { schema },
|
||||
});
|
||||
|
||||
console.log("Résultat de la génération par IA:", output);
|
||||
|
||||
result = output;
|
||||
|
||||
await logNewAction({
|
||||
description: `Génération par IA réussie`,
|
||||
isSuccess: true,
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
attempts += 1;
|
||||
console.log(`Tentative ${attempts} échouée:`, error);
|
||||
|
||||
if (attempts >= 3) {
|
||||
await logNewAction({
|
||||
description: `Génération par IA échouée après plusieurs tentatives`,
|
||||
isSuccess: false,
|
||||
error,
|
||||
});
|
||||
|
||||
throw new Error(
|
||||
"Erreur lors de la génération par IA après plusieurs tentatives, veuillez réessayer."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
Web / API server started at 127.0.0.1:4000
|
||||
Web / API server started at ::1:4000
|
||||
@@ -0,0 +1,32 @@
|
||||
import { registerRootComponent } from "expo";
|
||||
import messaging from "@react-native-firebase/messaging";
|
||||
import notifee from "@notifee/react-native";
|
||||
|
||||
import App from "./App";
|
||||
|
||||
import {
|
||||
displayNotification,
|
||||
triggerEvent,
|
||||
} from "./src/providers/NotificationProvider";
|
||||
|
||||
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||
// It also ensures that whether you load the app in Expo Go or in a native build,
|
||||
// the environment is set up appropriately
|
||||
|
||||
messaging().setBackgroundMessageHandler(async (remoteMessage) => {
|
||||
console.log("Message handled in the background!", remoteMessage);
|
||||
await displayNotification(remoteMessage.data);
|
||||
});
|
||||
|
||||
notifee.onBackgroundEvent(triggerEvent);
|
||||
|
||||
function HeadlessCheck({ isHeadless }) {
|
||||
if (isHeadless) {
|
||||
// App has been launched in the background by iOS, ignore
|
||||
return null;
|
||||
}
|
||||
|
||||
return <App />;
|
||||
}
|
||||
|
||||
registerRootComponent(App, () => HeadlessCheck);
|
||||
@@ -0,0 +1,6 @@
|
||||
import { registerRootComponent } from "expo";
|
||||
import "./src/styles/css/global.css";
|
||||
|
||||
import App from "./App";
|
||||
|
||||
registerRootComponent(App);
|
||||
@@ -0,0 +1,30 @@
|
||||
# OSX
|
||||
#
|
||||
.DS_Store
|
||||
|
||||
# Xcode
|
||||
#
|
||||
build/
|
||||
*.pbxuser
|
||||
!default.pbxuser
|
||||
*.mode1v3
|
||||
!default.mode1v3
|
||||
*.mode2v3
|
||||
!default.mode2v3
|
||||
*.perspectivev3
|
||||
!default.perspectivev3
|
||||
xcuserdata
|
||||
*.xccheckout
|
||||
*.moved-aside
|
||||
DerivedData
|
||||
*.hmap
|
||||
*.ipa
|
||||
*.xcuserstate
|
||||
project.xcworkspace
|
||||
.xcode.env.local
|
||||
|
||||
# Bundle artifacts
|
||||
*.jsbundle
|
||||
|
||||
# CocoaPods
|
||||
/Pods/
|
||||
@@ -0,0 +1,11 @@
|
||||
# This `.xcode.env` file is versioned and is used to source the environment
|
||||
# used when running script phases inside Xcode.
|
||||
# To customize your local environment, you can create an `.xcode.env.local`
|
||||
# file that is not versioned.
|
||||
|
||||
# NODE_BINARY variable contains the PATH to the node executable.
|
||||
#
|
||||
# Customize the NODE_BINARY variable here.
|
||||
# For example, to use nvm with brew, add the following line
|
||||
# . "$(brew --prefix nvm)/nvm.sh" --no-use
|
||||
export NODE_BINARY=$(command -v node)
|
||||
@@ -0,0 +1,66 @@
|
||||
require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking")
|
||||
require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods")
|
||||
|
||||
require 'json'
|
||||
podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {}
|
||||
|
||||
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
|
||||
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
|
||||
|
||||
platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
|
||||
install! 'cocoapods',
|
||||
:deterministic_uuids => false
|
||||
|
||||
prepare_react_native_project!
|
||||
|
||||
target 'minuitstarter' do
|
||||
use_expo_modules!
|
||||
|
||||
if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1'
|
||||
config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"];
|
||||
else
|
||||
config_command = [
|
||||
'node',
|
||||
'--no-warnings',
|
||||
'--eval',
|
||||
'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))',
|
||||
'react-native-config',
|
||||
'--json',
|
||||
'--platform',
|
||||
'ios'
|
||||
]
|
||||
end
|
||||
|
||||
config = use_native_modules!(config_command)
|
||||
|
||||
use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
|
||||
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
|
||||
|
||||
use_react_native!(
|
||||
:path => config[:reactNativePath],
|
||||
:hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes',
|
||||
# An absolute path to your application root.
|
||||
:app_path => "#{Pod::Config.instance.installation_root}/..",
|
||||
:privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false',
|
||||
)
|
||||
|
||||
post_install do |installer|
|
||||
react_native_post_install(
|
||||
installer,
|
||||
config[:reactNativePath],
|
||||
:mac_catalyst_enabled => false,
|
||||
:ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true',
|
||||
)
|
||||
|
||||
# This is necessary for Xcode 14, because it signs resource bundles by default
|
||||
# when building for devices.
|
||||
installer.target_installation_results.pod_target_installation_results
|
||||
.each do |pod_name, target_installation_result|
|
||||
target_installation_result.resource_bundle_targets.each do |resource_bundle_target|
|
||||
resource_bundle_target.build_configurations.each do |config|
|
||||
config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"expo.jsEngine": "hermes",
|
||||
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
|
||||
"newArchEnabled": "false",
|
||||
"ios.deploymentTarget": "15.1",
|
||||
"ios.useFrameworks": "static",
|
||||
"apple.extraPods": "[]",
|
||||
"apple.ccacheEnabled": "false",
|
||||
"apple.privacyManifestAggregationEnabled": "true"
|
||||
}
|
||||
@@ -0,0 +1,477 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 46;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
|
||||
96905EF65AED1B983A6B3ABC /* libPods-minuitstarter.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-minuitstarter.a */; };
|
||||
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
|
||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
|
||||
86E87E7EF63547E884C7EE41 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 233FD88934724953B0CAFD13 /* GoogleService-Info.plist */; };
|
||||
724C02E629504D58B1E3E1C4 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB4CCD694E13411AA978E1F4 /* noop-file.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
13B07F961A680F5B00A75B9A /* minuitstarter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = minuitstarter.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = minuitstarter/AppDelegate.h; sourceTree = "<group>"; };
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = minuitstarter/AppDelegate.mm; sourceTree = "<group>"; };
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = minuitstarter/Images.xcassets; sourceTree = "<group>"; };
|
||||
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = minuitstarter/Info.plist; sourceTree = "<group>"; };
|
||||
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = minuitstarter/main.m; sourceTree = "<group>"; };
|
||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-minuitstarter.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-minuitstarter.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
6C2E3173556A471DD304B334 /* Pods-minuitstarter.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-minuitstarter.debug.xcconfig"; path = "Target Support Files/Pods-minuitstarter/Pods-minuitstarter.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
7A4D352CD337FB3A3BF06240 /* Pods-minuitstarter.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-minuitstarter.release.xcconfig"; path = "Target Support Files/Pods-minuitstarter/Pods-minuitstarter.release.xcconfig"; sourceTree = "<group>"; };
|
||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = minuitstarter/SplashScreen.storyboard; sourceTree = "<group>"; };
|
||||
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-minuitstarter/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
|
||||
233FD88934724953B0CAFD13 /* GoogleService-Info.plist */ = {isa = PBXFileReference; name = "GoogleService-Info.plist"; path = "minuitstarter/GoogleService-Info.plist"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = text.plist.xml; explicitFileType = undefined; includeInIndex = 0; };
|
||||
EB4CCD694E13411AA978E1F4 /* noop-file.swift */ = {isa = PBXFileReference; name = "noop-file.swift"; path = "minuitstarter/noop-file.swift"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.swift; explicitFileType = undefined; includeInIndex = 0; };
|
||||
900CF7F80C634C0FB32C45F6 /* minuitstarter-Bridging-Header.h */ = {isa = PBXFileReference; name = "minuitstarter-Bridging-Header.h"; path = "minuitstarter/minuitstarter-Bridging-Header.h"; sourceTree = "<group>"; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; explicitFileType = undefined; includeInIndex = 0; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
96905EF65AED1B983A6B3ABC /* libPods-minuitstarter.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
13B07FAE1A68108700A75B9A /* minuitstarter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB2F792B24A3F905000567C9 /* Supporting */,
|
||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */,
|
||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */,
|
||||
13B07FB51A68108700A75B9A /* Images.xcassets */,
|
||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||
13B07FB71A68108700A75B9A /* main.m */,
|
||||
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
|
||||
233FD88934724953B0CAFD13 /* GoogleService-Info.plist */,
|
||||
EB4CCD694E13411AA978E1F4 /* noop-file.swift */,
|
||||
900CF7F80C634C0FB32C45F6 /* minuitstarter-Bridging-Header.h */,
|
||||
);
|
||||
name = minuitstarter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
|
||||
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-minuitstarter.a */,
|
||||
);
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
name = Libraries;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83CBB9F61A601CBA00E9B192 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07FAE1A68108700A75B9A /* minuitstarter */,
|
||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||
83CBBA001A601CBA00E9B192 /* Products */,
|
||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||
D65327D7A22EEC0BE12398D9 /* Pods */,
|
||||
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */,
|
||||
);
|
||||
indentWidth = 2;
|
||||
sourceTree = "<group>";
|
||||
tabWidth = 2;
|
||||
usesTabs = 0;
|
||||
};
|
||||
83CBBA001A601CBA00E9B192 /* Products */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
13B07F961A680F5B00A75B9A /* minuitstarter.app */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
92DBD88DE9BF7D494EA9DA96 /* minuitstarter */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
|
||||
);
|
||||
name = minuitstarter;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BB2F792B24A3F905000567C9 /* Supporting */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BB2F792C24A3F905000567C9 /* Expo.plist */,
|
||||
);
|
||||
name = Supporting;
|
||||
path = minuitstarter/Supporting;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D65327D7A22EEC0BE12398D9 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6C2E3173556A471DD304B334 /* Pods-minuitstarter.debug.xcconfig */,
|
||||
7A4D352CD337FB3A3BF06240 /* Pods-minuitstarter.release.xcconfig */,
|
||||
);
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
92DBD88DE9BF7D494EA9DA96 /* minuitstarter */,
|
||||
);
|
||||
name = ExpoModulesProviders;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
13B07F861A680F5B00A75B9A /* minuitstarter */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "minuitstarter" */;
|
||||
buildPhases = (
|
||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
|
||||
13B07F871A680F5B00A75B9A /* Sources */,
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = minuitstarter;
|
||||
productName = minuitstarter;
|
||||
productReference = 13B07F961A680F5B00A75B9A /* minuitstarter.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
83CBB9F71A601CBA00E9B192 /* Project object */ = {
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastUpgradeCheck = 1130;
|
||||
TargetAttributes = {
|
||||
13B07F861A680F5B00A75B9A = {
|
||||
LastSwiftMigration = 1250;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "minuitstarter" */;
|
||||
compatibilityVersion = "Xcode 3.2";
|
||||
developmentRegion = en;
|
||||
hasScannedForEncodings = 0;
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
13B07F861A680F5B00A75B9A /* minuitstarter */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
|
||||
/* Begin PBXResourcesBuildPhase section */
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
|
||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
|
||||
86E87E7EF63547E884C7EE41 /* GoogleService-Info.plist in Resources */,
|
||||
5B79BC161B624D5A9880D7AA /* minuitstarter-Bridging-Header.h in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Bundle React Native code and images";
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
|
||||
};
|
||||
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
||||
"${PODS_ROOT}/Manifest.lock",
|
||||
);
|
||||
name = "[CP] Check Pods Manifest.lock";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
"$(DERIVED_FILE_DIR)/Pods-minuitstarter-checkManifestLockResult.txt",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-minuitstarter/Pods-minuitstarter-resources.sh",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/EXUpdates/EXUpdates.bundle",
|
||||
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
|
||||
);
|
||||
name = "[CP] Copy Pods Resources";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXUpdates.bundle",
|
||||
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-minuitstarter/Pods-minuitstarter-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
|
||||
13B07FC11A68108700A75B9A /* main.m in Sources */,
|
||||
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
|
||||
724C02E629504D58B1E3E1C4 /* noop-file.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-minuitstarter.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
"FB_SONARKIT_ENABLED=1",
|
||||
);
|
||||
INFOPLIST_FILE = minuitstarter/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.minuit.starter";
|
||||
PRODUCT_NAME = "minuitstarter";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
DEVELOPMENT_TEAM = W2QZ9CTMYJ;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = minuitstarter/minuitstarter-Bridging-Header.h;
|
||||
CODE_SIGN_ENTITLEMENTS = minuitstarter/minuitstarter.entitlements;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
13B07F951A680F5B00A75B9A /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-minuitstarter.release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
INFOPLIST_FILE = minuitstarter/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||
MARKETING_VERSION = 1.0;
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
"-ObjC",
|
||||
"-lc++",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "com.minuit.starter";
|
||||
PRODUCT_NAME = "minuitstarter";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
DEVELOPMENT_TEAM = W2QZ9CTMYJ;
|
||||
TARGETED_DEVICE_FAMILY = "1,2";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = minuitstarter/minuitstarter-Bridging-Header.h;
|
||||
CODE_SIGN_ENTITLEMENTS = minuitstarter/minuitstarter.entitlements;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
83CBBA201A601CBA00E9B192 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
ENABLE_TESTABILITY = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_DYNAMIC_NO_PIC = NO;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_OPTIMIZATION_LEVEL = 0;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"DEBUG=1",
|
||||
"$(inherited)",
|
||||
);
|
||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||
LIBRARY_SEARCH_PATHS = "\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SDKROOT = iphoneos;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
83CBBA211A601CBA00E9B192 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CLANG_ENABLE_OBJC_ARC = YES;
|
||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||
CLANG_WARN_COMMA = YES;
|
||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||
CLANG_WARN_EMPTY_BODY = YES;
|
||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||
CLANG_WARN_INT_CONVERSION = YES;
|
||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
|
||||
COPY_PHASE_STRIP = YES;
|
||||
ENABLE_NS_ASSERTIONS = NO;
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
|
||||
LIBRARY_SEARCH_PATHS = "\"$(inherited)\"";
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
SDKROOT = iphoneos;
|
||||
VALIDATE_PRODUCT = YES;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "minuitstarter" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
13B07F941A680F5B00A75B9A /* Debug */,
|
||||
13B07F951A680F5B00A75B9A /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "minuitstarter" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
83CBBA201A601CBA00E9B192 /* Debug */,
|
||||
83CBBA211A601CBA00E9B192 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
};
|
||||
rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1130"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "minuitstarter.app"
|
||||
BlueprintName = "minuitstarter"
|
||||
ReferencedContainer = "container:minuitstarter.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "00E356ED1AD99517003FC87E"
|
||||
BuildableName = "minuitstarterTests.xctest"
|
||||
BlueprintName = "minuitstarterTests"
|
||||
ReferencedContainer = "container:minuitstarter.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "minuitstarter.app"
|
||||
BlueprintName = "minuitstarter"
|
||||
ReferencedContainer = "container:minuitstarter.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
|
||||
BuildableName = "minuitstarter.app"
|
||||
BlueprintName = "minuitstarter"
|
||||
ReferencedContainer = "container:minuitstarter.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -0,0 +1,7 @@
|
||||
#import <RCTAppDelegate.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Expo/Expo.h>
|
||||
|
||||
@interface AppDelegate : EXAppDelegateWrapper
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,66 @@
|
||||
#import "AppDelegate.h"
|
||||
#import <Firebase/Firebase.h>
|
||||
|
||||
#import <React/RCTBundleURLProvider.h>
|
||||
#import <React/RCTLinkingManager.h>
|
||||
|
||||
@implementation AppDelegate
|
||||
|
||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||
{
|
||||
// @generated begin @react-native-firebase/app-didFinishLaunchingWithOptions - expo prebuild (DO NOT MODIFY) sync-ecd111c37e49fdd1ed6354203cd6b1e2a38cccda
|
||||
[FIRApp configure];
|
||||
// @generated end @react-native-firebase/app-didFinishLaunchingWithOptions
|
||||
self.moduleName = @"main";
|
||||
|
||||
// You can add your custom initial props in the dictionary below.
|
||||
// They will be passed down to the ViewController used by React Native.
|
||||
self.initialProps = @{};
|
||||
|
||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
}
|
||||
|
||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||
{
|
||||
return [self bundleURL];
|
||||
}
|
||||
|
||||
- (NSURL *)bundleURL
|
||||
{
|
||||
#if DEBUG
|
||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"];
|
||||
#else
|
||||
return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
|
||||
#endif
|
||||
}
|
||||
|
||||
// Linking API
|
||||
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options];
|
||||
}
|
||||
|
||||
// Universal Links
|
||||
- (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
|
||||
BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler];
|
||||
return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result;
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
|
||||
{
|
||||
return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
|
||||
{
|
||||
return [super application:application didFailToRegisterForRemoteNotificationsWithError:error];
|
||||
}
|
||||
|
||||
// Explicitly define remote notification delegates to ensure compatibility with some third-party libraries
|
||||
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
|
||||
{
|
||||
return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CLIENT_ID</key>
|
||||
<string>943006074419-h43c7p48nhn5f4td4oimv4dq7b1p3s29.apps.googleusercontent.com</string>
|
||||
<key>REVERSED_CLIENT_ID</key>
|
||||
<string>com.googleusercontent.apps.943006074419-h43c7p48nhn5f4td4oimv4dq7b1p3s29</string>
|
||||
<key>API_KEY</key>
|
||||
<string>AIzaSyAkl2RAzUCYTQCcYhSeij6-tXNk6z7121Y</string>
|
||||
<key>GCM_SENDER_ID</key>
|
||||
<string>943006074419</string>
|
||||
<key>PLIST_VERSION</key>
|
||||
<string>1</string>
|
||||
<key>BUNDLE_ID</key>
|
||||
<string>com.minuit.starter</string>
|
||||
<key>PROJECT_ID</key>
|
||||
<string>minuitcloud</string>
|
||||
<key>STORAGE_BUCKET</key>
|
||||
<string>minuitcloud.appspot.com</string>
|
||||
<key>IS_ADS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_ANALYTICS_ENABLED</key>
|
||||
<false></false>
|
||||
<key>IS_APPINVITE_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_GCM_ENABLED</key>
|
||||
<true></true>
|
||||
<key>IS_SIGNIN_ENABLED</key>
|
||||
<true></true>
|
||||
<key>GOOGLE_APP_ID</key>
|
||||
<string>1:943006074419:ios:5deb4cce66c04c09658546</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
After Width: | Height: | Size: 663 KiB |
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"filename": "App-Icon-1024x1024@1x.png",
|
||||
"idiom": "universal",
|
||||
"platform": "ios",
|
||||
"size": "1024x1024"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "expo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"colors": [
|
||||
{
|
||||
"color": {
|
||||
"components": {
|
||||
"alpha": "1.000",
|
||||
"blue": "0.0784313725490196",
|
||||
"green": "0.0470588235294118",
|
||||
"red": "0.0588235294117647"
|
||||
},
|
||||
"color-space": "srgb"
|
||||
},
|
||||
"idiom": "universal",
|
||||
"appearances": [
|
||||
{
|
||||
"appearance": "luminosity",
|
||||
"value": "light"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"images": [
|
||||
{
|
||||
"idiom": "universal",
|
||||
"appearances": [
|
||||
{
|
||||
"appearance": "luminosity",
|
||||
"value": "light"
|
||||
}
|
||||
],
|
||||
"filename": "image.png",
|
||||
"scale": "1x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"appearances": [
|
||||
{
|
||||
"appearance": "luminosity",
|
||||
"value": "light"
|
||||
}
|
||||
],
|
||||
"filename": "image@2x.png",
|
||||
"scale": "2x"
|
||||
},
|
||||
{
|
||||
"idiom": "universal",
|
||||
"appearances": [
|
||||
{
|
||||
"appearance": "luminosity",
|
||||
"value": "light"
|
||||
}
|
||||
],
|
||||
"filename": "image@3x.png",
|
||||
"scale": "3x"
|
||||
}
|
||||
],
|
||||
"info": {
|
||||
"version": 1,
|
||||
"author": "expo"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>minuit.starter</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>$(PRODUCT_NAME)</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2024.04.1</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>minuit</string>
|
||||
<string>com.minuit.starter</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>com.googleusercontent.apps.943006074419-h43c7p48nhn5f4td4oimv4dq7b1p3s29</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>CFBundleURLSchemes</key>
|
||||
<array>
|
||||
<string>exp+minuitstarter</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSApplicationQueriesSchemes</key>
|
||||
<array>
|
||||
<string>itms-apps</string>
|
||||
<string>minuit</string>
|
||||
</array>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>12.0</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<false/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>Nous avons besoin d'accéder à votre appareil photo pour vous permettre de prendre des photos de vos tâches.</string>
|
||||
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
|
||||
<string>Allow $(PRODUCT_NAME) to access your location</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>Allow $(PRODUCT_NAME) to access your location</string>
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>Allow $(PRODUCT_NAME) to access your location</string>
|
||||
<key>NSMicrophoneUsageDescription</key>
|
||||
<string>Nous avons besoin d'accéder à votre microphone pour vous permettre d'enregistrer des messages vocaux pour vos tâches.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>Nous avons besoin d'accéder à votre galerie pour vous permettre d'ajouter des photos à vos tâches.</string>
|
||||
<key>RCTRootViewBackgroundColor</key>
|
||||
<integer>4279176212</integer>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>SplashScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UIRequiresFullScreen</key>
|
||||
<true/>
|
||||
<key>UIStatusBarStyle</key>
|
||||
<string>UIStatusBarStyleDefault</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIUserInterfaceStyle</key>
|
||||
<string>Dark</string>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,48 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="EXPO-VIEWCONTROLLER-1">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||
<capability name="Named colors" minToolsVersion="9.0"/>
|
||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<scene sceneID="EXPO-SCENE-1">
|
||||
<objects>
|
||||
<viewController storyboardIdentifier="SplashScreenViewController" id="EXPO-VIEWCONTROLLER-1" sceneMemberID="viewController">
|
||||
<view key="view" userInteractionEnabled="NO" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="EXPO-ContainerView" userLabel="ContainerView">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<imageView id="EXPO-SplashScreen" userLabel="SplashScreenLogo" image="SplashScreenLogo" contentMode="scaleAspectFit" clipsSubviews="true" userInteractionEnabled="false" translatesAutoresizingMaskIntoConstraints="false">
|
||||
<rect key="frame" x="0" y="0" width="414" height="736"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<viewLayoutGuide key="safeArea" id="Rmq-lb-GrQ"/>
|
||||
<constraints>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="top" secondItem="EXPO-ContainerView" secondAttribute="top" id="83fcb9b545b870ba44c24f0feeb116490c499c52"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="leading" secondItem="EXPO-ContainerView" secondAttribute="leading" id="61d16215e44b98e39d0a2c74fdbfaaa22601b12c"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="trailing" secondItem="EXPO-ContainerView" secondAttribute="trailing" id="f934da460e9ab5acae3ad9987d5b676a108796c1"/>
|
||||
<constraint firstItem="EXPO-SplashScreen" firstAttribute="bottom" secondItem="EXPO-ContainerView" secondAttribute="bottom" id="d6a0be88096b36fb132659aa90203d39139deda9"/>
|
||||
</constraints>
|
||||
<color key="backgroundColor" name="SplashScreenBackground"/>
|
||||
<color key="backgroundColor" name="SplashScreenBackground"/>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="EXPO-PLACEHOLDER-1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="0.0" y="0.0"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="SplashScreenLogo" width="414" height="736"/>
|
||||
<namedColor name="SplashScreenBackground">
|
||||
<color alpha="1.000" blue="0.0784313725490196" green="0.0470588235294118" red="0.0588235294117647" customColorSpace="sRGB" colorSpace="custom"/>
|
||||
</namedColor>
|
||||
<namedColor name="SplashScreenBackground">
|
||||
<color alpha="1.000" blue="0.0784313725490196" green="0.0470588235294118" red="0.0588235294117647" customColorSpace="sRGB" colorSpace="custom"/>
|
||||
</namedColor>
|
||||
</resources>
|
||||
</document>
|
||||