Feature/notification system #26

Open
mattdimegs wants to merge 34 commits from feature/notification-system into main
113 changed files with 7919 additions and 10066 deletions

View file

@ -0,0 +1,32 @@
<?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>API_KEY</key>
<string>AIzaSyClZkx4PFWhfIG3JDb6GD3g6Bl7-pWLNrU</string>
<key>GCM_SENDER_ID</key>
<string>917296693147</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.mattdimegs.tones</string>
<key>PROJECT_ID</key>
<string>tones-9f1d4</string>
<key>STORAGE_BUCKET</key>
<string>tones-9f1d4.firebasestorage.app</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:917296693147:ios:73d6d426aa60e52b35ab3e</string>
<key>DATABASE_URL</key>
<string>https://tones-9f1d4-default-rtdb.firebaseio.com</string>
</dict>
</plist>

View file

@ -0,0 +1,30 @@
{
"project_info": {
"project_number": "917296693147",
"firebase_url": "https://tones-9f1d4-default-rtdb.firebaseio.com",
"project_id": "tones-9f1d4",
"storage_bucket": "tones-9f1d4.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:917296693147:android:9387809020ba083035ab3e",
"android_client_info": {
"package_name": "com.mattdimegs.tones"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAT0khB8fuAvWjz0WeWrdPAw1RD-v9ylNU"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View file

@ -14,18 +14,19 @@ react {
hermesCommand = new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim()).getParentFile().getAbsolutePath() + "/sdks/hermesc/%OS-BIN%/hermesc" 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() 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()
enableBundleCompression = (findProperty('android.enableBundleCompression') ?: false).toBoolean()
// Use Expo CLI to bundle the app, this ensures the Metro config // Use Expo CLI to bundle the app, this ensures the Metro config
// works correctly with Expo projects. // 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()) cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
bundleCommand = "export:embed" bundleCommand = "export:embed"
/* Folders */ /* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..' // The root of your project, i.e. where "package.json" lives. Default is '../..'
// root = file("../") // root = file("../../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native // The folder where the react-native NPM package is. Default is ../../node_modules/react-native
// reactNativeDir = file("../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 // The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
// codegenDir = file("../node_modules/@react-native/codegen") // codegenDir = file("../../node_modules/@react-native/codegen")
/* Variants */ /* Variants */
// The list of variants to that are debuggable. For those we're going to // The list of variants to that are debuggable. For those we're going to
@ -57,6 +58,9 @@ react {
// //
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
// hermesFlags = ["-O", "-output-source-map"] // hermesFlags = ["-O", "-output-source-map"]
/* Autolinking */
autolinkLibrariesWithApp()
} }
/** /**
@ -75,7 +79,7 @@ def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInRelea
* give correct results when using with locales other than en-US. Note that * give correct results when using with locales other than en-US. Note that
* this variant is about 6MiB larger per architecture than default. * this variant is about 6MiB larger per architecture than default.
*/ */
def jscFlavor = 'org.webkit:android-jsc:+' def jscFlavor = 'io.github.react-native-community:jsc-android:2026004.+'
android { android {
ndkVersion rootProject.ext.ndkVersion ndkVersion rootProject.ext.ndkVersion
@ -83,13 +87,13 @@ android {
buildToolsVersion rootProject.ext.buildToolsVersion buildToolsVersion rootProject.ext.buildToolsVersion
compileSdk rootProject.ext.compileSdkVersion compileSdk rootProject.ext.compileSdkVersion
namespace 'com.anonymous.testapplication' namespace 'com.mattdimegs.tones'
defaultConfig { defaultConfig {
applicationId 'com.anonymous.testapplication' applicationId 'com.mattdimegs.tones'
minSdkVersion rootProject.ext.minSdkVersion minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1 versionCode 1
versionName "1.0.0" versionName "1.0.1"
} }
signingConfigs { signingConfigs {
debug { debug {
@ -110,6 +114,7 @@ android {
shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false) shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
minifyEnabled enableProguardInReleaseBuilds minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true)
} }
} }
packagingOptions { packagingOptions {
@ -117,6 +122,9 @@ android {
useLegacyPackaging (findProperty('expo.useLegacyPackaging')?.toBoolean() ?: false) 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` // Apply static values from `gradle.properties` to the `android.packagingOptions`
@ -149,15 +157,15 @@ dependencies {
if (isGifEnabled) { if (isGifEnabled) {
// For animated gif support // For animated gif support
implementation("com.facebook.fresco:animated-gif:${reactAndroidLibs.versions.fresco.get()}") implementation("com.facebook.fresco:animated-gif:${expoLibs.versions.fresco.get()}")
} }
if (isWebpEnabled) { if (isWebpEnabled) {
// For webp support // For webp support
implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}") implementation("com.facebook.fresco:webpsupport:${expoLibs.versions.fresco.get()}")
if (isWebpAnimatedEnabled) { if (isWebpAnimatedEnabled) {
// Animated webp support // Animated webp support
implementation("com.facebook.fresco:animated-webp:${reactAndroidLibs.versions.fresco.get()}") implementation("com.facebook.fresco:animated-webp:${expoLibs.versions.fresco.get()}")
} }
} }
@ -168,5 +176,4 @@ dependencies {
} }
} }
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle"); apply plugin: 'com.google.gms.google-services'
applyNativeModulesAppBuildGradle(project)

View file

@ -0,0 +1,30 @@
{
"project_info": {
"project_number": "917296693147",
"firebase_url": "https://tones-9f1d4-default-rtdb.firebaseio.com",
"project_id": "tones-9f1d4",
"storage_bucket": "tones-9f1d4.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:917296693147:android:9387809020ba083035ab3e",
"android_client_info": {
"package_name": "com.mattdimegs.tones"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyAT0khB8fuAvWjz0WeWrdPAw1RD-v9ylNU"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View file

@ -3,5 +3,7 @@
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:usesCleartextTraffic" /> <application android:usesCleartextTraffic="true" tools:targetApi="28" tools:ignore="GoogleAppIndexingWarning" tools:replace="android:usesCleartextTraffic">
<meta-data android:name="delivery_metrics_exported_to_big_query_enabled" android:value="false"/>
</application>
</manifest> </manifest>

View file

@ -11,7 +11,7 @@
<data android:scheme="https"/> <data android:scheme="https"/>
</intent> </intent>
</queries> </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"> <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.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_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/> <meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
@ -25,9 +25,7 @@
<category android:name="android.intent.category.DEFAULT"/> <category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/> <category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="myapp"/> <data android:scheme="myapp"/>
<data android:scheme="com.anonymous.testapplication"/>
</intent-filter> </intent-filter>
</activity> </activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
</application> </application>
</manifest> </manifest>

View file

@ -1,4 +1,5 @@
package com.anonymous.testapplication package com.mattdimegs.tones
import expo.modules.splashscreen.SplashScreenManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@ -15,7 +16,10 @@ class MainActivity : ReactActivity() {
// Set the theme to AppTheme BEFORE onCreate to support // Set the theme to AppTheme BEFORE onCreate to support
// coloring the background, status bar, and navigation bar. // coloring the background, status bar, and navigation bar.
// This is required for expo-splash-screen. // This is required for expo-splash-screen.
setTheme(R.style.AppTheme); // 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) super.onCreate(null)
} }

View file

@ -1,4 +1,4 @@
package com.anonymous.testapplication package com.mattdimegs.tones
import android.app.Application import android.app.Application
import android.content.res.Configuration import android.content.res.Configuration
@ -10,6 +10,7 @@ import com.facebook.react.ReactPackage
import com.facebook.react.ReactHost import com.facebook.react.ReactHost
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactNativeHost import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.react.soloader.OpenSourceMergedSoMapping
import com.facebook.soloader.SoLoader import com.facebook.soloader.SoLoader
import expo.modules.ApplicationLifecycleDispatcher import expo.modules.ApplicationLifecycleDispatcher
@ -21,9 +22,10 @@ class MainApplication : Application(), ReactApplication {
this, this,
object : DefaultReactNativeHost(this) { object : DefaultReactNativeHost(this) {
override fun getPackages(): List<ReactPackage> { override fun getPackages(): List<ReactPackage> {
val packages = PackageList(this).packages
// Packages that cannot be autolinked yet can be added manually here, for example: // Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage()); // packages.add(MyReactNativePackage())
return PackageList(this).packages return packages
} }
override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry" override fun getJSMainModuleName(): String = ".expo/.virtual-metro-entry"
@ -40,7 +42,7 @@ class MainApplication : Application(), ReactApplication {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
SoLoader.init(this, false) SoLoader.init(this, OpenSourceMergedSoMapping)
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
// If you opted-in for the New Architecture, we load the native entry point for this app. // If you opted-in for the New Architecture, we load the native entry point for this app.
load() load()

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -1,3 +1,6 @@
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/splashscreen_background"/> <item android:drawable="@color/splashscreen_background"/>
<item>
<bitmap android:gravity="center" android:src="@drawable/splashscreen_logo"/>
</item>
</layer-list> </layer-list>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

Before

Width:  |  Height:  |  Size: 5 KiB

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

View file

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

View file

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,5 +1,5 @@
<resources> <resources>
<string name="app_name">test-application</string> <string name="app_name">Tones</string>
<string name="expo_splash_screen_resize_mode" translatable="false">contain</string> <string name="expo_splash_screen_resize_mode" translatable="false">contain</string>
<string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string> <string name="expo_splash_screen_status_bar_translucent" translatable="false">false</string>
<string name="expo_system_ui_user_interface_style" translatable="false">automatic</string> <string name="expo_system_ui_user_interface_style" translatable="false">automatic</string>

View file

@ -1,17 +1,13 @@
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <style name="AppTheme" parent="Theme.AppCompat.DayNight.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="android:editTextBackground">@drawable/rn_edit_text_material</item>
<item name="android:windowOptOutEdgeToEdgeEnforcement" tools:targetApi="35">true</item>
<item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimary">@color/colorPrimary</item>
<item name="android:statusBarColor">#ffffff</item> <item name="android:statusBarColor">#ffffff</item>
</style> </style>
<style name="ResetEditText" parent="@android:style/Widget.EditText"> <style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
<item name="android:padding">0dp</item> <item name="windowSplashScreenBackground">@color/splashscreen_background</item>
<item name="android:textColorHint">#c8c8c8</item> <item name="windowSplashScreenAnimatedIcon">@drawable/splashscreen_logo</item>
<item name="android:textColor">@android:color/black</item> <item name="postSplashScreenTheme">@style/AppTheme</item>
</style>
<style name="Theme.App.SplashScreen" parent="AppTheme">
<item name="android:windowBackground">@drawable/splashscreen</item>
</style> </style>
</resources> </resources>

View file

@ -1,41 +1,38 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext { repositories {
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '34.0.0' google()
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '23') mavenCentral()
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '34') }
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34') dependencies {
kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.23' classpath 'com.google.gms:google-services:4.4.1'
classpath('com.android.tools.build:gradle')
ndkVersion = "26.1.10909125" classpath('com.facebook.react:react-native-gradle-plugin')
} classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
repositories { }
google()
mavenCentral()
}
dependencies {
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" def reactNativeAndroidDir = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('react-native/package.json')")
}.standardOutput.asText.get().trim(),
"../android"
)
allprojects { allprojects {
repositories { repositories {
maven { maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm // 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')) url(reactNativeAndroidDir)
}
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' }
} }
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
}
} }
apply plugin: "expo-root-project"
apply plugin: "com.facebook.react.rootproject"

View file

@ -22,8 +22,8 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
# https://developer.android.com/topic/libraries/support-library/androidx-rn # https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX # Enable AAPT2 PNG crunching
android.enableJetifier=true android.enablePngCrunchInReleaseBuilds=true
# Use this property to specify which architecture you want to build. # Use this property to specify which architecture you want to build.
# You can also override it from the CLI using # You can also override it from the CLI using
@ -35,7 +35,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want # your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that # to write custom TurboModules/Fabric components OR use libraries that
# are providing them. # are providing them.
newArchEnabled=false newArchEnabled=true
# Use this property to enable or disable the Hermes JS engine. # Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead. # If set to false, you will be using JSC instead.
@ -54,3 +54,6 @@ EX_DEV_CLIENT_NETWORK_INSPECTOR=true
# Use legacy packaging to compress native libraries in the resulting APK. # Use legacy packaging to compress native libraries in the resulting APK.
expo.useLegacyPackaging=false expo.useLegacyPackaging=false
# Whether the app is configured to use edge-to-edge via the app config or `react-native-edge-to-edge` plugin
expo.edgeToEdgeEnabled=false

Binary file not shown.

View file

@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

6
android/gradlew vendored Executable file → Normal file
View file

@ -15,6 +15,8 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
# #
# SPDX-License-Identifier: Apache-2.0
#
############################################################################## ##############################################################################
# #
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop. # Darwin, MinGW, and NonStop.
# #
# (3) This script is generated from the Groovy template # (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # 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. # within the Gradle project.
# #
# You can find Gradle at https://github.com/gradle/gradle/. # You can find Gradle at https://github.com/gradle/gradle/.
@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum

2
android/gradlew.bat vendored
View file

@ -13,6 +13,8 @@
@rem See the License for the specific language governing permissions and @rem See the License for the specific language governing permissions and
@rem limitations under the License. @rem limitations under the License.
@rem @rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off @if "%DEBUG%"=="" @echo off
@rem ########################################################################## @rem ##########################################################################

View file

@ -1,18 +1,39 @@
rootProject.name = 'test-application' pluginManagement {
def reactNativeGradlePlugin = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('@react-native/gradle-plugin/package.json', { paths: [require.resolve('react-native/package.json')] })")
}.standardOutput.asText.get().trim()
).getParentFile().absolutePath
includeBuild(reactNativeGradlePlugin)
dependencyResolutionManagement { def expoPluginsPath = new File(
versionCatalogs { providers.exec {
reactAndroidLibs { workingDir(rootDir)
from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml"))) commandLine("node", "--print", "require.resolve('expo-modules-autolinking/package.json', { paths: [require.resolve('expo/package.json')] })")
} }.standardOutput.asText.get().trim(),
} "../android/expo-gradle-plugin"
).absolutePath
includeBuild(expoPluginsPath)
} }
apply from: new File(["node", "--print", "require.resolve('expo/package.json')"].execute(null, rootDir).text.trim(), "../scripts/autolinking.gradle"); plugins {
useExpoModules() id("com.facebook.react.settings")
id("expo-autolinking-settings")
}
apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle"); extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
applyNativeModulesSettingsGradle(settings) if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') {
ex.autolinkLibrariesFromCommand()
} else {
ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
}
}
expoAutolinking.useExpoModules()
rootProject.name = 'Tones'
expoAutolinking.useExpoVersionCatalog()
include ':app' 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()) includeBuild(expoAutolinking.reactNativeGradlePlugin)

View file

@ -1,7 +1,7 @@
{ {
"expo": { "expo": {
"name": "Tones", "name": "Tones",
"slug": "Tones", "slug": "tones",
"version": "1.0.1", "version": "1.0.1",
"orientation": "portrait", "orientation": "portrait",
"icon": "./assets/images/icon.png", "icon": "./assets/images/icon.png",
@ -13,22 +13,40 @@
"backgroundColor": "#ffffff" "backgroundColor": "#ffffff"
}, },
"ios": { "ios": {
"entitlements": {
"aps-environment": "production"
},
"infoPlist": {
"UIBackgroundModes": ["remote-notification"]
},
"supportsTablet": true, "supportsTablet": true,
"bundleIdentifier": "com.anonymous.testapplication" "bundleIdentifier": "com.mattdimegs.tones",
"googleServicesFile": "./GoogleServices/GoogleService-Info.plist"
}, },
"android": { "android": {
"adaptiveIcon": { "adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png", "foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff" "backgroundColor": "#ffffff"
}, },
"package": "com.anonymous.testapplication" "package": "com.mattdimegs.tones",
"googleServicesFile": "./GoogleServices/google-services.json"
}, },
"plugins": [ "plugins": [
"expo-router", "expo-router",
"expo-font" "expo-font",
"expo-web-browser"
], ],
"experiments": { "experiments": {
"typedRoutes": true "typedRoutes": true
} },
"extra": {
"router": {
"origin": false
},
"eas": {
"projectId": "6903a306-dafd-42ab-b010-f0c8205269ad"
}
},
"owner": "tones-bd"
} }
} }

72
app/_layout.jsx Normal file
View file

@ -0,0 +1,72 @@
import React, { useState, useEffect } from 'react';
import { router, Stack } from 'expo-router';
import { AuthProvider, GlobalVariablesProvider, WebSocketProvider } from '@/contexts';
import { useNotifications, useWebSocketContext } from '@/hooks';
import { useAuth } from '@/contexts/AuthContext';
function AppTest() {
const [ oldMessage, setOldMessage ] = useState(1);
const { user } = useAuth();
const { lastMessage } = useWebSocketContext();
const { schedulePushNotification } = useNotifications();
const [isReady, setIsReady] = useState(false);
useEffect(() => {
setIsReady(true);
}, []);
useEffect(() => {
if (isReady) {
if (user) {
// Ok to Leave Console Log... Displays the name so you know what you are signed in as.
console.log('user: ', user?.displayName || 'Not Found');
router.replace('./incidents');
} else {
router.replace('/login');
}
}
}, [isReady, user]);
const parseAddress = (data) => {
const { Address } = data;
const { StreetAddress, AddressApartment, Town, State } = Address;
const response = `${StreetAddress}${AddressApartment ? ` - ${AddressApartment}` : ''} ${Town}, ${State}`
return response;
}
useEffect(() => {
if (lastMessage) {
const parsedMessage = JSON?.parse(lastMessage);
if (parsedMessage?.data && new Date(oldMessage)?.getTime() < new Date(parsedMessage?.timestamp)?.getTime()) {
setOldMessage(new Date(parsedMessage?.timestamp)?.getTime());
schedulePushNotification(
`${parsedMessage?.data?.Response?.ServiceName} - ${parsedMessage?.data?.Incident?.IncNatureCode}`,
`${parsedMessage?.data?.Incident?.IncNature}\n${parseAddress(parsedMessage?.data)}`,
parsedMessage?.data
);
}
}
}, [lastMessage]);
return (
<Stack
screenOptions={{
gestureEnabled: false,
headerBackVisible: false,
headerShown: false
}}
/>
)
}
export default function App() {
return (
<GlobalVariablesProvider>
<WebSocketProvider>
<AuthProvider>
<AppTest />
</AuthProvider>
</WebSocketProvider>
</GlobalVariablesProvider>
);
}

View file

@ -1,26 +0,0 @@
import React, { useEffect } from 'react';
import { router, Stack } from 'expo-router';
import { WebSocketProvider } from '../contexts/WebSocketContext';
export const unstable_settings = {
initialRouteName: 'login',
};
export default function App() {
useEffect(() => {
router.replace('/login');
}, []);
return (
<WebSocketProvider>
<Stack
screenOptions={{
headerShown: false
}}
>
<Stack.Screen name="login" />
</Stack>
</WebSocketProvider>
);
}

File diff suppressed because it is too large Load diff

View file

@ -42,7 +42,7 @@ export default function TabTwoScreen() {
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for <ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
different screen densities different screen densities
</ThemedText> </ThemedText>
<Image source={require('@/assets/images/tones-logo.png')} style={{ alignSelf: 'center' }} /> <Image source={require('../assets/images/tones-logo.png')} style={{ alignSelf: 'center' }} />
<ExternalLink href="https://reactnative.dev/docs/images"> <ExternalLink href="https://reactnative.dev/docs/images">
<ThemedText type="link">Learn more</ThemedText> <ThemedText type="link">Learn more</ThemedText>
</ExternalLink> </ExternalLink>

View file

@ -1,366 +1,421 @@
import React, { useState, useRef, useEffect } from 'react'; import React, { useState, useRef, useEffect } from "react";
import styled from 'styled-components'; import styled from "styled-components";
import { useCallFeed } from '../hooks/useCallFeed'; import { useCallFeed } from "@/hooks";
import { router } from 'expo-router'; import { router } from "expo-router";
import { Platform, Linking, View, ScrollView, Text, TouchableOpacity } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { SafeAreaView } from 'react-native-safe-area-context';
import { import {
PageHeader, Platform,
PageFooter, Linking,
} from '../components/generalHelpers.jsx'; View,
import { Ionicons } from '@expo/vector-icons'; ScrollView,
Text,
TouchableOpacity,
} from "react-native";
import { StatusBar } from "expo-status-bar";
import { SafeAreaView } from "react-native-safe-area-context";
import { PageHeader, PageFooter } from "@/components/generalHelpers.jsx";
import { Ionicons } from "@expo/vector-icons";
import { AccidentAndEmergency } from "healthicons-react-native/dist/outline"; import { AccidentAndEmergency } from "healthicons-react-native/dist/outline";
import ActionSheet from 'react-native-actions-sheet'; import ActionSheet from "react-native-actions-sheet";
const DepartmentActionSheet = styled(ActionSheet)``; const DepartmentActionSheet = styled(ActionSheet)``;
export default function Incidents() { function toBase64Unicode(str) {
const actionSheetRef = useRef(null); return btoa(
const callFeed = useCallFeed(); new TextEncoder()
.encode(str)
const { .reduce((data, byte) => data + String.fromCharCode(byte), "")
departments, );
callIconMap, }
callDetails,
callColorSelector, export default function Incidents() {
formatCallTimePast, const actionSheetRef = useRef(null);
formatCallDateTime const callFeed = useCallFeed();
} = callFeed;
const {
const sortedAndFilteredCalls = callDetails departments,
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp)) callIconMap,
.filter((item, index, self) => { callDetails,
return index === self.findIndex(i => { callColorSelector,
return `${i?.data?.Incident?.IncID}${i?.data?.Response?.ServiceName}` === `${item?.data?.Incident?.IncID}${item?.data?.Response?.ServiceName}` formatCallTimePast,
}); formatCallDateTime,
}); } = callFeed;
const { const sortedAndFilteredCalls =
departmentTypeMap, callDetails
accountDetails, .sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
deptList, .filter((item, index, self) => {
setDeptList, return (
selectedDepartment, index ===
setSelectedDepartment, self.findIndex((i) => {
updateSelectedDepartment, return (
selectedDepartmentColorPicker, `${i?.incident?.incID}${i?.response?.serviceName}` ===
} = departments; `${item?.incident?.incID}${item?.response?.serviceName}`
);
return ( })
<React.Fragment> );
<PageHeader> })
<View ?.map((item) => {
style={{ return { ...item, timestamp: item?.incident?.incDate };
flexDirection: 'column', }) || [];
height: 80,
alignItems: 'center', const {
justifyContent: 'flex-end', departmentTypeMap,
paddingBottom: 7 accountDetails,
}} deptList,
> setDeptList,
<TouchableOpacity selectedDepartment,
style={{ setSelectedDepartment,
borderRadius: 6, updateSelectedDepartment,
elevation: 3, selectedDepartmentColorPicker,
backgroundColor: '#fff', } = departments;
shadowOffset: { width: 0, height: 0 },
shadowColor: '#333', return (
shadowOpacity: .8, <React.Fragment>
shadowRadius: 2, <PageHeader
paddingHorizontal: 10, centerHeader={
paddingVertical: 2, <View style={{ flex: 1, alignItems: "center" }}>
}} <TouchableOpacity
onPress={() => { style={{
actionSheetRef.current?.show(); borderRadius: 6,
}} elevation: 3,
> backgroundColor: "#fff",
<Text shadowOffset: { width: 0, height: 0 },
style={{ shadowColor: "#333",
color: selectedDepartmentColorPicker(selectedDepartment?.type), shadowOpacity: 0.8,
fontWeight: 600, shadowRadius: 2,
fontSize: 14 paddingHorizontal: 10,
}} paddingVertical: 2,
> }}
{selectedDepartment?.deptAbv} onPress={() => {
</Text> actionSheetRef.current?.show();
</TouchableOpacity> }}
</View> >
</PageHeader> <Text
<ScrollView> style={{
<StatusBar style="dark" /> color: selectedDepartmentColorPicker(
<SafeAreaView /> selectedDepartment?.type
<View style={{ flexDirection: 'column', padding: 20 }}> ),
{sortedAndFilteredCalls?.length ? ( fontWeight: 600,
sortedAndFilteredCalls?.map((callItem, index) => { fontSize: 14,
const { data: call, timestamp } = callItem; }}
const { Incident, Address, Response } = call; >
const { {selectedDepartment?.deptAbv}
ServiceNumber, </Text>
IncDate, </TouchableOpacity>
IncNature, </View>
IncNatureCode, }
IncNatureCodeDesc, />
Status, <ScrollView>
} = Incident; <StatusBar style="dark" />
const { <SafeAreaView />
StreetAddress, <View style={{ flexDirection: "column", padding: 20 }}>
AddressApartment, {sortedAndFilteredCalls?.length ? (
Town, sortedAndFilteredCalls?.map((callItem, index) => {
State, const { incident, address, response, timestamp } = callItem;
LocationName, const {
} = Address; serviceNumber,
const { incDate,
ServiceName incNature,
} = Response; incNatureCode,
const SelectedIcon = callIconMap[IncNature] || AccidentAndEmergency; incNatureCodeDesc,
return ( status,
<TouchableOpacity } = incident;
key={`callDetails - ${timestamp}`} const {
style={{ padding: 2 }} streetAddress,
onPress={() => { addressApartment,
router.push({ town,
pathname: '/call', state,
params: { locationName,
callDetails: btoa(JSON.stringify(call)) } = address;
} const { serviceName } = response;
}) const SelectedIcon =
}} callIconMap[incNature] || AccidentAndEmergency;
> return (
<View <TouchableOpacity
style={{ key={`callDetails - ${timestamp}`}
borderRadius: 12, style={{ paddingBottom: 15 }}
elevation: 3, onPress={() => {
backgroundColor: '#fff', router.push({
shadowOffset: { width: 0, height: 0 }, pathname: "/call",
shadowColor: callColorSelector( params: {
IncNatureCode, callDetails: toBase64Unicode(JSON.stringify(callItem)),
IncNature, },
Status });
), }}
shadowOpacity: 1, >
shadowRadius: 5, <View
padding: 10, style={{
}} borderRadius: 12,
> elevation: 3,
<View style={{ flexDirection: 'column' }}> backgroundColor: "#fff",
<View key="callDateAndTime" shadowOffset: { width: 0, height: 0 },
style={{ shadowColor: callColorSelector(
paddingBottom: 6, incNatureCodeDesc,
flexDirection: 'row', incNature,
justifyContent: 'space-between' status
}} ),
> shadowOpacity: 1,
<Text style={{ fontSize: 12 }}>{formatCallDateTime(IncDate)}</Text> shadowRadius: 5,
<View style={{ flexDirection: 'row', alignItems: 'center' }}> padding: 10,
<Text style={{ fontSize: 12 }}>{formatCallTimePast(IncDate)}</Text> }}
{Status === 'CLOSED' ? ( >
<Ionicons <View style={{ flexDirection: "column" }}>
name="lock-closed-outline" <View
color='red' key="callDateAndTime"
style={{ style={{
shadowColor: 'black', paddingBottom: 6,
shadowOffset: 0, flexDirection: "row",
shadowOpacity: 1, justifyContent: "space-between",
shadowRadius: 10 }}
}} >
/> <Text style={{ fontSize: 12 }}>
) : null} {formatCallDateTime(incDate)}
</View> </Text>
</View> <View
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}> style={{ flexDirection: "row", alignItems: "center" }}
<SelectedIcon >
color={callColorSelector( <Text style={{ fontSize: 12 }}>
IncNatureCode, {formatCallTimePast(incDate)}
IncNature, </Text>
Status {status === "CLOSED" ? (
)} <Ionicons
opacity={0.3} name="lock-closed-outline"
width={56} color="red"
height={56} style={{
/> shadowColor: "black",
<View style={{ flexDirection: 'column' }}> shadowOffset: 0,
{LocationName ? ( shadowOpacity: 1,
<Text shadowRadius: 10,
style={{ }}
color: 'black', />
fontWeight: 600, ) : null}
fontSize: 16 </View>
}} </View>
> <View
{`${LocationName}`} style={{ flexDirection: "row", alignItems: "center" }}
</Text> >
) : ( <SelectedIcon
<View style={{ flexDirection: 'row' }}> color={callColorSelector(
<Text incNatureCodeDesc,
style={[{ incNature,
color: 'black', status
fontSize: 12, )}
fontWeight: 600 opacity={0.3}
}]} width={56}
> height={56}
{`${StreetAddress}`} />
</Text> <View
{AddressApartment ? ( style={{
<Text flex: 1,
style={[{ flexDirection: "column",
color: 'black', alignItems: "flex-start",
fontSize: 12, paddingHorizontal: 2,
fontWeight: 600 maxWidth: "80%",
}]} }}
> >
{` - ${AddressApartment}`} {locationName ? (
</Text> <Text
) : null } style={{
<Text color: "black",
style={[{ fontWeight: 600,
color: 'black', fontSize: 16,
fontSize: 12, }}
fontWeight: 600 >
}]} {`${locationName}`}
> </Text>
{` ${Town}, ${State}`} ) : (
</Text> <View style={{ flexDirection: "row" }}>
</View> <Text
)} style={[
<Text {
style={{ color: "black",
color: 'black', fontSize: 12,
fontWeight: 600, fontWeight: 600,
fontSize: 16 },
}} ]}
> >
{`${IncNature}`} {`${streetAddress?.split(",")[0]}`}
</Text> </Text>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}> {addressApartment ? (
<Text <Text
style={{ style={[
color: 'black', {
fontSize: 12, color: "black",
textShadowColor: callColorSelector( fontSize: 12,
IncNatureCode, fontWeight: 600,
IncNature, },
Status ]}
), >
textShadowRadius: 1 {` - ${addressApartment}`}
}} </Text>
> ) : null}
{`${IncNatureCodeDesc}`} <Text
</Text> style={[
</View> {
</View> color: "black",
<View style={{ padding: 0 }}> fontSize: 12,
<View> fontWeight: 600,
<Ionicons name="chevron-forward-outline" /> },
</View> ]}
</View> >
</View> {` ${town}, ${state}`}
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}> </Text>
<Text </View>
style={{ )}
fontSize: 12, <Text
}} style={{
> color: "black",
Service: {ServiceName} fontWeight: 600,
</Text> fontSize: 16,
<Text }}
style={{ >
fontSize: 12, {`${incNature}`}
textAlign: 'right' </Text>
}} <View
> style={{
{`Incident #: ${ServiceNumber}`} flexDirection: "row",
</Text> justifyContent: "space-between",
</View> }}
</View> >
</View> <Text
</TouchableOpacity> style={{
) color: "black",
})) : ( fontSize: 12,
<View style={{ textShadowColor: callColorSelector(
marginTop: '50%', incNatureCode,
flexDirection: 'column', incNature,
alignItems: 'center', status
justifyContent: 'flex-end', ),
}}> textShadowRadius: 1,
<Ionicons name="warning-outline" size={100} color='orange' /> }}
<Text style={{ fontSize: 25 }}>There are no Incidents</Text> >
</View> {`${incNatureCodeDesc}`}
) } </Text>
</View> </View>
</ScrollView> </View>
<PageFooter> <View style={{ padding: 0 }}>
<View <View>
style={{ <Ionicons name="chevron-forward-outline" />
flexDirection: 'column', </View>
height: 100, </View>
alignItems: 'center', </View>
justifyContent: 'flex-start', <View
paddingTop: 7 style={{
}} paddingTop: 5,
/> flexDirection: "row",
</PageFooter> justifyContent: "space-between",
<DepartmentActionSheet }}
ref={actionSheetRef} >
gestureEnabled <Text
containerStyle={{ style={{
height: "50%", fontSize: 12,
width: "100%", }}
backgroundColor: '#ECEDEE', >
}} Service: {serviceName}
> </Text>
<View style={{ flexDirection: 'column', padding: 20 }}> <Text
{deptList?.map((item) => { style={{
return ( fontSize: 12,
<View style={{ padding: 2 }} key={item?.deptId}> textAlign: "right",
<TouchableOpacity }}
style={{ >
borderRadius: 6, {`Incident #: ${serviceNumber}`}
elevation: 3, </Text>
backgroundColor: item?.selected ? 'grey' : '#fff', </View>
shadowOffset: { width: 1, height: 1 }, </View>
shadowColor: '#333', </View>
shadowOpacity: 0.3, </TouchableOpacity>
shadowRadius: 2, );
marginHorizontal: 20, })
marginVertical: 6, ) : (
padding: 10 <Text>There are no Calls</Text>
}} )}
onPress={() => { </View>
actionSheetRef.current?.hide(); </ScrollView>
return updateSelectedDepartment( <PageFooter>
selectedDepartment?.deptId, <View
item?.deptId style={{
) flexDirection: "column",
}} height: 100,
> alignItems: "center",
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}> justifyContent: "flex-start",
<Text paddingTop: 7,
style={{ }}
color: selectedDepartmentColorPicker( />
item?.type </PageFooter>
), <DepartmentActionSheet
fontWeight: 600, ref={actionSheetRef}
fontSize: 16 gestureEnabled
}} containerStyle={{
> height: "50%",
{item?.dept} width: "100%",
</Text> backgroundColor: "#ECEDEE",
{item?.primary ? <Ionicons name="star" size={16} color="yellow" style={{ }}
paddingLeft: 20, >
shadowColor: '#333', <View style={{ flexDirection: "column", padding: 20 }}>
shadowOffset: 1, {deptList?.map((item) => {
shadowOpacity: 1, return (
shadowRadius: 6 <View style={{ padding: 2 }} key={item?.deptId}>
}} /> : null} <TouchableOpacity
</View> style={{
<Text>{`${item?.deptAbv} - ${departmentTypeMap[item?.type]}`}</Text> borderRadius: 6,
</TouchableOpacity> elevation: 3,
</View> backgroundColor: item?.selected ? "grey" : "#fff",
); shadowOffset: { width: 1, height: 1 },
})} shadowColor: "#333",
</View> shadowOpacity: 0.3,
</DepartmentActionSheet> shadowRadius: 2,
</React.Fragment> marginHorizontal: 20,
) marginVertical: 6,
padding: 10,
}}
onPress={() => {
actionSheetRef.current?.hide();
return updateSelectedDepartment(
selectedDepartment?.deptId,
item?.deptId
);
}}
>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text
style={{
color: selectedDepartmentColorPicker(item?.type),
fontWeight: 600,
fontSize: 16,
}}
>
{item?.dept}
</Text>
{item?.primary ? (
<Ionicons
name="star"
size={16}
color="yellow"
style={{
paddingLeft: 20,
shadowColor: "#333",
shadowOffset: 1,
shadowOpacity: 1,
shadowRadius: 6,
}}
/>
) : null}
</View>
<Text>{`${item?.deptAbv} - ${
departmentTypeMap[item?.type]
}`}</Text>
</TouchableOpacity>
</View>
);
})}
</View>
</DepartmentActionSheet>
</React.Fragment>
);
} }

View file

@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native'; import { View, Text, ScrollView, KeyboardAvoidingView, Platform } from 'react-native';
import { StatusBar } from 'expo-status-bar'; import { StatusBar } from 'expo-status-bar';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
@ -20,111 +20,123 @@ import {
ExtraText, ExtraText,
TextLinkContent, TextLinkContent,
LoginTextInput LoginTextInput
} from '../components/generalHelpers.jsx'; } from '@/components/generalHelpers.jsx';
import { signInWithEmailAndPassword } from 'firebase/auth';
import { auth } from '@/contexts/firebase';
import { useNotifications } from '@/hooks';
export default function Login() { export default function Login() {
const [hidePassword, setHidePassword] = useState(true); const [hidePassword, setHidePassword] = useState(true);
const [loginButtonDisabled, setLoginButtonDisabled] = useState(true); const [loginButtonDisabled, setLoginButtonDisabled] = useState(true);
const [auth, setAuth] = useState(false); const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const { expoPushToken } = useNotifications();
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
number: '', email: '',
password: '' password: ''
}, },
onSubmit: (values) => { onSubmit: async (values) => {
values.number = values.number.replace(/[()\-\s]/g, ''); setError('');
console.log(values); setLoading(true);
setAuth(true); try {
}, await signInWithEmailAndPassword(auth, values.email, values.password);
});
const formValues = formik.values;
useEffect(() => {
if (formValues) {
if (formValues.number.length === 14 && formValues.password) {
setLoginButtonDisabled(false);
} else {
setLoginButtonDisabled(true);
}
}
}, [formValues])
useEffect(() => {
// Temp for Testing
// if (!auth) {
// setTimeout(() => {
// router.replace('./incidents');
// }, 1000);
// }
if (auth) {
router.replace('./incidents'); router.replace('./incidents');
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
} }
}, [auth]) },
});
return ( const formValues = formik.values;
<React.Fragment>
<PageHeader> useEffect(() => {
<View style={{ flexDirection: 'row', height: 80, alignItems: 'center' }} /> if (formValues) {
</PageHeader> if (formValues.email && formValues.password) {
<StyledContainer> setLoginButtonDisabled(false);
<StatusBar style="dark" /> } else {
<SafeAreaView /> setLoginButtonDisabled(true);
<InnerContainer> }
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} /> }
<Title>Tones</Title> }, [formValues]);
<SubTitle>Account Login</SubTitle>
<StyledFormArea> return (
<LoginTextInput <View style={{ flex: 1 }}>
label="Phone Number" <StatusBar style="dark" />
icon="call-outline" <PageHeader>
placeholder="123-456-7890" <View style={{ flexDirection: 'row', height: 80, alignItems: 'center' }} />
placeholderTextColor="gray" </PageHeader>
onChangeText={formik.handleChange('number')} <KeyboardAvoidingView
onBlur={formik.handleBlur('number')} style={{ flex: 1 }}
value={formik.values.number.replace(/^(\d{3})(\d{3})(\d+)$/, "($1) $2-$3")} behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardType="number-pad" keyboardVerticalOffset={0}
maxLength={14} >
/> <ScrollView keyboardShouldPersistTaps="handled" contentContainerStyle={{ flexGrow: 1 }}>
<LoginTextInput <StyledContainer>
label="Password" <SafeAreaView />
icon="lock-closed-outline" <InnerContainer>
placeholder="* * * * * *" <PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
placeholderTextColor="gray" <Title>Tones</Title>
onChangeText={formik.handleChange('password')} <SubTitle>Account Login</SubTitle>
onBlur={formik.handleBlur('password')} <StyledFormArea>
value={formik.values.password} {error ? <MessageBox style={{ color: 'red' }}>{error}</MessageBox> : null}
secureTextEntry={hidePassword} <LoginTextInput
isPassword label="Email Address"
hidePassword={hidePassword} icon="mail-outline"
setHidePassword={setHidePassword} placeholder="test@organization.com"
/> placeholderTextColor="gray"
<MessageBox>...</MessageBox> onChangeText={formik.handleChange('email')}
<StyledButton onPress={formik.handleSubmit} disabled={loginButtonDisabled} style={loginButtonDisabled ? {backgroundColor: 'grey'} : {}}> onBlur={formik.handleBlur('email')}
<ButtonText>Login</ButtonText> value={formik.values.email}
</StyledButton> keyboardType="email-address"
autoComplete='email'
autoCapitalize='none'
/>
<LoginTextInput
label="Password"
icon="lock-closed-outline"
placeholder="* * * * * *"
placeholderTextColor="gray"
onChangeText={formik.handleChange('password')}
onBlur={formik.handleBlur('password')}
value={formik.values.password}
secureTextEntry={hidePassword}
isPassword
hidePassword={hidePassword}
setHidePassword={setHidePassword}
/>
<MessageBox>...</MessageBox>
<StyledButton onPress={formik.handleSubmit} disabled={loginButtonDisabled} style={loginButtonDisabled ? { backgroundColor: 'grey' } : {}}>
<ButtonText>Login</ButtonText>
</StyledButton>
<Line />
<ExtraView>
<ExtraText>Don't have an account already? </ExtraText>
<Link href='./register'>
<TextLinkContent>Register</TextLinkContent>
</Link>
</ExtraView>
</StyledFormArea>
<Line /> <Line />
<ExtraView> <Text>Temporary Area:</Text>
<ExtraText>Don't have an account already? </ExtraText> <View>
<Link href='./register'> <Link href='./incidents'>
<TextLinkContent>Register</TextLinkContent> <TextLinkContent>Incidents</TextLinkContent>
</Link> </Link>
</ExtraView> <Link href='./landing'>
</StyledFormArea> <TextLinkContent>Landing</TextLinkContent>
<Line /> </Link>
<Text>Temporary Area:</Text> </View>
<View> <Text>View Token: {expoPushToken}</Text>
<Link href='./incidents'> <Line />
<TextLinkContent>Incidents</TextLinkContent> </InnerContainer>
</Link> </StyledContainer>
<Link href='./landing'> </ScrollView>
<TextLinkContent>Landing</TextLinkContent> </KeyboardAvoidingView>
</Link> </View>
</View> );
<Line /> }
</InnerContainer>
</StyledContainer>
</React.Fragment>
);
}

View file

@ -1,6 +1,10 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import * as Notifications from 'expo-notifications';
import { router } from 'expo-router'; import { router } from 'expo-router';
import { View, ScrollView, Text, TouchableOpacity } from 'react-native'; import { createUserWithEmailAndPassword, updateProfile } from 'firebase/auth';
import { auth } from '@/contexts/firebase';
import { useAuth } from '@/contexts/AuthContext';
import { View, ScrollView, Text, TouchableOpacity, KeyboardAvoidingView, Platform } from 'react-native';
import { StatusBar } from 'expo-status-bar'; import { StatusBar } from 'expo-status-bar';
import { useFormik } from 'formik'; import { useFormik } from 'formik';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
@ -19,14 +23,16 @@ import {
MessageBox, MessageBox,
LoginTextInput, LoginTextInput,
RegisterDropdownInput, RegisterDropdownInput,
} from '../components/generalHelpers.jsx'; formatPhoneNumber
} from '@/components/generalHelpers.jsx';
export default function Register() { export default function Register() {
const [providerDropdownOpen, setProviderDropdownOpen] = useState(false); const [providerDropdownOpen, setProviderDropdownOpen] = useState(false);
const [hidePassword, setHidePassword] = useState(true); const [hidePassword, setHidePassword] = useState(true);
const [registerButtonDisabled, setRegisterButtonDisabled] = useState(true); const [registerButtonDisabled, setRegisterButtonDisabled] = useState(true);
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const { user } = useAuth();
const formik = useFormik({ const formik = useFormik({
initialValues: { initialValues: {
@ -38,9 +44,60 @@ export default function Register() {
password: '', password: '',
passwordConfirmation: '' passwordConfirmation: ''
}, },
onSubmit: (values) => { onSubmit: async (values) => {
values.number = values.number.replace(/[()\-\s]/g, ''); setError('');
console.log(values); setLoading(true);
try {
const userCredential = await createUserWithEmailAndPassword(auth, values.email, values.password);
await updateProfile(userCredential.user, {
displayName: `${values.firstName} ${values.lastName}`
});
// Request permissions and get push token (native FCM if possible, else Expo token)
let token = null;
let tokenType = null;
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus === 'granted') {
try {
// Try to get native FCM token (works in dev/prod builds, not Expo Go)
const deviceToken = await Notifications.getDevicePushTokenAsync({ provider: 'fcm' });
if (deviceToken?.data) {
token = deviceToken.data;
tokenType = 'fcm';
} else {
// Fallback to Expo push token
const expoToken = await Notifications.getExpoPushTokenAsync();
token = expoToken.data;
tokenType = 'expo';
}
} catch (e) {
// Fallback to Expo push token if native fails
const expoToken = await Notifications.getExpoPushTokenAsync();
token = expoToken.data;
tokenType = 'expo';
}
}
await postgresServices.createUser({
firstName: values.firstName,
lastName: values.lastName,
number: values.number,
email: values.email,
provider: values.provider,
departments: [],
token,
uid: userCredential.user.uid
})
router.replace('./incidents');
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
} }
}); });
@ -48,7 +105,8 @@ export default function Register() {
useEffect(() => { useEffect(() => {
if (formValues) { if (formValues) {
if ((formValues.number.length === 14 || (formValues.number.length === 10 && !formValues.number.includes('('))) && formValues.email && formValues.firstName && formValues.lastName) { const regex = /^[^@]+@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z0-9-]+$/;
if ((formValues.number.length === 14 || (formValues.number.length === 10 && !formValues.number.includes('('))) && regex.test(formValues.email) && formValues.firstName && formValues.lastName) {
if (formValues.password.length !== 0 && (formValues.password === formValues.passwordConfirmation)) { if (formValues.password.length !== 0 && (formValues.password === formValues.passwordConfirmation)) {
setRegisterButtonDisabled(false); setRegisterButtonDisabled(false);
} else { } else {
@ -62,106 +120,121 @@ export default function Register() {
} }
}, [formValues]) }, [formValues])
useEffect(() => {
if (user) {
router.replace('./incidents');
}
}, [user]);
return ( return (
<View> <View style={{ flex: 1 }}>
<PageHeader> <StatusBar style="dark" />
<View style={{ flexDirection: 'row', height: 80, alignItems: 'flex-end' }}> <PageHeader
leftHeader={
<TouchableOpacity onPress={router.back} style={{ flexDirection: 'row', alignItems: 'center', paddingBottom: 5 }}> <TouchableOpacity onPress={router.back} style={{ flexDirection: 'row', alignItems: 'center', paddingBottom: 5 }}>
<Ionicons name="chevron-back-outline" size={22} color="red" style={{ paddingLeft: 20 }} /> <Ionicons name="chevron-back-outline" size={22} color="red" style={{ paddingLeft: 20 }} />
<Text style={{ color: 'red', fontWeight: 600 }}>Back to Login</Text> <Text style={{ color: 'red', fontWeight: 600 }}>Back to Login</Text>
</TouchableOpacity> </TouchableOpacity>}
</View> />
</PageHeader> <KeyboardAvoidingView
<ScrollView> style={{ flex: 1 }}
<StyledContainer> behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
<StatusBar style="dark" /> keyboardVerticalOffset={0}
>
<ScrollView keyboardShouldPersistTaps="handled" contentContainerStyle={{ flexGrow: 1 }}>
<SafeAreaView /> <SafeAreaView />
<InnerContainer> <StyledContainer>
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} /> <InnerContainer>
<Title>Tones</Title> <PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
<SubTitle>Account Register</SubTitle> {error ? <MessageBox style={{ color: 'red' }}>{error}</MessageBox> : null}
<StyledFormArea> <Title>Tones</Title>
<LoginTextInput <SubTitle>Account Register</SubTitle>
label="First Name" <StyledFormArea>
icon="person-outline" <LoginTextInput
placeholder="Bud" label="First Name"
placeholderTextColor="gray" icon="person-outline"
onChangeText={formik.handleChange('firstName')} placeholder="Bud"
onBlur={formik.handleBlur('firstName')} placeholderTextColor="gray"
value={formik.values.firstName} onChangeText={formik.handleChange('firstName')}
/> onBlur={formik.handleBlur('firstName')}
<LoginTextInput value={formik.values.firstName}
label="Last Name" />
icon="people-outline" <LoginTextInput
placeholder="Doble" label="Last Name"
placeholderTextColor="gray" icon="people-outline"
onChangeText={formik.handleChange('lastName')} placeholder="Doble"
onBlur={formik.handleBlur('lastName')} placeholderTextColor="gray"
value={formik.values.lastName} onChangeText={formik.handleChange('lastName')}
/> onBlur={formik.handleBlur('lastName')}
<LoginTextInput value={formik.values.lastName}
label="Phone Number" />
icon="call-outline" <LoginTextInput
placeholder="123-456-7890" label="Phone Number"
placeholderTextColor="gray" icon="call-outline"
onChangeText={formik.handleChange('number')} placeholder="123-456-7890"
onBlur={formik.handleBlur('number')} placeholderTextColor="gray"
value={formik.values.number.replace(/^(\d{3})(\d{3})(\d+)$/, "($1) $2-$3")} onChangeText={formik.handleChange('number')}
keyboardType="number-pad" onBlur={formik.handleBlur('number')}
maxLength={14} value={formatPhoneNumber(formik.values.number)}
/> autoComplete='tel'
<RegisterDropdownInput keyboardType='phone-pad'
label="Cellular Provider" maxLength={14}
isOpen={providerDropdownOpen} />
setOpen={setProviderDropdownOpen} <RegisterDropdownInput
selectedValue={formik.values.provider} label="Cellular Provider"
onValueChange={formik.handleChange('provider')} isOpen={providerDropdownOpen}
menu={providerMenu} setOpen={setProviderDropdownOpen}
/> selectedValue={formik.values.provider}
<LoginTextInput onValueChange={formik.handleChange('provider')}
label="Email Address" menu={providerMenu}
icon="mail-outline" />
placeholder="test@organization.com" <LoginTextInput
placeholderTextColor="gray" label="Email Address"
onChangeText={formik.handleChange('email')} icon="mail-outline"
onBlur={formik.handleBlur('email')} placeholder="test@organization.com"
value={formik.values.email} placeholderTextColor="gray"
keyboardType="email-address" onChangeText={formik.handleChange('email')}
/> onBlur={formik.handleBlur('email')}
<LoginTextInput value={formik.values.email}
label="Password" keyboardType="email-address"
icon="lock-closed-outline" autoComplete='email'
placeholder="* * * * * *" autoCapitalize='none'
placeholderTextColor="gray" />
onChangeText={formik.handleChange('password')} <LoginTextInput
onBlur={formik.handleBlur('password')} label="Password"
value={formik.values.password} icon="lock-closed-outline"
secureTextEntry={hidePassword} placeholder="* * * * * *"
isPassword placeholderTextColor="gray"
hidePassword={hidePassword} onChangeText={formik.handleChange('password')}
setHidePassword={setHidePassword} onBlur={formik.handleBlur('password')}
/> value={formik.values.password}
<LoginTextInput secureTextEntry={hidePassword}
label="Confirm Password" isPassword
icon="shield-checkmark-outline" hidePassword={hidePassword}
placeholder="* * * * * *" setHidePassword={setHidePassword}
placeholderTextColor="gray" />
onChangeText={formik.handleChange('passwordConfirmation')} <LoginTextInput
onBlur={formik.handleBlur('passwordConfirmation')} label="Confirm Password"
value={formik.values.passwordConfirmation} icon="shield-checkmark-outline"
secureTextEntry={hidePassword} placeholder="* * * * * *"
isPassword placeholderTextColor="gray"
hidePassword={hidePassword} onChangeText={formik.handleChange('passwordConfirmation')}
setHidePassword={setHidePassword} onBlur={formik.handleBlur('passwordConfirmation')}
/> value={formik.values.passwordConfirmation}
<MessageBox>...</MessageBox> secureTextEntry={hidePassword}
<StyledButton onPress={formik.handleSubmit} style={registerButtonDisabled ? {backgroundColor: 'grey'} : {}}> isPassword
<ButtonText>Register</ButtonText> hidePassword={hidePassword}
</StyledButton> setHidePassword={setHidePassword}
</StyledFormArea> />
</InnerContainer> <MessageBox>...</MessageBox>
</StyledContainer> <StyledButton onPress={formik.handleSubmit} style={registerButtonDisabled ? { backgroundColor: 'grey' } : {}}>
</ScrollView> <ButtonText>Register</ButtonText>
</StyledButton>
</StyledFormArea>
</InnerContainer>
</StyledContainer>
</ScrollView>
</KeyboardAvoidingView>
</View> </View>
) )
} }

View file

@ -1,91 +0,0 @@
import { Image, StyleSheet, Platform } from 'react-native';
import { HelloWave } from '@/components/HelloWave';
import ParallaxScrollView from '@/components/ParallaxScrollView';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
export default function HomeScreen() {
return (
<ParallaxScrollView
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
headerImage={
<Image
source={require('@/assets/images/tones-logo.png')}
style={styles.reactLogo}
/>
}
>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Welcome!</ThemedText>
<HelloWave />
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
<ThemedText>
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
Press{' '}
<ThemedText type="defaultSemiBold">
{Platform.select({ ios: 'cmd + d', android: 'cmd + m' })}
</ThemedText>{' '}
to open developer tools.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
<ThemedText>
Tap the Explore tab to learn more about what's included in this starter app.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
<ThemedText>
When you're ready, run{' '}
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
<ThemedText>
When you're ready, run{' '}
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
<ThemedText>
When you're ready, run{' '}
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
</ThemedText>
</ThemedView>
</ParallaxScrollView>
);
}
const styles = StyleSheet.create({
titleContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
stepContainer: {
gap: 8,
marginBottom: 8,
},
reactLogo: {
height: 178,
width: 290,
bottom: 0,
left: 0,
position: 'absolute',
},
});

View file

@ -2,8 +2,8 @@ import Ionicons from '@expo/vector-icons/Ionicons';
import { PropsWithChildren, useState } from 'react'; import { PropsWithChildren, useState } from 'react';
import { StyleSheet, TouchableOpacity, useColorScheme } from 'react-native'; import { StyleSheet, TouchableOpacity, useColorScheme } from 'react-native';
import { ThemedText } from '@/components/ThemedText'; import { ThemedText } from '../components/ThemedText';
import { ThemedView } from '@/components/ThemedView'; import { ThemedView } from '../components/ThemedView';
import { Colors } from '@/constants/Colors'; import { Colors } from '@/constants/Colors';
export function Collapsible({ children, title }: PropsWithChildren & { title: string }) { export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {

View file

@ -7,7 +7,7 @@ import Animated, {
withSequence, withSequence,
} from 'react-native-reanimated'; } from 'react-native-reanimated';
import { ThemedText } from '@/components/ThemedText'; import { ThemedText } from '../components/ThemedText';
export function HelloWave() { export function HelloWave() {
const rotationAnimation = useSharedValue(0); const rotationAnimation = useSharedValue(0);

View file

@ -7,7 +7,7 @@ import Animated, {
useScrollViewOffset, useScrollViewOffset,
} from 'react-native-reanimated'; } from 'react-native-reanimated';
import { ThemedView } from '@/components/ThemedView'; import { ThemedView } from '../components/ThemedView';
const HEADER_HEIGHT = 250; const HEADER_HEIGHT = 250;

View file

@ -1,6 +1,6 @@
import { Text, type TextProps, StyleSheet } from 'react-native'; import { Text, type TextProps, StyleSheet } from 'react-native';
import { useThemeColor } from '@/hooks/useThemeColor'; import { useThemeColor } from '../hooks/useThemeColor';
export type ThemedTextProps = TextProps & { export type ThemedTextProps = TextProps & {
lightColor?: string; lightColor?: string;

View file

@ -1,6 +1,6 @@
import { View, type ViewProps } from 'react-native'; import { View, type ViewProps } from 'react-native';
import { useThemeColor } from '@/hooks/useThemeColor'; import { useThemeColor } from '../hooks/useThemeColor';
export type ThemedViewProps = ViewProps & { export type ThemedViewProps = ViewProps & {
lightColor?: string; lightColor?: string;

View file

@ -1,7 +1,8 @@
import React, { useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import { View, Text, LayoutAnimation, Image, TextInput, TouchableOpacity, TouchableNativeFeedback, ScrollView } from 'react-native'; import { View, Text, LayoutAnimation, Image, TextInput, TouchableOpacity, TouchableNativeFeedback, ScrollView, Platform, Modal, Pressable } from 'react-native';
import { Picker } from '@react-native-picker/picker';
import { Ionicons } from '@expo/vector-icons'; import { Ionicons } from '@expo/vector-icons';
import { Row } from './Row';
import { Container } from './Container'; import { Container } from './Container';
export const StyledContainer = styled.View` export const StyledContainer = styled.View`
@ -44,7 +45,7 @@ export const StyledTextInput = styled.TextInput`
margin-bottom: 10px; margin-bottom: 10px;
`; `;
export const StyledInputLabel = styled.Text` export const StyledInputLabel = styled.Text`
font-size: 13px; font-size: 13px;
text-align: left; text-align: left;
`; `;
@ -204,11 +205,33 @@ const providerConversion = {
} }
export const PageHeader = ({ export const PageHeader = ({
children leftHeader = <View style={{ flex: 1 }} />,
centerHeader = <View style={{ flex: 1 }} />,
rightHeader = <View style={{ flex: 1 }} />
}) => { }) => {
return ( return (
<View style={{ position: 'sticky', top: 0, backgroundColor: '#ECEDEE', zIndex: 1, marginBottom: -100 }}> <View style={{ position: 'sticky', top: 0, backgroundColor: '#ECEDEE', zIndex: 1, marginBottom: -100 }}>
{children} <View
style={{
flexDirection: 'column',
height: 80,
alignItems: 'center',
justifyContent: 'flex-end',
paddingBottom: 7
}}
>
<View style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
width: '100%',
paddingHorizontal: 0
}}>
{leftHeader}
{centerHeader}
{rightHeader}
</View>
</View>
</View> </View>
) )
} }
@ -228,7 +251,7 @@ export const LoginTextInput = ({
icon, icon,
isPassword = false, isPassword = false,
hidePassword = true, hidePassword = true,
setHidePassword = (boolean) => {}, setHidePassword = (boolean) => { },
...props ...props
}) => { }) => {
return ( return (
@ -239,7 +262,7 @@ export const LoginTextInput = ({
<StyledInputLabel>{label}</StyledInputLabel> <StyledInputLabel>{label}</StyledInputLabel>
<StyledTextInput {...props} /> <StyledTextInput {...props} />
{isPassword ? ( {isPassword ? (
<RightIcon onPress={() => {setHidePassword(!hidePassword)}}> <RightIcon onPress={() => { setHidePassword(!hidePassword) }}>
<Ionicons name={hidePassword ? 'eye-off-outline' : 'eye-outline'} size={30} color="gray" /> <Ionicons name={hidePassword ? 'eye-off-outline' : 'eye-outline'} size={30} color="gray" />
</RightIcon> </RightIcon>
) : null} ) : null}
@ -249,91 +272,108 @@ export const LoginTextInput = ({
export const RegisterDropdownInput = ({ export const RegisterDropdownInput = ({
label, label,
isOpen, isOpen: _isOpen,
setOpen, setOpen: _setOpen,
selectedValue, selectedValue,
onValueChange, onValueChange,
menu menu
}) => { }) => {
const [modalVisible, setModalVisible] = useState(false);
return ( return (
<Container> <Container>
<StyledInputLabel>{label}</StyledInputLabel> <StyledInputLabel>{label}</StyledInputLabel>
<TouchableOpacity activeOpacity={0.8} key={`${menu.menuName}2`} <TouchableOpacity
activeOpacity={0.8}
style={{ style={{
backgroundColor: '#E5E7EB', backgroundColor: '#E5E7EB',
marginTop: 3, marginTop: 3,
marginBottom: 10, marginBottom: 10,
borderRadius: '5px', borderRadius: 5,
flexDirection: 'row',
alignItems: 'center',
minHeight: 60,
paddingHorizontal: 15,
}} }}
onPress={() => { onPress={() => setModalVisible(true)}
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut); >
LayoutAnimation.configureNext(LayoutAnimation.create(200, 'easeInEaseOut', 'opacity')); <Ionicons
isOpen ? setOpen(false) : setOpen(true); name={menu.iconName}
size={30}
color={menu.iconColor}
/>
<Text style={{ color: selectedValue ? 'black' : 'grey', fontSize: 16, paddingHorizontal: 16, flex: 1 }}>
{selectedValue ? providerConversion[selectedValue] : menu.placeholder}
</Text>
<DropdownArrow onPress={() => setModalVisible(!modalVisible)}>
<Ionicons name={modalVisible ? "chevron-up-outline" : "chevron-down-outline"} size={30} color="gray" />
</DropdownArrow>
</TouchableOpacity>
<Modal
visible={modalVisible}
animationType="slide"
transparent={true}
onRequestClose={() => setModalVisible(false)}
>
<Pressable
style={{ flex: 1 }}
onPress={() => setModalVisible(false)}
/>
<View style={{
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
backgroundColor: '#fff',
borderTopLeftRadius: 16,
borderTopRightRadius: 16,
paddingBottom: 32,
paddingTop: 16,
}}> }}>
<Row style={{ <Pressable
paddingHorizontal: 16, style={{ flex: 1, paddingHorizontal: 15, alignItems: 'flex-end' }}
paddingVertical: 16 / 1.2, onPress={() => setModalVisible(false)}
}}> >
<Ionicons <Text style={{ color: 'red', fontWeight: 600 }}>
name={menu.iconName} Close
size={30} </Text>
color={menu.iconColor} </Pressable>
/> <Picker
<Text style={{ selectedValue={selectedValue}
fontSize: 16, onValueChange={(value) => {
paddingHorizontal: 16 onValueChange(value);
}}>
{selectedValue ? providerConversion[selectedValue] : menu.placeholder}
</Text>
<DropdownArrow
onPress={() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
LayoutAnimation.configureNext(LayoutAnimation.create(200, 'easeInEaseOut', 'opacity'));
isOpen ? setOpen(false) : setOpen(true);
}} }}
> >
<Ionicons {menu.dropdownList.map((item) => (
name={isOpen ? "chevron-up-outline" : "chevron-down-outline"} <Picker.Item color="black" label={item.label} value={item.value} key={item.value} />
size={30} ))}
color="gray" </Picker>
/> </View>
</DropdownArrow> </Modal>
</Row>
{isOpen && <ScrollView style={{ borderRadius: '5px', backgroundColor: '#E5E7EB' }}>
{menu.dropdownList.map((subMenu, index) => {
return (
<TouchableNativeFeedback
key={index}
onPress={() => {
onValueChange(subMenu.value);
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
LayoutAnimation.configureNext(LayoutAnimation.create(200, 'easeInEaseOut', 'opacity'));
setOpen(false);
}}
>
<View style={{
paddingHorizontal: 16,
paddingVertical: 16 / 1.5,
borderTopColor: 'gray',
borderTopWidth: .5,
marginHorizontal: 10
}}>
<Text>{subMenu.label}</Text>
{selectedValue === subMenu.value &&
<SelectedCheckmark>
<Ionicons
name="checkmark-outline"
size={30}
color="red"
/>
</SelectedCheckmark>
}
</View>
</TouchableNativeFeedback>
)
})}
</ScrollView>}
</TouchableOpacity>
</Container> </Container>
) );
} }
export const formatPhoneNumber = (e) => {
let formattedNumber;
const length = e?.length;
const regex = () => e.replace(/[^0-9\.]+/g, "");
const areaCode = () => `(${regex().slice(0, 3)})`;
const firstSix = () => `${areaCode()} ${regex().slice(3, 6)}`;
const trailer = (start) => `${regex().slice(start, regex().length)}`;
if (length <= 3) {
formattedNumber = regex();
} else if (length === 4) {
formattedNumber = `${areaCode()} ${trailer(3)}`;
} else if (length === 5) {
formattedNumber = `${areaCode().replace(")", "")}`;
} else if (length >= 5 && length <= 9) {
formattedNumber = `${areaCode()} ${trailer(3)}`;
} else if (length >= 10) {
formattedNumber = `${firstSix()}-${trailer(6)}`;
}
return formattedNumber;
};

30
contexts/AuthContext.js Normal file
View file

@ -0,0 +1,30 @@
import React, { createContext, useContext, useEffect, useState } from 'react';
import { onAuthStateChanged, signOut } from 'firebase/auth';
import { auth } from './firebase';
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (firebaseUser) => {
setUser(firebaseUser);
setLoading(false);
});
return unsubscribe;
}, []);
const logout = () => signOut(auth);
return (
<AuthContext.Provider value={{ user, loading, logout }}>
{children}
</AuthContext.Provider>
);
};
export const useAuth = () => {
return useContext(AuthContext);
};

View file

@ -0,0 +1,35 @@
import React, { createContext, useEffect, useState } from 'react';
import { AppState } from 'react-native';
export const GlobalVariablesContext = createContext(null);
export const GlobalVariablesProvider = ({ children }) => {
const [appState, setAppState] = useState(AppState.currentState);
useEffect(() => {
const handleAppStateChange = (nextAppState) => {
if (appState.match(/active/) && nextAppState.match(/inactive|background/)) {
console.log('App is in background or inactive');
} else if (appState.match(/inactive|background/) && nextAppState === 'active') {
console.log('App is in the foreground');
}
setAppState(nextAppState);
};
const subscription = AppState.addEventListener('change', handleAppStateChange);
return () => {
subscription.remove();
};
}, [appState]);
const exportedVariables = {
appState,
setAppState
}
return (
<GlobalVariablesContext.Provider value={exportedVariables}>
{children}
</GlobalVariablesContext.Provider>
)
};

View file

@ -20,7 +20,7 @@ export const WebSocketProvider = ({ children }) => {
} }
console.log(`🔁 Connecting (Attempt ${reconnectAttempts.current + 1}/${maxReconnectAttempts})`); console.log(`🔁 Connecting (Attempt ${reconnectAttempts.current + 1}/${maxReconnectAttempts})`);
ws.current = new WebSocket(process.env.EXPO_PUBLIC_WS_URL); ws.current = new WebSocket(`${process.env.EXPO_PUBLIC_WS_URL}/callfeed?apiKey=${process.env.EXPO_PUBLIC_DATABASE_API_KEY}`);
ws.current.onopen = () => { ws.current.onopen = () => {
console.log('✅ WebSocket connected'); console.log('✅ WebSocket connected');

23
contexts/firebase.js Normal file
View file

@ -0,0 +1,23 @@
import { initializeApp } from 'firebase/app';
import { initializeAuth, getReactNativePersistence } from 'firebase/auth';
import AsyncStorage from '@react-native-async-storage/async-storage';
const firebaseConfig = {
apiKey: process.env.EXPO_PUBLIC_FIREBASE_API_KEY,
authDomain: process.env.EXPO_PUBLIC_FIREBASE_AUTH_DOMAIN,
databaseURL: process.env.EXPO_PUBLIC_FIREBASE_DATABASE_URL,
projectId: process.env.EXPO_PUBLIC_FIREBASE_PROJECT_ID,
storageBucket: process.env.EXPO_PUBLIC_FIREBASE_STORAGE_BUCKET,
messagingSenderId: process.env.EXPO_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
appId: process.env.EXPO_PUBLIC_FIREBASE_APP_ID,
};
const app = initializeApp(firebaseConfig);
const auth = initializeAuth(app, {
persistence: getReactNativePersistence(AsyncStorage)
});
export { auth };

4
contexts/index.js Normal file
View file

@ -0,0 +1,4 @@
export { AuthProvider } from './AuthContext';
export { GlobalVariablesProvider, GlobalVariablesContext } from './GlobalVariablesContext';
export { postgresServices } from './postgres';
export { WebSocketProvider, WebSocketContext } from './WebSocketContext';

62
contexts/postgres.js Normal file
View file

@ -0,0 +1,62 @@
export const postgresServices = {
getCallsParams: async (args) => {
let response;
try {
const { numCalls, departments, status } = args;
response = await fetch(`${process.env.EXPO_PUBLIC_DATABASE_URL}/getCallsParams`, {
method: "POST",
headers: {
apiKey: process.env.EXPO_PUBLIC_DATABASE_API_KEY,
"Content-Type": 'application/json'
},
body: JSON.stringify({
numCalls,
departments,
status
}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
} catch (e) {
console.error("Failed to fetch initial calls: ", e);
}
return response?.json() || [];
},
createUser: async (args) => {
try {
const {
firstName,
lastName,
number,
email,
provider,
departments,
token,
uid
} = args;
await fetch(`${process.env.EXPO_PUBLIC_DATABASE_URL}/createUser`, {
method: "POST",
headers: {
apiKey: process.env.EXPO_PUBLIC_DATABASE_API_KEY,
"Content-Type": 'application/json'
},
body: JSON.stringify({
firstName,
lastName,
number,
email,
provider,
departments,
globalRole: 'user',
primaryDept,
token,
firebaseUid: uid
}),
});
} catch (e) {
console.error("Failed to Add User to Database: ", e);
}
return 'User Added to Database';
}
}

21
eas.json Normal file
View file

@ -0,0 +1,21 @@
{
"cli": {
"version": ">= 16.3.2",
"appVersionSource": "remote"
},
"build": {
"development": {
"developmentClient": true,
"distribution": "internal"
},
"preview": {
"distribution": "internal"
},
"production": {
"autoIncrement": true
}
},
"submit": {
"production": {}
}
}

View file

@ -1,2 +1,5 @@
export { useCallFeed } from './useCallFeed'; export { useCallFeed } from './useCallFeed';
export { useDepartments } from './useDepartments'; export { useDepartments } from './useDepartments';
export { useGlobalVariablesContext } from './useGlobalVariablesContext';
export { useNotifications } from './useNotifications';
export { useWebSocketContext } from './useWebSocketContext';

View file

@ -1,13 +1,18 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from "react";
import { useDepartments } from '../useDepartments'; import { useDepartments } from "../useDepartments";
import { C, Cardiology, Cpr, FourByFour } from "healthicons-react-native/dist/outline"; import {
import { useWebSocketContext } from '../useWebSocketContext'; Cardiology,
Cpr,
FourByFour,
} from "healthicons-react-native/dist/outline";
import { postgresServices } from "@/contexts";
import { useWebSocketContext } from "../useWebSocketContext";
const callIconMap = { const callIconMap = {
"CHEST PAIN|HEART PROBLEMS": Cardiology, "CHEST PAIN|HEART PROBLEMS": Cardiology,
"CARDIAC ARREST|DEATH": Cpr, "CARDIAC ARREST|DEATH": Cpr,
"MOTOR VEHICLE COLLISION (MVC)": FourByFour, "MOTOR VEHICLE COLLISION (MVC)": FourByFour,
} };
// Squares // Squares
@ -37,200 +42,116 @@ const callIconMap = {
// Bacteria - Sick // Bacteria - Sick
// RuralClinic - Medical Facility Response // RuralClinic - Medical Facility Response
const callDetails = {
"Incident": {
"IncID": 75,
"IncNumber": 6873,
"JurisdictionNumber": 3,
"ServiceNumber": 42,
"ServiceID": 45,
"IncDate": "2024-09-25T01:01:01.55",
// "IncNature": "CHEST PAIN|HEART PROBLEMS",
"IncNature": "MOTOR VEHICLE COLLISION (MVC)",
// "IncNature": "CARDIAC ARREST|DEATH",
"IncNatureCode": "ALS",
"IncNatureCodeDesc": "ALS PRIORITY (ALS)",
"Notes": "570, 16:30> 311 Responding\n580, 16:25> Call Dispatched",
"Status": "OPEN",
"Origin": "911"
},
"Address": {
"StreetAddress": "275 E Main St",
"AddressApartment": "IFO",
"Town": "Bridgeport",
"State": "CT",
"ZipCode": "06608",
"Latitude": 41.178435683035445,
"Longitude": -73.18194442701176,
"County": "Fairfield",
"Intersection1": "E MAIN ST",
"Intersection2": "STRATFORD AVE",
"LocationName": "Chipotle Mexican Grill",
"WeatherCondition": "Foggy"
},
"Person": {
"Name": "John Doe",
"Age": 19,
"Gender": "Female",
"Statement": "BLOOD PRESSURE 56/41 - IN AND OUT OF CON",
"Conscious": "No",
"Breathing": "Yes",
"CallBackNumber": "(223) 456-7890"
},
"Response": {
"IncID": 75,
"ResponseID": 75,
"ServiceID": 45,
"ServiceName": "DARIEN EMS",
"Units": [
{
"Unit": 311,
"Department": 'Darien EMS',
"Dispatched": "2024-09-25T01:01:01.55",
"Responding": "2024-09-25T01:02:02.55",
"OnScene": "2024-09-25T01:10:10.55",
"Transporting": "2024-09-25T01:25:01.55",
"InService": "2024-09-25T02:00:01.55",
},
{
"Unit": 315,
"Department": 'Darien EMS Supv',
"Dispatched": "2024-09-25T01:01:01.55",
"Responding": "2024-09-25T01:03:03.15",
"OnScene": "2024-09-25T01:11:11.55",
"Transporting": null,
"InService": "2024-09-25T02:10:01.55",
},
{
"Unit": 310,
"Department": 'Darien EMS',
"Dispatched": "2024-09-25T01:01:01.55",
"Responding": "2024-09-25T01:01:01.55",
"OnScene": "2024-09-25T01:06:06.55",
"Transporting": "2024-09-25T01:25:01.55",
"InService": "2024-09-25T02:15:01.55",
},
{
"Unit": 'NHT20',
"Department": 'Noroton Heights Fire Department',
"Dispatched": "2024-09-25T01:01:01.55",
"Responding": "2024-09-25T01:08:08.55",
"OnScene": "2024-09-25T01:12:12.55",
"Transporting": null,
"InService": "2024-09-25T01:01:01.55",
},
{
"Unit": 'NFDE31',
"Department": 'Noroton Fire Department',
"Dispatched": "2024-09-25T01:01:01.55",
"Responding": "2024-09-25T01:07:07.55",
"OnScene": "2024-09-25T01:14:14.55",
"Transporting": null,
"InService": "2024-09-25T01:01:01.55",
},
{
"Unit": 'DFDT43',
"Department": 'Darien Fire Department',
"Dispatched": "2024-09-25T01:01:01.55",
"Responding": "2024-09-25T01:10:10.55",
"OnScene": "2024-09-25T01:18:18.55",
"Transporting": null,
"InService": "2024-09-25T01:01:01.55",
},
{
"Unit": 1514,
"Department": 'Greenwich EMS',
"Dispatched": "2024-09-25T01:15:15.55",
"Responding": "2024-09-25T01:30:30.55",
"OnScene": "2024-09-25T01:45:45.55",
"Transporting": "2024-09-25T02:05:15.55",
"InService": "2024-09-25T02:25:01.55",
},
]
}
}
const formatCallTimePast = (callValue) => { const formatCallTimePast = (callValue) => {
const initDate = new Date(callValue); const initDate = new Date(callValue);
const currentTime = new Date(); const currentTime = new Date();
const timeDifference = currentTime - initDate; const timeDifference = currentTime - initDate;
const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24)); const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
const hours = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)); const hours = Math.floor(
const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60)); (timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
const seconds = Math.floor((timeDifference % (1000 * 60)) / 1000); );
if (days && days !== 0) { const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
return `${days} day${days === 1 ? '' : 's'} ago`; const seconds = Math.floor((timeDifference % (1000 * 60)) / 1000);
} else if (hours && hours !== 0) { if (days && days !== 0) {
return `${hours} hour${hours === 1 ? '' : 's'} ago`; return `${days} day${days === 1 ? "" : "s"} ago`;
} else if (minutes && minutes !== 0) { } else if (hours && hours !== 0) {
return `${minutes} minute${minutes === 1 ? '' : 's'} ago`; return `${hours} hour${hours === 1 ? "" : "s"} ago`;
} else if (seconds && seconds !== 0) { } else if (minutes && minutes !== 0) {
return `${seconds} second${seconds === 1 ? '' : 's'} ago`; return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
} } else if (seconds && seconds !== 0) {
return `Unknown Time Past`; return `${seconds} second${seconds === 1 ? "" : "s"} ago`;
} }
return `Unknown Time Past`;
};
const formatCallDateTime = (callValue) => { const formatCallDateTime = (callValue) => {
const initDate = new Date(callValue); const initDate = new Date(callValue);
if (initDate) { if (initDate) {
const formattedDate = `${initDate.toLocaleDateString('en-US', { const formattedDate = `${initDate.toLocaleDateString("en-US", {
month: 'short', month: "short",
day: 'numeric', day: "numeric",
year: 'numeric', year: "numeric",
})}`; })}`;
const hours = initDate.getHours().toString().padStart(2, '0'); const hours = initDate.getHours().toString().padStart(2, "0");
const minutes = initDate.getMinutes().toString().padStart(2, '0'); const minutes = initDate.getMinutes().toString().padStart(2, "0");
const formattedTime = `${hours}:${minutes}`; const formattedTime = `${hours}:${minutes}`;
return `${formattedDate} - ${formattedTime}`; return `${formattedDate} - ${formattedTime}`;
}
return "Date Unavailable";
};
const getIncidents = async (departments, incidentStatus) => {
return postgresServices.getCallsParams({
numCalls: 25,
departments,
status: incidentStatus
});
};
export const useCallFeed = (callPage = false) => {
const departments = useDepartments();
const { lastMessage } = useWebSocketContext();
const [allCalls, setAllCalls] = useState([]);
const [init, setInit] = useState(true);
const { CallThemes, InitList } = departments?.accountDetails;
const { CriticalCallList, HighCallList, MediumCallList, LowCallList } =
CallThemes;
const [selectedStatus, setSelectedStatus] = useState({id: 'all', value: 'All Incidents'});
useEffect(() => {
async function fetchData() {
const incidents = await getIncidents(InitList?.map((dept) => { return dept?.deptId }), selectedStatus?.id);
setAllCalls(incidents);
setInit(false);
} }
return 'Date Unavailable'; if (!callPage) fetchData();
} }, []);
export const useCallFeed = () => { useEffect(() => {
const departments = useDepartments(); if (lastMessage && !init) {
const { lastMessage } = useWebSocketContext(); const parsedMessage = JSON?.parse(lastMessage);
const [allCalls, setAllCalls] = useState([]); if (parsedMessage?.data) {
const { CallThemes } = departments?.accountDetails; setAllCalls([...allCalls, parsedMessage]);
const { }
CriticalCallList,
HighCallList,
MediumCallList,
LowCallList,
} = CallThemes;
useEffect(() => {
if (lastMessage) {
const parsedMessage = JSON?.parse(lastMessage);
if (parsedMessage?.data) {
setAllCalls([...allCalls, parsedMessage]);
}
}
}, [lastMessage]);
const callColorSelector = (callAcuity, cardiacArrestCall, status) => {
if (status === 'CLOSED') {
return '#0000CD';
} else if (CriticalCallList.includes(cardiacArrestCall)) {
return '#8B0000';
} else if (HighCallList.includes(callAcuity)) {
return "#FF0000";
} else if (MediumCallList.includes(callAcuity)) {
return "#FF8C00";
} else if (LowCallList.includes(callAcuity)) {
return "#228B22";
}
return 'grey';
};
return {
departments,
callIconMap,
callDetails: allCalls,
callColorSelector,
formatCallTimePast,
formatCallDateTime
} }
} }, [lastMessage]);
useEffect(() => {
async function fetchData() {
const incidents = await getIncidents(InitList?.map((dept) => { return dept?.deptId }), selectedStatus?.id);
setAllCalls(incidents);
}
if (!init) {
if (!callPage) fetchData();
}
}, [selectedStatus]);
const callColorSelector = (callAcuity, cardiacArrestCall, status) => {
if (status?.toLowerCase() === "closed") {
return "#0000CD";
} else if (CriticalCallList.some(critical => cardiacArrestCall.includes(critical))) {
return "#8B0000";
} else if (HighCallList.some(high => callAcuity.includes(high))) {
return "#FF0000";
} else if (MediumCallList.some(medium => callAcuity.includes(medium))) {
return "#FF8C00";
} else if (LowCallList.some(low => callAcuity.includes(low))) {
return "#228B22";
}
return "grey";
};
return {
departments,
callIconMap,
callDetails: allCalls,
callColorSelector,
formatCallTimePast,
formatCallDateTime,
selectedStatus,
setSelectedStatus
};
};

View file

@ -1,5 +1,4 @@
import React, { useState, useEffect } from 'react'; import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
const departmentTypeMap = { const departmentTypeMap = {
EMS: 'Medical Services', EMS: 'Medical Services',
@ -10,27 +9,47 @@ const departmentTypeMap = {
const accountDetails = { const accountDetails = {
"CallThemes" : { "CallThemes" : {
"CriticalCallList": [ "CriticalCallList": [
"CARDIAC ARREST|DEATH", "CARDIAC ARREST"
], ],
"HighCallList": [ "HighCallList": [
"ALS" "EMS ALS PRIORITY (ALS)",
"EMS Advanced Life Support life threatening response (ALS)",
"EMS ALS Intercept Required",
"EMS ALS Response Cardiac Related",
"EMS ALS Response Respiratory Distress",
"EMS ALS Response Stroke Alert",
"EMS Basic Life Support with Paramedic Assist (BLS)",
"EMS ALS Tiered Response with Fire",
"EMS Response Behavioral Emergency (ALS)",
"EMS Response Obstetric Emergency (ALS)",
"EMS ALS Response Overdose / Poisoning",
"EMS ALS Priority Interfacility Transfer",
"EMS Response Unresponsive / Unconscious Patient (ALS)"
], ],
"MediumCallList": [ "MediumCallList": [
"ALS-STANDARD", "ALS-STANDARD",
"BLS-PRIORITY" "BLS-PRIORITY",
"EMS BLS Priority Response",
"EMS BLS Response Fall Injury"
], ],
"LowCallList": [ "LowCallList": [
"BLS-STANDARD" "EMS Standard Basic Life Support Response (BLS)",
"EMS BLS Non-Emergency Transport",
"EMS BLS Standby Event Coverage",
"EMS Public Assist Lift Only (BLS)",
"EMS BLS Scheduled Interfacility Transport"
] ]
}, },
"InitList": [ "InitList": [
{ {
deptId: 0, deptId: 1,
dept: 'Darien EMS', dept: 'Darien EMS',
addDepts: [ addDepts: [
'Darien EMS Supv' 'Darien EMS Supv'
], ],
deptAbv: 'DEMS', deptAbv: 'DEMS',
rank: 'Assistant Director',
rankAbv: 'Asst. Director',
type: 'EMS', type: 'EMS',
primary: true, primary: true,
selected: true, selected: true,
@ -39,9 +58,11 @@ const accountDetails = {
hasVolunteer: true, hasVolunteer: true,
}, },
{ {
deptId: 1, deptId: 2,
dept: 'Noroton Fire Department', dept: 'Noroton Fire Department',
deptAbv: 'NFD', deptAbv: 'NFD',
rank: 'Lieutenant',
rankAbv: 'Lt.',
type: 'Fire', type: 'Fire',
primary: false, primary: false,
selected: false, selected: false,
@ -50,9 +71,11 @@ const accountDetails = {
hasVolunteer: true, hasVolunteer: true,
}, },
{ {
deptId: 2, deptId: 3,
dept: 'Stamford Fire Department', dept: 'Stamford Fire Rescue',
deptAbv: 'SFD', deptAbv: 'SFD',
rank: 'Paramedic',
rankAbv: 'EMT-P',
type: 'Rescue', type: 'Rescue',
primary: false, primary: false,
selected: false, selected: false,

View file

@ -0,0 +1 @@
export { useGlobalVariablesContext } from './useGlobalVariablesContext';

View file

@ -0,0 +1,4 @@
import { useContext } from 'react';
import { GlobalVariablesContext } from '../../contexts';
export const useGlobalVariablesContext = () => useContext(GlobalVariablesContext);

View file

@ -0,0 +1 @@
export { useNotifications } from './useNotifications';

View file

@ -0,0 +1,112 @@
import { useState, useEffect, useRef } from 'react';
import { Platform } from 'react-native';
import * as Device from 'expo-device';
import * as Notifications from 'expo-notifications';
import Constants from 'expo-constants';
Notifications.setNotificationHandler({
handleNotification: async () => ({
shouldShowAlert: true,
shouldPlaySound: false,
shouldSetBadge: false,
}),
});
const schedulePushNotification = async (title, body, dataObj) => {
await Notifications.scheduleNotificationAsync({
content: {
title,
body,
data: { data: dataObj },
},
trigger: {
type: Notifications.SchedulableTriggerInputTypes.TIME_INTERVAL,
seconds: 2,
},
});
}
const registerForPushNotificationsAsync = async () => {
let token;
if (Platform.OS === 'android') {
await Notifications.setNotificationChannelAsync('myNotificationChannel', {
name: 'A channel is needed for the permissions prompt to appear',
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: '#FF231F7C',
});
}
// if (Device.isDevice) {
const { status: existingStatus } = await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== 'granted') {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== 'granted') {
alert('Failed to get push token for push notification!');
return;
}
// Learn more about projectId:
// https://docs.expo.dev/push-notifications/push-notifications-setup/#configure-projectid
// EAS projectId is used here.
try {
const projectId =
Constants?.expoConfig?.extra?.eas?.projectId ?? Constants?.easConfig?.projectId;
if (!projectId) {
throw new Error('Project ID not found');
}
token = (
await Notifications.getExpoPushTokenAsync({
projectId,
})
).data;
} catch (e) {
token = `${e}`;
}
// } else {
// alert('Must use physical device for Push Notifications');
// }
return token;
}
export const useNotifications = () => {
const [expoPushToken, setExpoPushToken] = useState('');
const [channels, setChannels] = useState([]);
const [notification, setNotification] = useState(undefined);
const notificationListener = useRef();
const responseListener = useRef();
useEffect(() => {
registerForPushNotificationsAsync().then(token => token && setExpoPushToken(token));
if (Platform.OS === 'android') {
Notifications.getNotificationChannelsAsync().then(value => setChannels(value ?? []));
}
notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
setNotification(notification);
});
responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
console.log(response);
});
return () => {
notificationListener.current?.remove();
responseListener.current?.remove();
};
}, []);
return ({
schedulePushNotification: async (title, body, dataObj) => {
await schedulePushNotification(title, body, dataObj);
},
expoPushToken,
expoNotificationChannels: channels,
expoNotification: notification,
});
}

View file

@ -1,4 +1,4 @@
import { useContext } from 'react'; import { useContext } from 'react';
import { WebSocketContext } from '../../contexts/WebSocketContext'; import { WebSocketContext } from '../../contexts';
export const useWebSocketContext = () => useContext(WebSocketContext); export const useWebSocketContext = () => useContext(WebSocketContext);

View file

@ -7,15 +7,31 @@ podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties
ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0'
ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR']
platform :ios, podfile_properties['ios.deploymentTarget'] || '13.4' platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1'
install! 'cocoapods', install! 'cocoapods',
:deterministic_uuids => false :deterministic_uuids => false
prepare_react_native_project! prepare_react_native_project!
target 'testapplication' do target 'Tones' do
use_expo_modules! use_expo_modules!
config = use_native_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 => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks']
use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
@ -47,12 +63,4 @@ target 'testapplication' do
end end
end end
end end
post_integrate do |installer|
begin
expo_patch_react_imports!(installer)
rescue => e
Pod::UI.warn e
end
end
end end

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
{ {
"expo.jsEngine": "hermes", "expo.jsEngine": "hermes",
"EX_DEV_CLIENT_NETWORK_INSPECTOR": "true" "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true",
"newArchEnabled": "false"
} }

View file

@ -10,31 +10,31 @@
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
3BB8D1083F3D4AAFA506B09F /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1350D069573E4AEBBF8BA6A3 /* noop-file.swift */; };
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; }; 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
640C5CE36A784C1E86D867C7 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2358EABEB0CA4428951E7149 /* noop-file.swift */; }; 93A35CBBC1C2DF84FE39E633 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EA8B81AA605F084BFB23750B /* PrivacyInfo.xcprivacy */; };
96905EF65AED1B983A6B3ABC /* libPods-testapplication.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-testapplication.a */; }; 96905EF65AED1B983A6B3ABC /* libPods-Tones.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Tones.a */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; }; B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; }; BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
FF358844A8FA907CDEC7216D /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = A9DA6C7B26A64A23DFDE186E /* PrivacyInfo.xcprivacy */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
13B07F961A680F5B00A75B9A /* testapplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testapplication.app; sourceTree = BUILT_PRODUCTS_DIR; }; 1350D069573E4AEBBF8BA6A3 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "Tones/noop-file.swift"; sourceTree = "<group>"; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = testapplication/AppDelegate.h; sourceTree = "<group>"; }; 13B07F961A680F5B00A75B9A /* Tones.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tones.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = testapplication/AppDelegate.mm; sourceTree = "<group>"; }; 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Tones/AppDelegate.h; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = testapplication/Images.xcassets; sourceTree = "<group>"; }; 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Tones/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapplication/Info.plist; sourceTree = "<group>"; }; 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Tones/Images.xcassets; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapplication/main.m; sourceTree = "<group>"; }; 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Tones/Info.plist; sourceTree = "<group>"; };
2358EABEB0CA4428951E7149 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "testapplication/noop-file.swift"; sourceTree = "<group>"; }; 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Tones/main.m; sourceTree = "<group>"; };
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-testapplication.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-testapplication.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Tones.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tones.a"; sourceTree = BUILT_PRODUCTS_DIR; };
6C2E3173556A471DD304B334 /* Pods-testapplication.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testapplication.debug.xcconfig"; path = "Target Support Files/Pods-testapplication/Pods-testapplication.debug.xcconfig"; sourceTree = "<group>"; }; 6C2E3173556A471DD304B334 /* Pods-Tones.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tones.debug.xcconfig"; path = "Target Support Files/Pods-Tones/Pods-Tones.debug.xcconfig"; sourceTree = "<group>"; };
7A4D352CD337FB3A3BF06240 /* Pods-testapplication.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-testapplication.release.xcconfig"; path = "Target Support Files/Pods-testapplication/Pods-testapplication.release.xcconfig"; sourceTree = "<group>"; }; 7A4D352CD337FB3A3BF06240 /* Pods-Tones.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Tones.release.xcconfig"; path = "Target Support Files/Pods-Tones/Pods-Tones.release.xcconfig"; sourceTree = "<group>"; };
96FAC8CF002144E68DCEBFD0 /* testapplication-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "testapplication-Bridging-Header.h"; path = "testapplication/testapplication-Bridging-Header.h"; sourceTree = "<group>"; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Tones/SplashScreen.storyboard; sourceTree = "<group>"; };
A9DA6C7B26A64A23DFDE186E /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = testapplication/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = testapplication/SplashScreen.storyboard; sourceTree = "<group>"; };
BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; }; BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = "<group>"; };
EA8B81AA605F084BFB23750B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = Tones/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; 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-testapplication/ExpoModulesProvider.swift"; sourceTree = "<group>"; }; F8CAF977DAD74AAA91FDDD99 /* Tones-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "Tones-Bridging-Header.h"; path = "Tones/Tones-Bridging-Header.h"; sourceTree = "<group>"; };
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-Tones/ExpoModulesProvider.swift"; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@ -42,14 +42,14 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
96905EF65AED1B983A6B3ABC /* libPods-testapplication.a in Frameworks */, 96905EF65AED1B983A6B3ABC /* libPods-Tones.a in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
/* End PBXFrameworksBuildPhase section */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
13B07FAE1A68108700A75B9A /* testapplication */ = { 13B07FAE1A68108700A75B9A /* Tones */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
BB2F792B24A3F905000567C9 /* Supporting */, BB2F792B24A3F905000567C9 /* Supporting */,
@ -59,18 +59,18 @@
13B07FB61A68108700A75B9A /* Info.plist */, 13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB71A68108700A75B9A /* main.m */, 13B07FB71A68108700A75B9A /* main.m */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */, AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
2358EABEB0CA4428951E7149 /* noop-file.swift */, 1350D069573E4AEBBF8BA6A3 /* noop-file.swift */,
96FAC8CF002144E68DCEBFD0 /* testapplication-Bridging-Header.h */, F8CAF977DAD74AAA91FDDD99 /* Tones-Bridging-Header.h */,
A9DA6C7B26A64A23DFDE186E /* PrivacyInfo.xcprivacy */, EA8B81AA605F084BFB23750B /* PrivacyInfo.xcprivacy */,
); );
name = testapplication; name = Tones;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
2D16E6871FA4F8E400B85C8A /* Frameworks */ = { 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */, ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-testapplication.a */, 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Tones.a */,
); );
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; sourceTree = "<group>";
@ -85,7 +85,7 @@
83CBB9F61A601CBA00E9B192 = { 83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
13B07FAE1A68108700A75B9A /* testapplication */, 13B07FAE1A68108700A75B9A /* Tones */,
832341AE1AAA6A7D00B99B32 /* Libraries */, 832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */, 83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */, 2D16E6871FA4F8E400B85C8A /* Frameworks */,
@ -100,17 +100,17 @@
83CBBA001A601CBA00E9B192 /* Products */ = { 83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
13B07F961A680F5B00A75B9A /* testapplication.app */, 13B07F961A680F5B00A75B9A /* Tones.app */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
92DBD88DE9BF7D494EA9DA96 /* testapplication */ = { 92DBD88DE9BF7D494EA9DA96 /* Tones */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */, FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
); );
name = testapplication; name = Tones;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
BB2F792B24A3F905000567C9 /* Supporting */ = { BB2F792B24A3F905000567C9 /* Supporting */ = {
@ -119,14 +119,14 @@
BB2F792C24A3F905000567C9 /* Expo.plist */, BB2F792C24A3F905000567C9 /* Expo.plist */,
); );
name = Supporting; name = Supporting;
path = testapplication/Supporting; path = Tones/Supporting;
sourceTree = "<group>"; sourceTree = "<group>";
}; };
D65327D7A22EEC0BE12398D9 /* Pods */ = { D65327D7A22EEC0BE12398D9 /* Pods */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6C2E3173556A471DD304B334 /* Pods-testapplication.debug.xcconfig */, 6C2E3173556A471DD304B334 /* Pods-Tones.debug.xcconfig */,
7A4D352CD337FB3A3BF06240 /* Pods-testapplication.release.xcconfig */, 7A4D352CD337FB3A3BF06240 /* Pods-Tones.release.xcconfig */,
); );
path = Pods; path = Pods;
sourceTree = "<group>"; sourceTree = "<group>";
@ -134,7 +134,7 @@
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = { D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
92DBD88DE9BF7D494EA9DA96 /* testapplication */, 92DBD88DE9BF7D494EA9DA96 /* Tones */,
); );
name = ExpoModulesProviders; name = ExpoModulesProviders;
sourceTree = "<group>"; sourceTree = "<group>";
@ -142,26 +142,26 @@
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
13B07F861A680F5B00A75B9A /* testapplication */ = { 13B07F861A680F5B00A75B9A /* Tones */ = {
isa = PBXNativeTarget; isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "testapplication" */; buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Tones" */;
buildPhases = ( buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */, 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
7E5AAFC44944C4D49F90AE74 /* [Expo] Configure project */, BBDF11722E0998CCEE7FC139 /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */, 13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */, 13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */, 13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */, 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
6CD366C487D1C4076EEFC1E9 /* [CP] Embed Pods Frameworks */, 5F318AA4CE51F8E175CC6D0A /* [CP] Embed Pods Frameworks */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
); );
name = testapplication; name = Tones;
productName = testapplication; productName = Tones;
productReference = 13B07F961A680F5B00A75B9A /* testapplication.app */; productReference = 13B07F961A680F5B00A75B9A /* Tones.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
}; };
/* End PBXNativeTarget section */ /* End PBXNativeTarget section */
@ -177,7 +177,7 @@
}; };
}; };
}; };
buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testapplication" */; buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Tones" */;
compatibilityVersion = "Xcode 3.2"; compatibilityVersion = "Xcode 3.2";
developmentRegion = en; developmentRegion = en;
hasScannedForEncodings = 0; hasScannedForEncodings = 0;
@ -190,7 +190,7 @@
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
targets = ( targets = (
13B07F861A680F5B00A75B9A /* testapplication */, 13B07F861A680F5B00A75B9A /* Tones */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@ -203,7 +203,7 @@
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */, BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */, 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
FF358844A8FA907CDEC7216D /* PrivacyInfo.xcprivacy in Resources */, 93A35CBBC1C2DF84FE39E633 /* PrivacyInfo.xcprivacy in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -240,20 +240,20 @@
outputFileListPaths = ( outputFileListPaths = (
); );
outputPaths = ( outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-testapplication-checkManifestLockResult.txt", "$(DERIVED_FILE_DIR)/Pods-Tones-checkManifestLockResult.txt",
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; 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"; 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; showEnvVarsInLog = 0;
}; };
6CD366C487D1C4076EEFC1E9 /* [CP] Embed Pods Frameworks */ = { 5F318AA4CE51F8E175CC6D0A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
); );
inputPaths = ( inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-testapplication/Pods-testapplication-frameworks.sh", "${PODS_ROOT}/Target Support Files/Pods-Tones/Pods-Tones-frameworks.sh",
"${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes", "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
); );
name = "[CP] Embed Pods Frameworks"; name = "[CP] Embed Pods Frameworks";
@ -262,10 +262,50 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-testapplication/Pods-testapplication-frameworks.sh\"\n"; shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tones/Pods-Tones-frameworks.sh\"\n";
showEnvVarsInLog = 0; showEnvVarsInLog = 0;
}; };
7E5AAFC44944C4D49F90AE74 /* [Expo] Configure project */ = { 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Tones/Pods-Tones-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/EXApplication/ExpoApplication_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXNotifications/ExpoNotifications_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoDevice/ExpoDevice_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/RCT-Folly/RCT-Folly_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
);
name = "[CP] Copy Pods Resources";
outputPaths = (
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoApplication_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoNotifications_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoDevice_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCT-Folly_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Tones/Pods-Tones-resources.sh\"\n";
showEnvVarsInLog = 0;
};
BBDF11722E0998CCEE7FC139 /* [Expo] Configure project */ = {
isa = PBXShellScriptBuildPhase; isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1; alwaysOutOfDate = 1;
buildActionMask = 2147483647; buildActionMask = 2147483647;
@ -282,33 +322,7 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh; shellPath = /bin/sh;
shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-testapplication/expo-configure-project.sh\"\n"; shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-Tones/expo-configure-project.sh\"\n";
};
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-testapplication/Pods-testapplication-resources.sh",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
"${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.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}/ExpoConstants_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle",
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-testapplication/Pods-testapplication-resources.sh\"\n";
showEnvVarsInLog = 0;
}; };
/* End PBXShellScriptBuildPhase section */ /* End PBXShellScriptBuildPhase section */
@ -320,7 +334,7 @@
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */, 13B07FC11A68108700A75B9A /* main.m in Sources */,
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */, B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
640C5CE36A784C1E86D867C7 /* noop-file.swift in Sources */, 3BB8D1083F3D4AAFA506B09F /* noop-file.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -329,19 +343,19 @@
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = { 13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-testapplication.debug.xcconfig */; baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-Tones.debug.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = testapplication/testapplication.entitlements; CODE_SIGN_ENTITLEMENTS = Tones/Tones.entitlements;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)", "$(inherited)",
"FB_SONARKIT_ENABLED=1", "FB_SONARKIT_ENABLED=1",
); );
INFOPLIST_FILE = testapplication/Info.plist; INFOPLIST_FILE = Tones/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4; IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
@ -350,9 +364,9 @@
"-lc++", "-lc++",
); );
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.testapplication; PRODUCT_BUNDLE_IDENTIFIER = com.mattdimegs.tones;
PRODUCT_NAME = testapplication; PRODUCT_NAME = Tones;
SWIFT_OBJC_BRIDGING_HEADER = "testapplication/testapplication-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Tones/Tones-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
@ -362,14 +376,14 @@
}; };
13B07F951A680F5B00A75B9A /* Release */ = { 13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-testapplication.release.xcconfig */; baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-Tones.release.xcconfig */;
buildSettings = { buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = testapplication/testapplication.entitlements; CODE_SIGN_ENTITLEMENTS = Tones/Tones.entitlements;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
INFOPLIST_FILE = testapplication/Info.plist; INFOPLIST_FILE = Tones/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4; IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = ( OTHER_LDFLAGS = (
@ -378,9 +392,9 @@
"-lc++", "-lc++",
); );
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE"; OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.testapplication; PRODUCT_BUNDLE_IDENTIFIER = com.mattdimegs.tones;
PRODUCT_NAME = testapplication; PRODUCT_NAME = Tones;
SWIFT_OBJC_BRIDGING_HEADER = "testapplication/testapplication-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Tones/Tones-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2"; TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic"; VERSIONING_SYSTEM = "apple-generic";
@ -391,7 +405,6 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CC = "";
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@ -418,7 +431,6 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO; COPY_PHASE_STRIP = NO;
CXX = "";
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES; ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
@ -436,9 +448,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4; IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD = "";
LDPLUSPLUS = "";
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES; MTL_ENABLE_DEBUG_INFO = YES;
@ -449,6 +459,7 @@
); );
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true; USE_HERMES = true;
}; };
name = Debug; name = Debug;
@ -457,7 +468,6 @@
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO; ALWAYS_SEARCH_USER_PATHS = NO;
CC = "";
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++"; CLANG_CXX_LIBRARY = "libc++";
@ -484,7 +494,6 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES; COPY_PHASE_STRIP = YES;
CXX = "";
ENABLE_NS_ASSERTIONS = NO; ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99; GCC_C_LANGUAGE_STANDARD = gnu99;
@ -495,9 +504,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES; GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4; IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD = "";
LDPLUSPLUS = "";
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO; MTL_ENABLE_DEBUG_INFO = NO;
@ -515,7 +522,7 @@
/* End XCBuildConfiguration section */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "testapplication" */ = { 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Tones" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */, 13B07F941A680F5B00A75B9A /* Debug */,
@ -524,7 +531,7 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testapplication" */ = { 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Tones" */ = {
isa = XCConfigurationList; isa = XCConfigurationList;
buildConfigurations = ( buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */, 83CBBA201A601CBA00E9B192 /* Debug */,

View file

@ -15,9 +15,9 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A" BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "testapplication.app" BuildableName = "Tones.app"
BlueprintName = "testapplication" BlueprintName = "Tones"
ReferencedContainer = "container:testapplication.xcodeproj"> ReferencedContainer = "container:Tones.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildActionEntry> </BuildActionEntry>
</BuildActionEntries> </BuildActionEntries>
@ -33,9 +33,9 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "00E356ED1AD99517003FC87E" BlueprintIdentifier = "00E356ED1AD99517003FC87E"
BuildableName = "testapplicationTests.xctest" BuildableName = "TonesTests.xctest"
BlueprintName = "testapplicationTests" BlueprintName = "TonesTests"
ReferencedContainer = "container:testapplication.xcodeproj"> ReferencedContainer = "container:Tones.xcodeproj">
</BuildableReference> </BuildableReference>
</TestableReference> </TestableReference>
</Testables> </Testables>
@ -55,9 +55,9 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A" BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "testapplication.app" BuildableName = "Tones.app"
BlueprintName = "testapplication" BlueprintName = "Tones"
ReferencedContainer = "container:testapplication.xcodeproj"> ReferencedContainer = "container:Tones.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
</LaunchAction> </LaunchAction>
@ -72,9 +72,9 @@
<BuildableReference <BuildableReference
BuildableIdentifier = "primary" BuildableIdentifier = "primary"
BlueprintIdentifier = "13B07F861A680F5B00A75B9A" BlueprintIdentifier = "13B07F861A680F5B00A75B9A"
BuildableName = "testapplication.app" BuildableName = "Tones.app"
BlueprintName = "testapplication" BlueprintName = "Tones"
ReferencedContainer = "container:testapplication.xcodeproj"> ReferencedContainer = "container:Tones.xcodeproj">
</BuildableReference> </BuildableReference>
</BuildableProductRunnable> </BuildableProductRunnable>
</ProfileAction> </ProfileAction>

View file

@ -2,7 +2,7 @@
<Workspace <Workspace
version = "1.0"> version = "1.0">
<FileRef <FileRef
location = "group:testapplication.xcodeproj"> location = "group:Tones.xcodeproj">
</FileRef> </FileRef>
<FileRef <FileRef
location = "group:Pods/Pods.xcodeproj"> location = "group:Pods/Pods.xcodeproj">

View file

@ -0,0 +1,32 @@
<?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>API_KEY</key>
<string>AIzaSyClZkx4PFWhfIG3JDb6GD3g6Bl7-pWLNrU</string>
<key>GCM_SENDER_ID</key>
<string>917296693147</string>
<key>PLIST_VERSION</key>
<string>1</string>
<key>BUNDLE_ID</key>
<string>com.mattdimegs.tones</string>
<key>PROJECT_ID</key>
<string>tones-9f1d4</string>
<key>STORAGE_BUCKET</key>
<string>tones-9f1d4.firebasestorage.app</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:917296693147:ios:73d6d426aa60e52b35ab3e</string>
<key>DATABASE_URL</key>
<string>https://tones-9f1d4-default-rtdb.firebaseio.com</string>
</dict>
</plist>

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

View file

@ -0,0 +1,20 @@
{
"colors": [
{
"color": {
"components": {
"alpha": "1.000",
"blue": "1.00000000000000",
"green": "1.00000000000000",
"red": "1.00000000000000"
},
"color-space": "srgb"
},
"idiom": "universal"
}
],
"info": {
"version": 1,
"author": "expo"
}
}

View file

@ -7,10 +7,12 @@
}, },
{ {
"idiom": "universal", "idiom": "universal",
"filename": "image@2x.png",
"scale": "2x" "scale": "2x"
}, },
{ {
"idiom": "universal", "idiom": "universal",
"filename": "image@3x.png",
"scale": "3x" "scale": "3x"
} }
], ],

View file

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View file

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View file

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View file

@ -7,7 +7,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>test-application</string> <string>Tones</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@ -19,7 +19,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> <string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.0.0</string> <string>1.0.1</string>
<key>CFBundleSignature</key> <key>CFBundleSignature</key>
<string>????</string> <string>????</string>
<key>CFBundleURLTypes</key> <key>CFBundleURLTypes</key>
@ -28,12 +28,14 @@
<key>CFBundleURLSchemes</key> <key>CFBundleURLSchemes</key>
<array> <array>
<string>myapp</string> <string>myapp</string>
<string>com.anonymous.testapplication</string> <string>com.mattdimegs.tones</string>
</array> </array>
</dict> </dict>
</array> </array>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>
<key>NSAppTransportSecurity</key> <key>NSAppTransportSecurity</key>

View file

@ -4,6 +4,16 @@
<dict> <dict>
<key>NSPrivacyAccessedAPITypes</key> <key>NSPrivacyAccessedAPITypes</key>
<array> <array>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>C617.1</string>
<string>0A2A.1</string>
<string>3B52.1</string>
</array>
</dict>
<dict> <dict>
<key>NSPrivacyAccessedAPIType</key> <key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryUserDefaults</string> <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
@ -14,12 +24,10 @@
</dict> </dict>
<dict> <dict>
<key>NSPrivacyAccessedAPIType</key> <key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string> <string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key> <key>NSPrivacyAccessedAPITypeReasons</key>
<array> <array>
<string>0A2A.1</string> <string>35F9.1</string>
<string>3B52.1</string>
<string>C617.1</string>
</array> </array>
</dict> </dict>
<dict> <dict>
@ -31,14 +39,6 @@
<string>85F4.1</string> <string>85F4.1</string>
</array> </array>
</dict> </dict>
<dict>
<key>NSPrivacyAccessedAPIType</key>
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
<key>NSPrivacyAccessedAPITypeReasons</key>
<array>
<string>35F9.1</string>
</array>
</dict>
</array> </array>
<key>NSPrivacyCollectedDataTypes</key> <key>NSPrivacyCollectedDataTypes</key>
<array/> <array/>

Some files were not shown because too many files have changed in this diff Show more