Compare commits

..

34 commits

Author SHA1 Message Date
c1e2ddd811 Update app.json 2025-08-25 15:05:05 -04:00
60f36e22b1 Update packages 2025-08-25 10:53:02 -04:00
067c3e9df1 linter 2025-08-25 10:35:59 -04:00
64856b0695 Update useCallFeed.jsx 2025-08-25 10:23:14 -04:00
6873abcc4c Update firebase.js 2025-08-25 10:21:23 -04:00
4ef53e9378 remove app 2025-08-25 10:19:22 -04:00
21bdce0e1d Fix Meta-data 2025-08-25 09:58:59 -04:00
889bd34520 Update build.gradle 2025-08-25 09:52:55 -04:00
f78bb3952f Update build.gradle 2025-08-25 09:52:16 -04:00
79fcedd24f Following Pre-Build 2025-08-25 09:50:55 -04:00
38ff55efba Push 2025-08-25 09:49:15 -04:00
38c8165298 add properties 2025-08-25 09:47:01 -04:00
9cf6e3ec8d Push 2025-08-25 09:46:27 -04:00
aafe568f18 Add Firebase App 2025-08-25 09:45:25 -04:00
a9782fef08 Add Postgres Services 2025-08-18 10:20:36 -04:00
e6c4389393 Update call.jsx 2025-08-13 21:37:18 -04:00
b91aa464af API Key Passthrough WebSocket 2025-08-13 01:43:35 -04:00
1b8533d192 Incident details encoding and UI headers
Introduces Unicode-safe base64 encoding/decoding for call details between incidents and call screens. Refactors PageHeader to accept left, center, and right header props for more flexible layouts. Updates call.jsx and register.jsx to use the new header structure, and improves department/unit rendering and modal dropdowns for department selection. Generalizes and modernizes UI code for better maintainability and cross-platform compatibility.
2025-08-12 16:36:26 -04:00
2f01c575db Fix and Delete Log 2025-08-12 15:01:49 -04:00
dd5a0523dc Adjust incident styles
Removed 'justifyContent: space-between' from the row container and updated the text container to use flex: 1, alignItems: 'flex-start', padding, and maxWidth for improved layout and text alignment.
2025-08-12 15:00:26 -04:00
4d21fbebf8 Fix call Statements 2025-08-12 12:19:05 -04:00
cb1758fb55 Changes for new DB 2025-08-11 15:34:16 -04:00
45f0dc948c Formatting & Add DB Call 2025-08-10 16:05:17 -04:00
34e0325f37 fix formatting 2025-08-10 10:14:56 -04:00
e32c8e108f Update incidents.jsx 2025-08-10 10:14:28 -04:00
0a857f86a7 Add Keyboard fixes & Realtime Database 2025-08-10 09:59:27 -04:00
1368369495 Big Updates -> 53.0.0 & Auth w/ FireBase 2025-08-10 03:11:09 -04:00
56a965e76c Typo 2025-04-22 00:32:31 -04:00
6ce41b0268 Build Changes 2025-04-21 20:04:21 -04:00
30efadddb1 move 2025-04-21 19:51:05 -04:00
9d42409ccb add 2025-04-21 19:49:32 -04:00
d01b41973d Add project stuff 2025-04-21 18:34:36 -04:00
0f3ef6aee5 Add Project ID 2025-04-21 18:04:03 -04:00
051837968a Add Customizable Notifications & Add Global Variables 2025-04-21 11:41:08 -04:00
113 changed files with 7918 additions and 10067 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"
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
// works correctly with Expo projects.
cliFile = new File(["node", "--print", "require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })"].execute(null, rootDir).text.trim())
bundleCommand = "export:embed"
/* Folders */
// The root of your project, i.e. where "package.json" lives. Default is '..'
// root = file("../")
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
// reactNativeDir = file("../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
// codegenDir = file("../node_modules/@react-native/codegen")
// The root of your project, i.e. where "package.json" lives. Default is '../..'
// root = file("../../")
// The folder where the react-native NPM package is. Default is ../../node_modules/react-native
// reactNativeDir = file("../../node_modules/react-native")
// The folder where the react-native Codegen package is. Default is ../../node_modules/@react-native/codegen
// codegenDir = file("../../node_modules/@react-native/codegen")
/* Variants */
// The list of variants to that are debuggable. For those we're going to
@ -57,6 +58,9 @@ react {
//
// The list of flags to pass to the Hermes compiler. By default is "-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
* 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 {
ndkVersion rootProject.ext.ndkVersion
@ -83,13 +87,13 @@ android {
buildToolsVersion rootProject.ext.buildToolsVersion
compileSdk rootProject.ext.compileSdkVersion
namespace 'com.anonymous.testapplication'
namespace 'com.mattdimegs.tones'
defaultConfig {
applicationId 'com.anonymous.testapplication'
applicationId 'com.mattdimegs.tones'
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
versionName "1.0.0"
versionName "1.0.1"
}
signingConfigs {
debug {
@ -110,6 +114,7 @@ android {
shrinkResources (findProperty('android.enableShrinkResourcesInReleaseBuilds')?.toBoolean() ?: false)
minifyEnabled enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
crunchPngs (findProperty('android.enablePngCrunchInReleaseBuilds')?.toBoolean() ?: true)
}
}
packagingOptions {
@ -117,6 +122,9 @@ android {
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`
@ -149,15 +157,15 @@ dependencies {
if (isGifEnabled) {
// 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) {
// For webp support
implementation("com.facebook.fresco:webpsupport:${reactAndroidLibs.versions.fresco.get()}")
implementation("com.facebook.fresco:webpsupport:${expoLibs.versions.fresco.get()}")
if (isWebpAnimatedEnabled) {
// 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");
applyNativeModulesAppBuildGradle(project)
apply plugin: 'com.google.gms.google-services'

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"/>
<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>

View file

@ -11,7 +11,7 @@
<data android:scheme="https"/>
</intent>
</queries>
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme">
<application android:name=".MainApplication" android:label="@string/app_name" android:icon="@mipmap/ic_launcher" android:roundIcon="@mipmap/ic_launcher_round" android:allowBackup="true" android:theme="@style/AppTheme" android:supportsRtl="true">
<meta-data android:name="expo.modules.updates.ENABLED" android:value="false"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_CHECK_ON_LAUNCH" android:value="ALWAYS"/>
<meta-data android:name="expo.modules.updates.EXPO_UPDATES_LAUNCH_WAIT_MS" android:value="0"/>
@ -25,9 +25,7 @@
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="myapp"/>
<data android:scheme="com.anonymous.testapplication"/>
</intent-filter>
</activity>
<activity android:name="com.facebook.react.devsupport.DevSettingsActivity" android:exported="false"/>
</application>
</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.Bundle
@ -15,7 +16,10 @@ class MainActivity : ReactActivity() {
// Set the theme to AppTheme BEFORE onCreate to support
// coloring the background, status bar, and navigation bar.
// This is required for expo-splash-screen.
setTheme(R.style.AppTheme);
// 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)
}

View file

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

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">
<item android:drawable="@color/splashscreen_background"/>
<item>
<bitmap android:gravity="center" android:src="@drawable/splashscreen_logo"/>
</item>
</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>
<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_status_bar_translucent" translatable="false">false</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">
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:textColor">@android:color/black</item>
<item name="android:editTextStyle">@style/ResetEditText</item>
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
<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="android:statusBarColor">#ffffff</item>
</style>
<style name="ResetEditText" parent="@android:style/Widget.EditText">
<item name="android:padding">0dp</item>
<item name="android:textColorHint">#c8c8c8</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="Theme.App.SplashScreen" parent="AppTheme">
<item name="android:windowBackground">@drawable/splashscreen</item>
<style name="Theme.App.SplashScreen" parent="Theme.SplashScreen">
<item name="windowSplashScreenBackground">@color/splashscreen_background</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/splashscreen_logo</item>
<item name="postSplashScreenTheme">@style/AppTheme</item>
</style>
</resources>

View file

@ -1,41 +1,38 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext {
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '34.0.0'
minSdkVersion = Integer.parseInt(findProperty('android.minSdkVersion') ?: '23')
compileSdkVersion = Integer.parseInt(findProperty('android.compileSdkVersion') ?: '34')
targetSdkVersion = Integer.parseInt(findProperty('android.targetSdkVersion') ?: '34')
kotlinVersion = findProperty('android.kotlinVersion') ?: '1.9.23'
ndkVersion = "26.1.10909125"
}
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')
}
repositories {
google()
mavenCentral()
}
dependencies {
classpath 'com.google.gms:google-services:4.4.1'
classpath('com.android.tools.build:gradle')
classpath('com.facebook.react:react-native-gradle-plugin')
classpath('org.jetbrains.kotlin:kotlin-gradle-plugin')
}
}
apply plugin: "com.facebook.react.rootproject"
def reactNativeAndroidDir = new File(
providers.exec {
workingDir(rootDir)
commandLine("node", "--print", "require.resolve('react-native/package.json')")
}.standardOutput.asText.get().trim(),
"../android"
)
allprojects {
repositories {
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url(new File(['node', '--print', "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), '../android'))
}
maven {
// Android JSC is installed from npm
url(new File(['node', '--print', "require.resolve('jsc-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), '../dist'))
}
google()
mavenCentral()
maven { url 'https://www.jitpack.io' }
repositories {
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url(reactNativeAndroidDir)
}
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
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Enable AAPT2 PNG crunching
android.enablePngCrunchInReleaseBuilds=true
# Use this property to specify which architecture you want to build.
# You can also override it from the CLI using
@ -35,7 +35,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
newArchEnabled=true
# Use this property to enable or disable the Hermes JS engine.
# 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.
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
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
validateDistributionUrl=true
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
# limitations under the License.
#
# SPDX-License-Identifier: Apache-2.0
#
##############################################################################
#
@ -55,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (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.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -84,7 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd "${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.
MAX_FD=maximum

186
android/gradlew.bat vendored
View file

@ -1,92 +1,94 @@
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@rem SPDX-License-Identifier: Apache-2.0
@rem
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if %ERRORLEVEL% equ 0 goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo. 1>&2
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
echo. 1>&2
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
echo location of your Java installation. 1>&2
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if %ERRORLEVEL% equ 0 goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
set EXIT_CODE=%ERRORLEVEL%
if %EXIT_CODE% equ 0 set EXIT_CODE=1
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
exit /b %EXIT_CODE%
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

View file

@ -1,18 +1,39 @@
rootProject.name = 'test-application'
dependencyResolutionManagement {
versionCatalogs {
reactAndroidLibs {
from(files(new File(["node", "--print", "require.resolve('react-native/package.json')"].execute(null, rootDir).text.trim(), "../gradle/libs.versions.toml")))
}
}
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)
def expoPluginsPath = new File(
providers.exec {
workingDir(rootDir)
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");
useExpoModules()
plugins {
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");
applyNativeModulesSettingsGradle(settings)
extensions.configure(com.facebook.react.ReactSettingsExtension) { ex ->
if (System.getenv('EXPO_USE_COMMUNITY_AUTOLINKING') == '1') {
ex.autolinkLibrariesFromCommand()
} else {
ex.autolinkLibrariesFromCommand(expoAutolinking.rnConfigCommand)
}
}
expoAutolinking.useExpoModules()
rootProject.name = 'Tones'
expoAutolinking.useExpoVersionCatalog()
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": {
"name": "Tones",
"slug": "Tones",
"slug": "tones",
"version": "1.0.1",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
@ -13,22 +13,40 @@
"backgroundColor": "#ffffff"
},
"ios": {
"entitlements": {
"aps-environment": "production"
},
"infoPlist": {
"UIBackgroundModes": ["remote-notification"]
},
"supportsTablet": true,
"bundleIdentifier": "com.anonymous.testapplication"
"bundleIdentifier": "com.mattdimegs.tones",
"googleServicesFile": "./GoogleServices/GoogleService-Info.plist"
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
},
"package": "com.anonymous.testapplication"
"package": "com.mattdimegs.tones",
"googleServicesFile": "./GoogleServices/google-services.json"
},
"plugins": [
"expo-router",
"expo-font"
"expo-font",
"expo-web-browser"
],
"experiments": {
"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
different screen densities
</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">
<ThemedText type="link">Learn more</ThemedText>
</ExternalLink>

View file

@ -1,366 +1,421 @@
import React, { useState, useRef, useEffect } from 'react';
import styled from 'styled-components';
import { useCallFeed } from '../hooks/useCallFeed';
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 React, { useState, useRef, useEffect } from "react";
import styled from "styled-components";
import { useCallFeed } from "@/hooks";
import { router } from "expo-router";
import {
PageHeader,
PageFooter,
} from '../components/generalHelpers.jsx';
import { Ionicons } from '@expo/vector-icons';
Platform,
Linking,
View,
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 ActionSheet from 'react-native-actions-sheet';
import ActionSheet from "react-native-actions-sheet";
const DepartmentActionSheet = styled(ActionSheet)``;
function toBase64Unicode(str) {
return btoa(
new TextEncoder()
.encode(str)
.reduce((data, byte) => data + String.fromCharCode(byte), "")
);
}
export default function Incidents() {
const actionSheetRef = useRef(null);
const callFeed = useCallFeed();
const actionSheetRef = useRef(null);
const callFeed = useCallFeed();
const {
departments,
callIconMap,
callDetails,
callColorSelector,
formatCallTimePast,
formatCallDateTime
} = callFeed;
const {
departments,
callIconMap,
callDetails,
callColorSelector,
formatCallTimePast,
formatCallDateTime,
} = callFeed;
const sortedAndFilteredCalls = callDetails
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
.filter((item, index, self) => {
return index === self.findIndex(i => {
return `${i?.data?.Incident?.IncID}${i?.data?.Response?.ServiceName}` === `${item?.data?.Incident?.IncID}${item?.data?.Response?.ServiceName}`
});
});
const sortedAndFilteredCalls =
callDetails
.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp))
.filter((item, index, self) => {
return (
index ===
self.findIndex((i) => {
return (
`${i?.incident?.incID}${i?.response?.serviceName}` ===
`${item?.incident?.incID}${item?.response?.serviceName}`
);
})
);
})
?.map((item) => {
return { ...item, timestamp: item?.incident?.incDate };
}) || [];
const {
departmentTypeMap,
accountDetails,
deptList,
setDeptList,
selectedDepartment,
setSelectedDepartment,
updateSelectedDepartment,
selectedDepartmentColorPicker,
} = departments;
const {
departmentTypeMap,
accountDetails,
deptList,
setDeptList,
selectedDepartment,
setSelectedDepartment,
updateSelectedDepartment,
selectedDepartmentColorPicker,
} = departments;
return (
<React.Fragment>
<PageHeader>
<View
style={{
flexDirection: 'column',
height: 80,
alignItems: 'center',
justifyContent: 'flex-end',
paddingBottom: 7
}}
>
<TouchableOpacity
style={{
borderRadius: 6,
elevation: 3,
backgroundColor: '#fff',
shadowOffset: { width: 0, height: 0 },
shadowColor: '#333',
shadowOpacity: .8,
shadowRadius: 2,
paddingHorizontal: 10,
paddingVertical: 2,
}}
onPress={() => {
actionSheetRef.current?.show();
}}
>
<Text
style={{
color: selectedDepartmentColorPicker(selectedDepartment?.type),
fontWeight: 600,
fontSize: 14
}}
>
{selectedDepartment?.deptAbv}
</Text>
</TouchableOpacity>
</View>
</PageHeader>
<ScrollView>
<StatusBar style="dark" />
<SafeAreaView />
<View style={{ flexDirection: 'column', padding: 20 }}>
{sortedAndFilteredCalls?.length ? (
sortedAndFilteredCalls?.map((callItem, index) => {
const { data: call, timestamp } = callItem;
const { Incident, Address, Response } = call;
const {
ServiceNumber,
IncDate,
IncNature,
IncNatureCode,
IncNatureCodeDesc,
Status,
} = Incident;
const {
StreetAddress,
AddressApartment,
Town,
State,
LocationName,
} = Address;
const {
ServiceName
} = Response;
const SelectedIcon = callIconMap[IncNature] || AccidentAndEmergency;
return (
<TouchableOpacity
key={`callDetails - ${timestamp}`}
style={{ padding: 2 }}
onPress={() => {
router.push({
pathname: '/call',
params: {
callDetails: btoa(JSON.stringify(call))
}
})
}}
>
<View
style={{
borderRadius: 12,
elevation: 3,
backgroundColor: '#fff',
shadowOffset: { width: 0, height: 0 },
shadowColor: callColorSelector(
IncNatureCode,
IncNature,
Status
),
shadowOpacity: 1,
shadowRadius: 5,
padding: 10,
}}
>
<View style={{ flexDirection: 'column' }}>
<View key="callDateAndTime"
style={{
paddingBottom: 6,
flexDirection: 'row',
justifyContent: 'space-between'
}}
>
<Text style={{ fontSize: 12 }}>{formatCallDateTime(IncDate)}</Text>
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
<Text style={{ fontSize: 12 }}>{formatCallTimePast(IncDate)}</Text>
{Status === 'CLOSED' ? (
<Ionicons
name="lock-closed-outline"
color='red'
style={{
shadowColor: 'black',
shadowOffset: 0,
shadowOpacity: 1,
shadowRadius: 10
}}
/>
) : null}
</View>
</View>
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between' }}>
<SelectedIcon
color={callColorSelector(
IncNatureCode,
IncNature,
Status
)}
opacity={0.3}
width={56}
height={56}
/>
<View style={{ flexDirection: 'column' }}>
{LocationName ? (
<Text
style={{
color: 'black',
fontWeight: 600,
fontSize: 16
}}
>
{`${LocationName}`}
</Text>
) : (
<View style={{ flexDirection: 'row' }}>
<Text
style={[{
color: 'black',
fontSize: 12,
fontWeight: 600
}]}
>
{`${StreetAddress}`}
</Text>
{AddressApartment ? (
<Text
style={[{
color: 'black',
fontSize: 12,
fontWeight: 600
}]}
>
{` - ${AddressApartment}`}
</Text>
) : null }
<Text
style={[{
color: 'black',
fontSize: 12,
fontWeight: 600
}]}
>
{` ${Town}, ${State}`}
</Text>
</View>
)}
<Text
style={{
color: 'black',
fontWeight: 600,
fontSize: 16
}}
>
{`${IncNature}`}
</Text>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Text
style={{
color: 'black',
fontSize: 12,
textShadowColor: callColorSelector(
IncNatureCode,
IncNature,
Status
),
textShadowRadius: 1
}}
>
{`${IncNatureCodeDesc}`}
</Text>
</View>
</View>
<View style={{ padding: 0 }}>
<View>
<Ionicons name="chevron-forward-outline" />
</View>
</View>
</View>
<View style={{ flexDirection: 'row', justifyContent: 'space-between' }}>
<Text
style={{
fontSize: 12,
}}
>
Service: {ServiceName}
</Text>
<Text
style={{
fontSize: 12,
textAlign: 'right'
}}
>
{`Incident #: ${ServiceNumber}`}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
)
})) : (
<View style={{
marginTop: '50%',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'flex-end',
}}>
<Ionicons name="warning-outline" size={100} color='orange' />
<Text style={{ fontSize: 25 }}>There are no Incidents</Text>
</View>
) }
</View>
</ScrollView>
<PageFooter>
<View
style={{
flexDirection: 'column',
height: 100,
alignItems: 'center',
justifyContent: 'flex-start',
paddingTop: 7
}}
/>
</PageFooter>
<DepartmentActionSheet
ref={actionSheetRef}
gestureEnabled
containerStyle={{
height: "50%",
width: "100%",
backgroundColor: '#ECEDEE',
}}
return (
<React.Fragment>
<PageHeader
centerHeader={
<View style={{ flex: 1, alignItems: "center" }}>
<TouchableOpacity
style={{
borderRadius: 6,
elevation: 3,
backgroundColor: "#fff",
shadowOffset: { width: 0, height: 0 },
shadowColor: "#333",
shadowOpacity: 0.8,
shadowRadius: 2,
paddingHorizontal: 10,
paddingVertical: 2,
}}
onPress={() => {
actionSheetRef.current?.show();
}}
>
<View style={{ flexDirection: 'column', padding: 20 }}>
{deptList?.map((item) => {
return (
<View style={{ padding: 2 }} key={item?.deptId}>
<TouchableOpacity
style={{
borderRadius: 6,
elevation: 3,
backgroundColor: item?.selected ? 'grey' : '#fff',
shadowOffset: { width: 1, height: 1 },
shadowColor: '#333',
shadowOpacity: 0.3,
shadowRadius: 2,
marginHorizontal: 20,
marginVertical: 6,
padding: 10
}}
onPress={() => {
actionSheetRef.current?.hide();
return updateSelectedDepartment(
selectedDepartment?.deptId,
item?.deptId
)
}}
<Text
style={{
color: selectedDepartmentColorPicker(
selectedDepartment?.type
),
fontWeight: 600,
fontSize: 14,
}}
>
{selectedDepartment?.deptAbv}
</Text>
</TouchableOpacity>
</View>
}
/>
<ScrollView>
<StatusBar style="dark" />
<SafeAreaView />
<View style={{ flexDirection: "column", padding: 20 }}>
{sortedAndFilteredCalls?.length ? (
sortedAndFilteredCalls?.map((callItem, index) => {
const { incident, address, response, timestamp } = callItem;
const {
serviceNumber,
incDate,
incNature,
incNatureCode,
incNatureCodeDesc,
status,
} = incident;
const {
streetAddress,
addressApartment,
town,
state,
locationName,
} = address;
const { serviceName } = response;
const SelectedIcon =
callIconMap[incNature] || AccidentAndEmergency;
return (
<TouchableOpacity
key={`callDetails - ${timestamp}`}
style={{ paddingBottom: 15 }}
onPress={() => {
router.push({
pathname: "/call",
params: {
callDetails: toBase64Unicode(JSON.stringify(callItem)),
},
});
}}
>
<View
style={{
borderRadius: 12,
elevation: 3,
backgroundColor: "#fff",
shadowOffset: { width: 0, height: 0 },
shadowColor: callColorSelector(
incNatureCodeDesc,
incNature,
status
),
shadowOpacity: 1,
shadowRadius: 5,
padding: 10,
}}
>
<View style={{ flexDirection: "column" }}>
<View
key="callDateAndTime"
style={{
paddingBottom: 6,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text style={{ fontSize: 12 }}>
{formatCallDateTime(incDate)}
</Text>
<View
style={{ flexDirection: "row", alignItems: "center" }}
>
<Text style={{ fontSize: 12 }}>
{formatCallTimePast(incDate)}
</Text>
{status === "CLOSED" ? (
<Ionicons
name="lock-closed-outline"
color="red"
style={{
shadowColor: "black",
shadowOffset: 0,
shadowOpacity: 1,
shadowRadius: 10,
}}
/>
) : null}
</View>
</View>
<View
style={{ flexDirection: "row", alignItems: "center" }}
>
<SelectedIcon
color={callColorSelector(
incNatureCodeDesc,
incNature,
status
)}
opacity={0.3}
width={56}
height={56}
/>
<View
style={{
flex: 1,
flexDirection: "column",
alignItems: "flex-start",
paddingHorizontal: 2,
maxWidth: "80%",
}}
>
{locationName ? (
<Text
style={{
color: "black",
fontWeight: 600,
fontSize: 16,
}}
>
{`${locationName}`}
</Text>
) : (
<View style={{ flexDirection: "row" }}>
<Text
style={[
{
color: "black",
fontSize: 12,
fontWeight: 600,
},
]}
>
{`${streetAddress?.split(",")[0]}`}
</Text>
{addressApartment ? (
<Text
style={[
{
color: "black",
fontSize: 12,
fontWeight: 600,
},
]}
>
<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>
{` - ${addressApartment}`}
</Text>
) : null}
<Text
style={[
{
color: "black",
fontSize: 12,
fontWeight: 600,
},
]}
>
{` ${town}, ${state}`}
</Text>
</View>
);
})}
</View>
</DepartmentActionSheet>
</React.Fragment>
)
}
)}
<Text
style={{
color: "black",
fontWeight: 600,
fontSize: 16,
}}
>
{`${incNature}`}
</Text>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text
style={{
color: "black",
fontSize: 12,
textShadowColor: callColorSelector(
incNatureCode,
incNature,
status
),
textShadowRadius: 1,
}}
>
{`${incNatureCodeDesc}`}
</Text>
</View>
</View>
<View style={{ padding: 0 }}>
<View>
<Ionicons name="chevron-forward-outline" />
</View>
</View>
</View>
<View
style={{
paddingTop: 5,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<Text
style={{
fontSize: 12,
}}
>
Service: {serviceName}
</Text>
<Text
style={{
fontSize: 12,
textAlign: "right",
}}
>
{`Incident #: ${serviceNumber}`}
</Text>
</View>
</View>
</View>
</TouchableOpacity>
);
})
) : (
<Text>There are no Calls</Text>
)}
</View>
</ScrollView>
<PageFooter>
<View
style={{
flexDirection: "column",
height: 100,
alignItems: "center",
justifyContent: "flex-start",
paddingTop: 7,
}}
/>
</PageFooter>
<DepartmentActionSheet
ref={actionSheetRef}
gestureEnabled
containerStyle={{
height: "50%",
width: "100%",
backgroundColor: "#ECEDEE",
}}
>
<View style={{ flexDirection: "column", padding: 20 }}>
{deptList?.map((item) => {
return (
<View style={{ padding: 2 }} key={item?.deptId}>
<TouchableOpacity
style={{
borderRadius: 6,
elevation: 3,
backgroundColor: item?.selected ? "grey" : "#fff",
shadowOffset: { width: 1, height: 1 },
shadowColor: "#333",
shadowOpacity: 0.3,
shadowRadius: 2,
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 { View, Text } from 'react-native';
import { View, Text, ScrollView, KeyboardAvoidingView, Platform } from 'react-native';
import { StatusBar } from 'expo-status-bar';
import { useFormik } from 'formik';
import { SafeAreaView } from 'react-native-safe-area-context';
@ -20,111 +20,123 @@ import {
ExtraText,
TextLinkContent,
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() {
const [hidePassword, setHidePassword] = useState(true);
const [loginButtonDisabled, setLoginButtonDisabled] = useState(true);
const [auth, setAuth] = useState(false);
const [hidePassword, setHidePassword] = useState(true);
const [loginButtonDisabled, setLoginButtonDisabled] = useState(true);
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const { expoPushToken } = useNotifications();
const formik = useFormik({
initialValues: {
number: '',
password: ''
},
onSubmit: (values) => {
values.number = values.number.replace(/[()\-\s]/g, '');
console.log(values);
setAuth(true);
},
});
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) {
const formik = useFormik({
initialValues: {
email: '',
password: ''
},
onSubmit: async (values) => {
setError('');
setLoading(true);
try {
await signInWithEmailAndPassword(auth, values.email, values.password);
router.replace('./incidents');
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}, [auth])
return (
<React.Fragment>
<PageHeader>
<View style={{ flexDirection: 'row', height: 80, alignItems: 'center' }} />
</PageHeader>
<StyledContainer>
<StatusBar style="dark" />
<SafeAreaView />
<InnerContainer>
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
<Title>Tones</Title>
<SubTitle>Account Login</SubTitle>
<StyledFormArea>
<LoginTextInput
label="Phone Number"
icon="call-outline"
placeholder="123-456-7890"
placeholderTextColor="gray"
onChangeText={formik.handleChange('number')}
onBlur={formik.handleBlur('number')}
value={formik.values.number.replace(/^(\d{3})(\d{3})(\d+)$/, "($1) $2-$3")}
keyboardType="number-pad"
maxLength={14}
/>
<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>
},
});
const formValues = formik.values;
useEffect(() => {
if (formValues) {
if (formValues.email && formValues.password) {
setLoginButtonDisabled(false);
} else {
setLoginButtonDisabled(true);
}
}
}, [formValues]);
return (
<View style={{ flex: 1 }}>
<StatusBar style="dark" />
<PageHeader>
<View style={{ flexDirection: 'row', height: 80, alignItems: 'center' }} />
</PageHeader>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={0}
>
<ScrollView keyboardShouldPersistTaps="handled" contentContainerStyle={{ flexGrow: 1 }}>
<StyledContainer>
<SafeAreaView />
<InnerContainer>
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
<Title>Tones</Title>
<SubTitle>Account Login</SubTitle>
<StyledFormArea>
{error ? <MessageBox style={{ color: 'red' }}>{error}</MessageBox> : null}
<LoginTextInput
label="Email Address"
icon="mail-outline"
placeholder="test@organization.com"
placeholderTextColor="gray"
onChangeText={formik.handleChange('email')}
onBlur={formik.handleBlur('email')}
value={formik.values.email}
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 />
<ExtraView>
<ExtraText>Don't have an account already? </ExtraText>
<Link href='./register'>
<TextLinkContent>Register</TextLinkContent>
<Text>Temporary Area:</Text>
<View>
<Link href='./incidents'>
<TextLinkContent>Incidents</TextLinkContent>
</Link>
</ExtraView>
</StyledFormArea>
<Line />
<Text>Temporary Area:</Text>
<View>
<Link href='./incidents'>
<TextLinkContent>Incidents</TextLinkContent>
</Link>
<Link href='./landing'>
<TextLinkContent>Landing</TextLinkContent>
</Link>
</View>
<Line />
</InnerContainer>
</StyledContainer>
</React.Fragment>
);
}
<Link href='./landing'>
<TextLinkContent>Landing</TextLinkContent>
</Link>
</View>
<Text>View Token: {expoPushToken}</Text>
<Line />
</InnerContainer>
</StyledContainer>
</ScrollView>
</KeyboardAvoidingView>
</View>
);
}

View file

@ -1,6 +1,10 @@
import React, { useState, useEffect } from 'react';
import * as Notifications from 'expo-notifications';
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 { useFormik } from 'formik';
import { SafeAreaView } from 'react-native-safe-area-context';
@ -19,14 +23,16 @@ import {
MessageBox,
LoginTextInput,
RegisterDropdownInput,
} from '../components/generalHelpers.jsx';
formatPhoneNumber
} from '@/components/generalHelpers.jsx';
export default function Register() {
const [providerDropdownOpen, setProviderDropdownOpen] = useState(false);
const [hidePassword, setHidePassword] = useState(true);
const [registerButtonDisabled, setRegisterButtonDisabled] = useState(true);
const [error, setError] = useState('');
const [loading, setLoading] = useState(false);
const { user } = useAuth();
const formik = useFormik({
initialValues: {
@ -34,13 +40,64 @@ export default function Register() {
lastName: '',
number: '',
provider: '',
email: '',
password: '',
passwordConfirmation: ''
email: '',
password: '',
passwordConfirmation: ''
},
onSubmit: (values) => {
values.number = values.number.replace(/[()\-\s]/g, '');
console.log(values);
onSubmit: async (values) => {
setError('');
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(() => {
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)) {
setRegisterButtonDisabled(false);
} else {
@ -62,106 +120,121 @@ export default function Register() {
}
}, [formValues])
useEffect(() => {
if (user) {
router.replace('./incidents');
}
}, [user]);
return (
<View>
<PageHeader>
<View style={{ flexDirection: 'row', height: 80, alignItems: 'flex-end' }}>
<View style={{ flex: 1 }}>
<StatusBar style="dark" />
<PageHeader
leftHeader={
<TouchableOpacity onPress={router.back} style={{ flexDirection: 'row', alignItems: 'center', paddingBottom: 5 }}>
<Ionicons name="chevron-back-outline" size={22} color="red" style={{ paddingLeft: 20 }} />
<Text style={{ color: 'red', fontWeight: 600 }}>Back to Login</Text>
</TouchableOpacity>
</View>
</PageHeader>
<ScrollView>
<StyledContainer>
<StatusBar style="dark" />
</TouchableOpacity>}
/>
<KeyboardAvoidingView
style={{ flex: 1 }}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
keyboardVerticalOffset={0}
>
<ScrollView keyboardShouldPersistTaps="handled" contentContainerStyle={{ flexGrow: 1 }}>
<SafeAreaView />
<InnerContainer>
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
<Title>Tones</Title>
<SubTitle>Account Register</SubTitle>
<StyledFormArea>
<LoginTextInput
label="First Name"
icon="person-outline"
placeholder="Bud"
placeholderTextColor="gray"
onChangeText={formik.handleChange('firstName')}
onBlur={formik.handleBlur('firstName')}
value={formik.values.firstName}
/>
<LoginTextInput
label="Last Name"
icon="people-outline"
placeholder="Doble"
placeholderTextColor="gray"
onChangeText={formik.handleChange('lastName')}
onBlur={formik.handleBlur('lastName')}
value={formik.values.lastName}
/>
<LoginTextInput
label="Phone Number"
icon="call-outline"
placeholder="123-456-7890"
placeholderTextColor="gray"
onChangeText={formik.handleChange('number')}
onBlur={formik.handleBlur('number')}
value={formik.values.number.replace(/^(\d{3})(\d{3})(\d+)$/, "($1) $2-$3")}
keyboardType="number-pad"
maxLength={14}
/>
<RegisterDropdownInput
label="Cellular Provider"
isOpen={providerDropdownOpen}
setOpen={setProviderDropdownOpen}
selectedValue={formik.values.provider}
onValueChange={formik.handleChange('provider')}
menu={providerMenu}
/>
<LoginTextInput
label="Email Address"
icon="mail-outline"
placeholder="test@organization.com"
placeholderTextColor="gray"
onChangeText={formik.handleChange('email')}
onBlur={formik.handleBlur('email')}
value={formik.values.email}
keyboardType="email-address"
/>
<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}
/>
<LoginTextInput
label="Confirm Password"
icon="shield-checkmark-outline"
placeholder="* * * * * *"
placeholderTextColor="gray"
onChangeText={formik.handleChange('passwordConfirmation')}
onBlur={formik.handleBlur('passwordConfirmation')}
value={formik.values.passwordConfirmation}
secureTextEntry={hidePassword}
isPassword
hidePassword={hidePassword}
setHidePassword={setHidePassword}
/>
<MessageBox>...</MessageBox>
<StyledButton onPress={formik.handleSubmit} style={registerButtonDisabled ? {backgroundColor: 'grey'} : {}}>
<ButtonText>Register</ButtonText>
</StyledButton>
</StyledFormArea>
</InnerContainer>
</StyledContainer>
</ScrollView>
<StyledContainer>
<InnerContainer>
<PageImage resizeMode="cover" source={require('./../assets/images/tones-logo.png')} />
{error ? <MessageBox style={{ color: 'red' }}>{error}</MessageBox> : null}
<Title>Tones</Title>
<SubTitle>Account Register</SubTitle>
<StyledFormArea>
<LoginTextInput
label="First Name"
icon="person-outline"
placeholder="Bud"
placeholderTextColor="gray"
onChangeText={formik.handleChange('firstName')}
onBlur={formik.handleBlur('firstName')}
value={formik.values.firstName}
/>
<LoginTextInput
label="Last Name"
icon="people-outline"
placeholder="Doble"
placeholderTextColor="gray"
onChangeText={formik.handleChange('lastName')}
onBlur={formik.handleBlur('lastName')}
value={formik.values.lastName}
/>
<LoginTextInput
label="Phone Number"
icon="call-outline"
placeholder="123-456-7890"
placeholderTextColor="gray"
onChangeText={formik.handleChange('number')}
onBlur={formik.handleBlur('number')}
value={formatPhoneNumber(formik.values.number)}
autoComplete='tel'
keyboardType='phone-pad'
maxLength={14}
/>
<RegisterDropdownInput
label="Cellular Provider"
isOpen={providerDropdownOpen}
setOpen={setProviderDropdownOpen}
selectedValue={formik.values.provider}
onValueChange={formik.handleChange('provider')}
menu={providerMenu}
/>
<LoginTextInput
label="Email Address"
icon="mail-outline"
placeholder="test@organization.com"
placeholderTextColor="gray"
onChangeText={formik.handleChange('email')}
onBlur={formik.handleBlur('email')}
value={formik.values.email}
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}
/>
<LoginTextInput
label="Confirm Password"
icon="shield-checkmark-outline"
placeholder="* * * * * *"
placeholderTextColor="gray"
onChangeText={formik.handleChange('passwordConfirmation')}
onBlur={formik.handleBlur('passwordConfirmation')}
value={formik.values.passwordConfirmation}
secureTextEntry={hidePassword}
isPassword
hidePassword={hidePassword}
setHidePassword={setHidePassword}
/>
<MessageBox>...</MessageBox>
<StyledButton onPress={formik.handleSubmit} style={registerButtonDisabled ? { backgroundColor: 'grey' } : {}}>
<ButtonText>Register</ButtonText>
</StyledButton>
</StyledFormArea>
</InnerContainer>
</StyledContainer>
</ScrollView>
</KeyboardAvoidingView>
</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 { StyleSheet, TouchableOpacity, useColorScheme } from 'react-native';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import { ThemedText } from '../components/ThemedText';
import { ThemedView } from '../components/ThemedView';
import { Colors } from '@/constants/Colors';
export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,8 @@
import React, { useState } from 'react';
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 { Row } from './Row';
import { Container } from './Container';
export const StyledContainer = styled.View`
@ -44,7 +45,7 @@ export const StyledTextInput = styled.TextInput`
margin-bottom: 10px;
`;
export const StyledInputLabel = styled.Text`
export const StyledInputLabel = styled.Text`
font-size: 13px;
text-align: left;
`;
@ -204,17 +205,39 @@ const providerConversion = {
}
export const PageHeader = ({
children
leftHeader = <View style={{ flex: 1 }} />,
centerHeader = <View style={{ flex: 1 }} />,
rightHeader = <View style={{ flex: 1 }} />
}) => {
return (
<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>
)
}
export const PageFooter = ({
children
children
}) => {
return (
<View style={{ position: 'fixed', top: 0, backgroundColor: '#ECEDEE', zIndex: 1 }}>
@ -224,11 +247,11 @@ export const PageFooter = ({
}
export const LoginTextInput = ({
label,
icon,
isPassword = false,
hidePassword = true,
setHidePassword = (boolean) => {},
label,
icon,
isPassword = false,
hidePassword = true,
setHidePassword = (boolean) => { },
...props
}) => {
return (
@ -239,7 +262,7 @@ export const LoginTextInput = ({
<StyledInputLabel>{label}</StyledInputLabel>
<StyledTextInput {...props} />
{isPassword ? (
<RightIcon onPress={() => {setHidePassword(!hidePassword)}}>
<RightIcon onPress={() => { setHidePassword(!hidePassword) }}>
<Ionicons name={hidePassword ? 'eye-off-outline' : 'eye-outline'} size={30} color="gray" />
</RightIcon>
) : null}
@ -249,91 +272,108 @@ export const LoginTextInput = ({
export const RegisterDropdownInput = ({
label,
isOpen,
setOpen,
isOpen: _isOpen,
setOpen: _setOpen,
selectedValue,
onValueChange,
menu
}) => {
const [modalVisible, setModalVisible] = useState(false);
return (
<Container>
<StyledInputLabel>{label}</StyledInputLabel>
<TouchableOpacity activeOpacity={0.8} key={`${menu.menuName}2`}
style={{
<TouchableOpacity
activeOpacity={0.8}
style={{
backgroundColor: '#E5E7EB',
marginTop: 3,
marginBottom: 10,
borderRadius: '5px',
borderRadius: 5,
flexDirection: 'row',
alignItems: 'center',
minHeight: 60,
paddingHorizontal: 15,
}}
onPress={() => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
LayoutAnimation.configureNext(LayoutAnimation.create(200, 'easeInEaseOut', 'opacity'));
isOpen ? setOpen(false) : setOpen(true);
onPress={() => setModalVisible(true)}
>
<Ionicons
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={{
paddingHorizontal: 16,
paddingVertical: 16 / 1.2,
}}>
<Ionicons
name={menu.iconName}
size={30}
color={menu.iconColor}
/>
<Text style={{
fontSize: 16,
paddingHorizontal: 16
}}>
{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);
<Pressable
style={{ flex: 1, paddingHorizontal: 15, alignItems: 'flex-end' }}
onPress={() => setModalVisible(false)}
>
<Text style={{ color: 'red', fontWeight: 600 }}>
Close
</Text>
</Pressable>
<Picker
selectedValue={selectedValue}
onValueChange={(value) => {
onValueChange(value);
}}
>
<Ionicons
name={isOpen ? "chevron-up-outline" : "chevron-down-outline"}
size={30}
color="gray"
/>
</DropdownArrow>
</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>
{menu.dropdownList.map((item) => (
<Picker.Item color="black" label={item.label} value={item.value} key={item.value} />
))}
</Picker>
</View>
</Modal>
</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})`);
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 = () => {
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 { 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 { useDepartments } from '../useDepartments';
import { C, Cardiology, Cpr, FourByFour } from "healthicons-react-native/dist/outline";
import { useWebSocketContext } from '../useWebSocketContext';
import React, { useState, useEffect } from "react";
import { useDepartments } from "../useDepartments";
import {
Cardiology,
Cpr,
FourByFour,
} from "healthicons-react-native/dist/outline";
import { postgresServices } from "@/contexts";
import { useWebSocketContext } from "../useWebSocketContext";
const callIconMap = {
"CHEST PAIN|HEART PROBLEMS": Cardiology,
"CARDIAC ARREST|DEATH": Cpr,
"MOTOR VEHICLE COLLISION (MVC)": FourByFour,
}
"CHEST PAIN|HEART PROBLEMS": Cardiology,
"CARDIAC ARREST|DEATH": Cpr,
"MOTOR VEHICLE COLLISION (MVC)": FourByFour,
};
// Squares
@ -37,200 +42,116 @@ const callIconMap = {
// Bacteria - Sick
// 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 initDate = new Date(callValue);
const currentTime = new Date();
const initDate = new Date(callValue);
const currentTime = new Date();
const timeDifference = currentTime - initDate;
const timeDifference = currentTime - initDate;
const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
const hours = Math.floor((timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeDifference % (1000 * 60)) / 1000);
if (days && days !== 0) {
return `${days} day${days === 1 ? '' : 's'} ago`;
} else if (hours && hours !== 0) {
return `${hours} hour${hours === 1 ? '' : 's'} ago`;
} else if (minutes && minutes !== 0) {
return `${minutes} minute${minutes === 1 ? '' : 's'} ago`;
} else if (seconds && seconds !== 0) {
return `${seconds} second${seconds === 1 ? '' : 's'} ago`;
}
return `Unknown Time Past`;
}
const days = Math.floor(timeDifference / (1000 * 60 * 60 * 24));
const hours = Math.floor(
(timeDifference % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
);
const minutes = Math.floor((timeDifference % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((timeDifference % (1000 * 60)) / 1000);
if (days && days !== 0) {
return `${days} day${days === 1 ? "" : "s"} ago`;
} else if (hours && hours !== 0) {
return `${hours} hour${hours === 1 ? "" : "s"} ago`;
} else if (minutes && minutes !== 0) {
return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
} else if (seconds && seconds !== 0) {
return `${seconds} second${seconds === 1 ? "" : "s"} ago`;
}
return `Unknown Time Past`;
};
const formatCallDateTime = (callValue) => {
const initDate = new Date(callValue);
if (initDate) {
const formattedDate = `${initDate.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: 'numeric',
})}`;
const hours = initDate.getHours().toString().padStart(2, '0');
const minutes = initDate.getMinutes().toString().padStart(2, '0');
const formattedTime = `${hours}:${minutes}`;
const initDate = new Date(callValue);
if (initDate) {
const formattedDate = `${initDate.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
year: "numeric",
})}`;
const hours = initDate.getHours().toString().padStart(2, "0");
const minutes = initDate.getMinutes().toString().padStart(2, "0");
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 = () => {
const departments = useDepartments();
const { lastMessage } = useWebSocketContext();
const [allCalls, setAllCalls] = useState([]);
const { CallThemes } = departments?.accountDetails;
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
useEffect(() => {
if (lastMessage && !init) {
const parsedMessage = JSON?.parse(lastMessage);
if (parsedMessage?.data) {
setAllCalls([...allCalls, parsedMessage]);
}
}
}
}, [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 { View, Text, TouchableOpacity } from 'react-native';
const departmentTypeMap = {
EMS: 'Medical Services',
@ -10,27 +9,47 @@ const departmentTypeMap = {
const accountDetails = {
"CallThemes" : {
"CriticalCallList": [
"CARDIAC ARREST|DEATH",
"CARDIAC ARREST"
],
"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": [
"ALS-STANDARD",
"BLS-PRIORITY"
"BLS-PRIORITY",
"EMS BLS Priority Response",
"EMS BLS Response Fall Injury"
],
"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": [
{
deptId: 0,
deptId: 1,
dept: 'Darien EMS',
addDepts: [
'Darien EMS Supv'
],
deptAbv: 'DEMS',
rank: 'Assistant Director',
rankAbv: 'Asst. Director',
type: 'EMS',
primary: true,
selected: true,
@ -39,9 +58,11 @@ const accountDetails = {
hasVolunteer: true,
},
{
deptId: 1,
deptId: 2,
dept: 'Noroton Fire Department',
deptAbv: 'NFD',
rank: 'Lieutenant',
rankAbv: 'Lt.',
type: 'Fire',
primary: false,
selected: false,
@ -50,9 +71,11 @@ const accountDetails = {
hasVolunteer: true,
},
{
deptId: 2,
dept: 'Stamford Fire Department',
deptId: 3,
dept: 'Stamford Fire Rescue',
deptAbv: 'SFD',
rank: 'Paramedic',
rankAbv: 'EMT-P',
type: 'Rescue',
primary: 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 { WebSocketContext } from '../../contexts/WebSocketContext';
import { WebSocketContext } from '../../contexts';
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['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',
:deterministic_uuids => false
prepare_react_native_project!
target 'testapplication' do
target 'Tones' do
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 => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS']
@ -47,12 +63,4 @@ target 'testapplication' do
end
end
end
post_integrate do |installer|
begin
expo_patch_react_imports!(installer)
rescue => e
Pod::UI.warn e
end
end
end

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,5 @@
{
"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 */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
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 */; };
640C5CE36A784C1E86D867C7 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2358EABEB0CA4428951E7149 /* noop-file.swift */; };
96905EF65AED1B983A6B3ABC /* libPods-testapplication.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-testapplication.a */; };
93A35CBBC1C2DF84FE39E633 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EA8B81AA605F084BFB23750B /* PrivacyInfo.xcprivacy */; };
96905EF65AED1B983A6B3ABC /* libPods-Tones.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Tones.a */; };
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
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 */
/* Begin PBXFileReference section */
13B07F961A680F5B00A75B9A /* testapplication.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = testapplication.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = testapplication/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = testapplication/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = testapplication/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapplication/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapplication/main.m; 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>"; };
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-testapplication.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-testapplication.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>"; };
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>"; };
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>"; };
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>"; };
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>"; };
13B07F961A680F5B00A75B9A /* Tones.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Tones.app; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = Tones/AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = Tones/AppDelegate.mm; sourceTree = "<group>"; };
13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = Tones/Images.xcassets; sourceTree = "<group>"; };
13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Tones/Info.plist; sourceTree = "<group>"; };
13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = Tones/main.m; sourceTree = "<group>"; };
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Tones.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Tones.a"; sourceTree = BUILT_PRODUCTS_DIR; };
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-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>"; };
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = Tones/SplashScreen.storyboard; 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; };
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 */
/* Begin PBXFrameworksBuildPhase section */
@ -42,14 +42,14 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
96905EF65AED1B983A6B3ABC /* libPods-testapplication.a in Frameworks */,
96905EF65AED1B983A6B3ABC /* libPods-Tones.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
13B07FAE1A68108700A75B9A /* testapplication */ = {
13B07FAE1A68108700A75B9A /* Tones */ = {
isa = PBXGroup;
children = (
BB2F792B24A3F905000567C9 /* Supporting */,
@ -59,18 +59,18 @@
13B07FB61A68108700A75B9A /* Info.plist */,
13B07FB71A68108700A75B9A /* main.m */,
AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
2358EABEB0CA4428951E7149 /* noop-file.swift */,
96FAC8CF002144E68DCEBFD0 /* testapplication-Bridging-Header.h */,
A9DA6C7B26A64A23DFDE186E /* PrivacyInfo.xcprivacy */,
1350D069573E4AEBBF8BA6A3 /* noop-file.swift */,
F8CAF977DAD74AAA91FDDD99 /* Tones-Bridging-Header.h */,
EA8B81AA605F084BFB23750B /* PrivacyInfo.xcprivacy */,
);
name = testapplication;
name = Tones;
sourceTree = "<group>";
};
2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
isa = PBXGroup;
children = (
ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-testapplication.a */,
58EEBF8E8E6FB1BC6CAF49B5 /* libPods-Tones.a */,
);
name = Frameworks;
sourceTree = "<group>";
@ -85,7 +85,7 @@
83CBB9F61A601CBA00E9B192 = {
isa = PBXGroup;
children = (
13B07FAE1A68108700A75B9A /* testapplication */,
13B07FAE1A68108700A75B9A /* Tones */,
832341AE1AAA6A7D00B99B32 /* Libraries */,
83CBBA001A601CBA00E9B192 /* Products */,
2D16E6871FA4F8E400B85C8A /* Frameworks */,
@ -100,17 +100,17 @@
83CBBA001A601CBA00E9B192 /* Products */ = {
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* testapplication.app */,
13B07F961A680F5B00A75B9A /* Tones.app */,
);
name = Products;
sourceTree = "<group>";
};
92DBD88DE9BF7D494EA9DA96 /* testapplication */ = {
92DBD88DE9BF7D494EA9DA96 /* Tones */ = {
isa = PBXGroup;
children = (
FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
);
name = testapplication;
name = Tones;
sourceTree = "<group>";
};
BB2F792B24A3F905000567C9 /* Supporting */ = {
@ -119,14 +119,14 @@
BB2F792C24A3F905000567C9 /* Expo.plist */,
);
name = Supporting;
path = testapplication/Supporting;
path = Tones/Supporting;
sourceTree = "<group>";
};
D65327D7A22EEC0BE12398D9 /* Pods */ = {
isa = PBXGroup;
children = (
6C2E3173556A471DD304B334 /* Pods-testapplication.debug.xcconfig */,
7A4D352CD337FB3A3BF06240 /* Pods-testapplication.release.xcconfig */,
6C2E3173556A471DD304B334 /* Pods-Tones.debug.xcconfig */,
7A4D352CD337FB3A3BF06240 /* Pods-Tones.release.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
@ -134,7 +134,7 @@
D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
isa = PBXGroup;
children = (
92DBD88DE9BF7D494EA9DA96 /* testapplication */,
92DBD88DE9BF7D494EA9DA96 /* Tones */,
);
name = ExpoModulesProviders;
sourceTree = "<group>";
@ -142,26 +142,26 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
13B07F861A680F5B00A75B9A /* testapplication */ = {
13B07F861A680F5B00A75B9A /* Tones */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "testapplication" */;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Tones" */;
buildPhases = (
08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
7E5AAFC44944C4D49F90AE74 /* [Expo] Configure project */,
BBDF11722E0998CCEE7FC139 /* [Expo] Configure project */,
13B07F871A680F5B00A75B9A /* Sources */,
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
6CD366C487D1C4076EEFC1E9 /* [CP] Embed Pods Frameworks */,
5F318AA4CE51F8E175CC6D0A /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = testapplication;
productName = testapplication;
productReference = 13B07F961A680F5B00A75B9A /* testapplication.app */;
name = Tones;
productName = Tones;
productReference = 13B07F961A680F5B00A75B9A /* Tones.app */;
productType = "com.apple.product-type.application";
};
/* 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";
developmentRegion = en;
hasScannedForEncodings = 0;
@ -190,7 +190,7 @@
projectDirPath = "";
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* testapplication */,
13B07F861A680F5B00A75B9A /* Tones */,
);
};
/* End PBXProject section */
@ -203,7 +203,7 @@
BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
FF358844A8FA907CDEC7216D /* PrivacyInfo.xcprivacy in Resources */,
93A35CBBC1C2DF84FE39E633 /* PrivacyInfo.xcprivacy in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -240,20 +240,20 @@
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-testapplication-checkManifestLockResult.txt",
"$(DERIVED_FILE_DIR)/Pods-Tones-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
6CD366C487D1C4076EEFC1E9 /* [CP] Embed Pods Frameworks */ = {
5F318AA4CE51F8E175CC6D0A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
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",
);
name = "[CP] Embed Pods Frameworks";
@ -262,10 +262,50 @@
);
runOnlyForDeploymentPostprocessing = 0;
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;
};
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;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
@ -282,33 +322,7 @@
);
runOnlyForDeploymentPostprocessing = 0;
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";
};
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;
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";
};
/* End PBXShellScriptBuildPhase section */
@ -320,7 +334,7 @@
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
13B07FC11A68108700A75B9A /* main.m in Sources */,
B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
640C5CE36A784C1E86D867C7 /* noop-file.swift in Sources */,
3BB8D1083F3D4AAFA506B09F /* noop-file.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -329,19 +343,19 @@
/* Begin XCBuildConfiguration section */
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-testapplication.debug.xcconfig */;
baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-Tones.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = testapplication/testapplication.entitlements;
CODE_SIGN_ENTITLEMENTS = Tones/Tones.entitlements;
CURRENT_PROJECT_VERSION = 1;
ENABLE_BITCODE = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
"FB_SONARKIT_ENABLED=1",
);
INFOPLIST_FILE = testapplication/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
INFOPLIST_FILE = Tones/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
@ -350,9 +364,9 @@
"-lc++",
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.testapplication;
PRODUCT_NAME = testapplication;
SWIFT_OBJC_BRIDGING_HEADER = "testapplication/testapplication-Bridging-Header.h";
PRODUCT_BUNDLE_IDENTIFIER = com.mattdimegs.tones;
PRODUCT_NAME = Tones;
SWIFT_OBJC_BRIDGING_HEADER = "Tones/Tones-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
@ -362,14 +376,14 @@
};
13B07F951A680F5B00A75B9A /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-testapplication.release.xcconfig */;
baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-Tones.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = testapplication/testapplication.entitlements;
CODE_SIGN_ENTITLEMENTS = Tones/Tones.entitlements;
CURRENT_PROJECT_VERSION = 1;
INFOPLIST_FILE = testapplication/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
INFOPLIST_FILE = Tones/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MARKETING_VERSION = 1.0;
OTHER_LDFLAGS = (
@ -378,9 +392,9 @@
"-lc++",
);
OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
PRODUCT_BUNDLE_IDENTIFIER = com.anonymous.testapplication;
PRODUCT_NAME = testapplication;
SWIFT_OBJC_BRIDGING_HEADER = "testapplication/testapplication-Bridging-Header.h";
PRODUCT_BUNDLE_IDENTIFIER = com.mattdimegs.tones;
PRODUCT_NAME = Tones;
SWIFT_OBJC_BRIDGING_HEADER = "Tones/Tones-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VERSIONING_SYSTEM = "apple-generic";
@ -391,7 +405,6 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "";
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
@ -418,7 +431,6 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
CXX = "";
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@ -436,9 +448,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD = "";
LDPLUSPLUS = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = YES;
@ -449,6 +459,7 @@
);
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
USE_HERMES = true;
};
name = Debug;
@ -457,7 +468,6 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CC = "";
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
CLANG_CXX_LIBRARY = "libc++";
@ -484,7 +494,6 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
CXX = "";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@ -495,9 +504,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 13.4;
LD = "";
LDPLUSPLUS = "";
IPHONEOS_DEPLOYMENT_TARGET = 15.1;
LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
MTL_ENABLE_DEBUG_INFO = NO;
@ -515,7 +522,7 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "testapplication" */ = {
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "Tones" */ = {
isa = XCConfigurationList;
buildConfigurations = (
13B07F941A680F5B00A75B9A /* Debug */,
@ -524,7 +531,7 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "testapplication" */ = {
83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "Tones" */ = {
isa = XCConfigurationList;
buildConfigurations = (
83CBBA201A601CBA00E9B192 /* Debug */,

View file

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

View file

@ -2,7 +2,7 @@
<Workspace
version = "1.0">
<FileRef
location = "group:testapplication.xcodeproj">
location = "group:Tones.xcodeproj">
</FileRef>
<FileRef
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",
"filename": "image@2x.png",
"scale": "2x"
},
{
"idiom": "universal",
"filename": "image@3x.png",
"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>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>test-application</string>
<string>Tones</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@ -19,7 +19,7 @@
<key>CFBundlePackageType</key>
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
<key>CFBundleShortVersionString</key>
<string>1.0.0</string>
<string>1.0.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleURLTypes</key>
@ -28,12 +28,14 @@
<key>CFBundleURLSchemes</key>
<array>
<string>myapp</string>
<string>com.anonymous.testapplication</string>
<string>com.mattdimegs.tones</string>
</array>
</dict>
</array>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSMinimumSystemVersion</key>
<string>12.0</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>

View file

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

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