diff --git a/README.md b/README.md
index 4e689e59..675b8e8c 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
![banner](/client/assets/images/github/Echo_banner_small2.png)
-Echo is a new chat messaging app aimed at allowing you to talk to strangers *(or as we like to say, friends who haven't met 😆)* within a radius around you. This allows for more time/location relevant chats as well as allowing users to connect with the people who are closer to them. Unlike many chat apps, our app aims to keep interactions in real time and doesn't save chats/information after you are done chatting! Just like real conversations, we believe the best socializing happens on the spot!
+Echo is a new chat messaging app aimed at allowing you to talk to strangers _(or as we like to say, friends who haven't met 😆)_ within a radius around you. This allows for more time/location relevant chats as well as allowing users to connect with the people who are closer to them. Unlike many chat apps, our app aims to keep interactions in real time and doesn't save chats/information after you are done chatting! Just like real conversations, we believe the best socializing happens on the spot!
## Overview
@@ -10,6 +10,11 @@ This repository holds the all of the code that our Echo app utilizes (pretty coo
If you are interested in contributing to the UI/UX design of our app, please see our Figma design [here](https://www.figma.com/file/2mvddKeA4XMODdCidYkDid/Proximity-Chat-App?type=design&node-id=0%3A1&mode=design&t=V5A9MVRhlmdxGH0M-1). Improvements here are always welcome.
+## Useful Links
+
+- [Documentation](https://osc-proximity-documentation.vercel.app/) 📖
+- [Figma Design](https://www.figma.com/file/2mvddKeA4XMODdCidYkDid/Proximity-Chat-App?type=design&node-id=0%3A1&mode=design&t=V5A9MVRhlmdxGH0M-1) 🖌️
+
## Installation
Unfortunately, as of Spring Semester of 2024, the app is still under development and you cannot download justttt yet. However, we promise it will be out soon! 🙏
@@ -17,11 +22,13 @@ Unfortunately, as of Spring Semester of 2024, the app is still under development
Want to speed up the development? Join our team by follow the instructions in [contributing](#contributing)!
## Contributing
+
As an open source project, developers of all walks of life are encouraged to pick up issues and speed up the development of the app! However, it is probably a good idea to get yourself used to the app's structure!
If you want to set up the app for development follow the steps in the [documentation](https://osc-proximity-documentation.vercel.app/). 📖
## About Us
+
The Echo app team was founded in Fall 2023 by 💻 [@h1dvp](https://github.com/h1divp) + ⚡ [@doigdaniels](https://github.com/doigdaniels) + 🦆 [@AlexanderWangY](https://github.com/AlexanderWangY).
Our team consists of a handful of dedicated and talented developers from the University of Florida. We started out of and currently reside within UF's Open Source Club. We are always on the lookout for more developers trying to get their hands dirty on a real project! We would love to have **YOU** join our team! ❤️
@@ -31,6 +38,7 @@ Our team consists of a handful of dedicated and talented developers from the Uni
This project is licensed under the [GNU General Public License v3.0](LICENSE) (GPL-3.0). If you intend to make a fork or a different distribution of our work, please remember to retain the same license. Other than that, happy hacking!
## Acknowledgments
+
A big thanks to [Open Source Club @ UF](https://github.com/ufosc) for hosting the development of this app!
And last but not least, **THANK YOU**! Your contributions and commitment make the app what it is! ❤️🥳
diff --git a/client/.eslintrc.js b/client/.eslintrc.js
index dce112ab..fe175112 100644
--- a/client/.eslintrc.js
+++ b/client/.eslintrc.js
@@ -1,4 +1,14 @@
// https://docs.expo.dev/guides/using-eslint/
module.exports = {
- extends: 'expo',
+ extends: "expo",
+ rules: {
+ "import/no-unresolved": [2, { ignore: ["^@env"] }],
+ },
+ settings: {
+ "import/resolver": {
+ node: {
+ extensions: [".js", ".jsx", ".ts", ".tsx"],
+ },
+ },
+ },
};
diff --git a/client/.gitignore b/client/.gitignore
index d70c2ec8..661619fb 100644
--- a/client/.gitignore
+++ b/client/.gitignore
@@ -35,4 +35,4 @@ yarn-error.*
# typescript
*.tsbuildinfo
-.prettierrc
\ No newline at end of file
+.prettierrc
diff --git a/client/app/components/settings/TextInputs.tsx b/client/app/components/settings/TextInputs.tsx
new file mode 100644
index 00000000..62d43c85
--- /dev/null
+++ b/client/app/components/settings/TextInputs.tsx
@@ -0,0 +1,171 @@
+import React, {useState} from "react";
+import {
+ Button,
+ Modal,
+ SafeAreaView,
+ Text,
+ TextInput,
+ View,
+ StyleSheet,
+ Pressable,
+ TouchableWithoutFeedback
+} from "react-native";
+
+type GenericTextInputProps = {
+ defaultValue: string;
+ isVisible: boolean;
+ visibleSetter: Function;
+ outputSetter: Function;
+ headerText: string;
+ errorMessage: string;
+ maxLength: number;
+ inputValidator: Function;
+
+}
+
+const GenericTextInput = ({
+ defaultValue,
+ isVisible,
+ visibleSetter,
+ outputSetter,
+ headerText,
+ errorMessage,
+ maxLength,
+ inputValidator,
+}: GenericTextInputProps) => {
+ const[textInput, setTextInput] = useState('');
+ const[error, setError] = useState('');
+
+ return(
+ {
+ visibleSetter(false);
+ setError('');
+ }}
+ >
+ {
+ visibleSetter(false);
+ setError('');
+ }}>
+
+
+
+ {headerText}
+ {error}
+ {setTextInput(text); setError('');}}
+ />
+
+
+
+
+
+
+
+ )};
+
+type InputProps = {
+ defaultValue: string;
+ isVisible: boolean;
+ visibleSetter: Function;
+ outputSetter: Function;
+};
+
+export const DisplayNameInput = ({
+ defaultValue,
+ isVisible,
+ visibleSetter,
+ outputSetter,
+}: InputProps) => GenericTextInput({
+ defaultValue: defaultValue,
+ isVisible: isVisible,
+ visibleSetter: visibleSetter,
+ outputSetter: outputSetter,
+ headerText: "Edit Display Name",
+ errorMessage: "Please enter a display name.",
+ maxLength: 12,
+ inputValidator: (input: string) => input.length > 0,
+});
+
+export const ColorInput = ({
+ defaultValue,
+ isVisible,
+ visibleSetter,
+ outputSetter,
+}: InputProps) => GenericTextInput({
+ defaultValue: defaultValue,
+ isVisible: isVisible,
+ visibleSetter: visibleSetter,
+ outputSetter: outputSetter,
+ headerText: "Edit Profile Color",
+ errorMessage: "Please enter a valid hex code.",
+ maxLength: 7,
+ inputValidator: (input: string) => (/^#[0-9A-Fa-f]{6}|#[0-9A-Fa-f]{3}$/).exec(input),
+});
+
+const styles = StyleSheet.create({
+ inputHeader: {
+ fontSize: 14,
+ fontWeight: "600",
+ color: "#a7a7a7",
+ textTransform: "uppercase",
+ letterSpacing: 1.2,
+ },
+ centeredView: {
+ height: "100%",
+ justifyContent: "flex-start",
+ alignItems: "center",
+ backgroundColor: "rgba(54, 54, 54, 0.5)",
+ },
+ inputModal: {
+ alignItems: "center",
+ backgroundColor: "#cccccc",
+ marginTop: "50%",
+ width: "70%",
+ borderRadius: 20,
+ padding: "5%",
+ shadowColor: "black",
+ shadowOffset: {
+ width: 0,
+ height: 2,
+ },
+ shadowOpacity: 0.25,
+ shadowRadius: 4,
+ elevation: 5,
+ },
+ buttonContainer: {
+ paddingHorizontal: "5%",
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ width: "90%",
+ },
+ textInput: {
+ marginVertical: "5%",
+ width: "75%",
+ textAlign: "center",
+ borderBottomWidth: 2,
+ fontSize: 20,
+ },
+});
\ No newline at end of file
diff --git a/client/app/configs/firebaseConfig.ts b/client/app/configs/firebaseConfig.ts
index e2a33e63..ed93ff38 100644
--- a/client/app/configs/firebaseConfig.ts
+++ b/client/app/configs/firebaseConfig.ts
@@ -10,7 +10,7 @@ import {
const firebaseConfig = {
apiKey: API_KEY || "Mock-Key",
- authDomain: AUTH_DOMAIN
+ authDomain: AUTH_DOMAIN,
};
let app;
diff --git a/client/app/contexts/UserContext.tsx b/client/app/contexts/UserContext.tsx
index 34294cd5..9dcaf92b 100644
--- a/client/app/contexts/UserContext.tsx
+++ b/client/app/contexts/UserContext.tsx
@@ -1,6 +1,7 @@
-import React, { createContext, useContext, useState } from "react";
+import React, { createContext, useContext, useEffect, useState } from "react";
import { UserType } from "../types/User";
+import { generateName } from "@app/utils/scripts";
const UserContext = createContext(null);
@@ -17,5 +18,9 @@ export const UserProvider = ({ children }: { children: React.ReactNode }) => {
},
});
+ useEffect(() => {
+ user.displayName = generateName()
+ }, [])
+
return {children};
};
diff --git a/client/app/screens/chat/ChatScreen.tsx b/client/app/screens/chat/ChatScreen.tsx
index 6885a5c6..f46da397 100644
--- a/client/app/screens/chat/ChatScreen.tsx
+++ b/client/app/screens/chat/ChatScreen.tsx
@@ -17,6 +17,7 @@ import { useSocket } from "../../contexts/SocketContext";
import { AuthStore } from "../../services/AuthStore";
import { Message } from "../../types/Message";
import { useState, useEffect } from "react";
+import { useUser } from "@app/contexts/UserContext";
const ChatScreen = () => {
const settings = useSettings();
@@ -24,6 +25,7 @@ const ChatScreen = () => {
const keyboardBehavior = Platform.OS === "ios" ? "padding" : undefined;
const socket = useSocket();
const location = useLocation();
+ const user = useUser();
const userAuth = AuthStore.useState();
// Note: To prevent complexity, all user information is grabbed from different contexts and services. If we wanted most information inside of UserContext, we would have to import contexts within contexts and have state change as certain things mount, which could cause errors that are difficult to pinpoint.
@@ -31,7 +33,6 @@ const ChatScreen = () => {
const [messages, setMessages] = useState([]);
const [messageContent, setMessageContent] = useState("");
-
useEffect(() => {
if (socket === null) return; // This line might need to be changed
@@ -54,7 +55,7 @@ const ChatScreen = () => {
const newMessage: Message = {
author: {
uid: String(userAuth.userAuthInfo?.uid),
- displayName: "Anonymous",
+ displayName: String(user?.displayName),
},
msgId: Crypto.randomUUID(),
msgContent: messageContent.trim(),
diff --git a/client/app/screens/settings/SettingsScreen.tsx b/client/app/screens/settings/SettingsScreen.tsx
index 4db3a263..33fd9069 100644
--- a/client/app/screens/settings/SettingsScreen.tsx
+++ b/client/app/screens/settings/SettingsScreen.tsx
@@ -1,7 +1,20 @@
import React, { useState } from "react";
-import { SafeAreaView, Text, StyleSheet, View, ScrollView } from "react-native";
+import {
+ SafeAreaView,
+ Text,
+ StyleSheet,
+ View,
+ ScrollView,
+ Image,
+ Pressable,
+ Modal,
+ Button,
+ FlatList,
+ TouchableWithoutFeedback
+} from "react-native";
import { SettingsItem } from "../../components/settings/SettingsItem";
+import {ColorInput, DisplayNameInput} from "@app/components/settings/TextInputs";
// List of settings items
// toggle type: a switch
@@ -33,18 +46,122 @@ const Sections = [
const SettingsScreen: React.FC = () => {
// settings values (will be changed later to reflect the actual settings)
const [data, setData] = useState({
+ displayName: "Display Name",
+ profilePicIndex: 0, // index for icons array
+ profileColor: "#1199ff",
notifyNewMessage: true,
darkMode: false,
language: "English",
deleteMessages: false,
});
+ const[profileVisible, setProfileVisible] = useState(false);
+ const[inputVisible, setInputVisible] = useState({
+ displayName: false,
+ profileColor: false,
+ });
+
+
+ const iconStyle = [styles.icon, {backgroundColor: data.profileColor}]
+
+ const icons = [
+ require("../../../assets/icons/user/face_01.png"),
+ require("../../../assets/icons/user/face_02.png"),
+ require("../../../assets/icons/user/face_03.png"),
+ require("../../../assets/icons/user/face_04.png"),
+ require("../../../assets/icons/user/face_05.png"),
+ require("../../../assets/icons/user/face_06.png"),
+ require("../../../assets/icons/user/face_07.png"),
+ require("../../../assets/icons/user/fake_pfp.jpg"),
+ ];
+
return (
+
+ {/* User Settings Menu */}
+ setProfileVisible(false)}
+ >
+
+ setProfileVisible(false)}>
+
+
+
+
+ Hi {data.displayName}!
+
+ setProfileVisible(false)}>
+
+
+
+
+ setInputVisible({...inputVisible, ["displayName"]: value})}
+ outputSetter={(output: string) => setData({...data, ["displayName"]: output})}
+ />
+ setInputVisible({...inputVisible, ["profileColor"]: value})}
+ outputSetter={(output: string) => setData({...data, ["profileColor"]: output})}
+ />
+
+ {/* User Settings */}
+
+
+ Edit Profile
+
+
+ setInputVisible({...inputVisible, ["displayName"]: true})}
+ />
+ setInputVisible({...inputVisible, ["profileColor"]: true})}
+ />
+
+
+ Change profile picture
+
+
+ (
+ setData({ ...data, ["profilePicIndex"]: icon.index })}>
+
+
+ )}>
+
+
+
+
+
+
+
+
+
+ {/* Settings Screen */}
Settings
+ setProfileVisible(true)}>
+
+
+
{Sections.map(({ header, items }) => (
@@ -79,6 +196,8 @@ const styles = StyleSheet.create({
paddingVertical: 24,
},
header: {
+ flexDirection: "row",
+ justifyContent: "space-between",
paddingLeft: 24,
paddingRight: 24,
},
@@ -106,6 +225,23 @@ const styles = StyleSheet.create({
borderBottomWidth: 1,
borderColor: "#e3e3e3",
},
+ icon: {
+ width: 50,
+ height: 50,
+ borderRadius: 20,
+ },
+ selected: {
+ borderWidth: 3,
+ borderColor: "yellow",
+ margin: 5,
+ },
+ userModal: {
+ backgroundColor: "#d4d4d4",
+ borderRadius: 10,
+ paddingVertical: 24,
+ marginLeft: "auto",
+ height: "50%", // For some reason I can't get the view to auto fit height to children so just set to 50% for now
+ },
});
export default SettingsScreen;
diff --git a/client/app/types/env.d.ts b/client/app/types/env.d.ts
index c9ce7c42..e48370fe 100644
--- a/client/app/types/env.d.ts
+++ b/client/app/types/env.d.ts
@@ -1,5 +1,9 @@
-declare module '@env' {
-
-export const EXPO_IP: string;
-export const API_KEY, AUTH_DOMAIN, PROJECT_ID, STORAGE_BUCKET, MESSAGING_SENDER_ID, APP_ID: string;
+declare module "@env" {
+ export const EXPO_IP: string;
+ export const API_KEY: string,
+ AUTH_DOMAIN: string,
+ PROJECT_ID: string,
+ STORAGE_BUCKET: string,
+ MESSAGING_SENDER_ID: string,
+ APP_ID: string;
}
diff --git a/client/babel.config.js b/client/babel.config.js
index 95f997c4..b43e2717 100644
--- a/client/babel.config.js
+++ b/client/babel.config.js
@@ -6,9 +6,12 @@ module.exports = function (api) {
[
"module:react-native-dotenv",
{
- envName: "APP_ENV",
- moudleName: "@env",
+ moduleName: "@env",
path: ".env",
+ blacklist: null,
+ whitelist: null,
+ safe: false,
+ allowUndefined: true,
},
],
],
diff --git a/client/env.d.ts b/client/env.d.ts
new file mode 100644
index 00000000..d78c9670
--- /dev/null
+++ b/client/env.d.ts
@@ -0,0 +1,6 @@
+declare module "@env" {
+ export const EXPO_IP: string;
+ export const API_KEY: string;
+ export const AUTH_DOMAIN: string;
+ export const LOCATION_REFRESH_RATE: string;
+}
diff --git a/client/package-lock.json b/client/package-lock.json
index e66df0b9..f4ed6e6f 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -26,11 +26,11 @@
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.6",
"expo-web-browser": "~13.0.3",
+ "firebase": "^10.13.1",
"pullstate": "^1.25.0",
"react": "18.2.0",
"react-dom": "18.2.0",
- "react-native": "0.74.2",
- "react-native-dotenv": "^3.4.11",
+ "react-native": "^0.74.2",
"react-native-feather": "^1.1.2",
"react-native-gesture-handler": "~2.16.1",
"react-native-reanimated": "~3.10.1",
@@ -48,6 +48,7 @@
"eslint-config-expo": "^7.0.0",
"jest": "^29.2.1",
"jest-expo": "~51.0.1",
+ "react-native-dotenv": "^3.4.11",
"react-test-renderer": "18.2.0",
"typescript": "~5.3.3"
}
@@ -3678,36 +3679,120 @@
"node": ">=8"
}
},
- "node_modules/@fastify/busboy": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
- "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
- "engines": {
- "node": ">=14"
+ "node_modules/@firebase/analytics": {
+ "version": "0.10.7",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics/-/analytics-0.10.7.tgz",
+ "integrity": "sha512-GE29uTT6y/Jv2EP0OjpTezeTQZ5FTCTaZXKrrdVGjb/t35AU4u/jiU+hUwUPpuK8fqhhiHkS/AawE3a3ZK/a9Q==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/analytics-compat": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics-compat/-/analytics-compat-0.2.13.tgz",
+ "integrity": "sha512-aZ4wGfNDMsCxhKzDbK2g1aV0JKsdQ9FbeIsjpNJPzhahV0XYj+z36Y4RNLPpG/6hHU4gxnezxs+yn3HhHkNL8w==",
+ "dependencies": {
+ "@firebase/analytics": "0.10.7",
+ "@firebase/analytics-types": "0.8.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
}
},
+ "node_modules/@firebase/analytics-types": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@firebase/analytics-types/-/analytics-types-0.8.2.tgz",
+ "integrity": "sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw=="
+ },
"node_modules/@firebase/app": {
- "version": "0.10.5",
- "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.5.tgz",
- "integrity": "sha512-iY/fNot+hWPk9sTX8aHMqlcX9ynRvpGkskWAdUZ2eQQdLo8d1hSFYcYNwPv0Q/frGMasw8udKWMcFOEpC9fG8g==",
+ "version": "0.10.10",
+ "resolved": "https://registry.npmjs.org/@firebase/app/-/app-0.10.10.tgz",
+ "integrity": "sha512-sDqkdeFdVn5uygQm5EuIKOQ6/wxTcX/qKfm0MR46AiwLRHGLCDUMrXBkc8GhkK3ca2d6mPUSfPmndggo43D6PQ==",
"dependencies": {
- "@firebase/component": "0.6.7",
+ "@firebase/component": "0.6.8",
"@firebase/logger": "0.4.2",
- "@firebase/util": "1.9.6",
+ "@firebase/util": "1.9.7",
"idb": "7.1.1",
"tslib": "^2.1.0"
}
},
+ "node_modules/@firebase/app-check": {
+ "version": "0.8.7",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check/-/app-check-0.8.7.tgz",
+ "integrity": "sha512-EkOeJcMKVR0zZ6z/jqcFTqHb/xq+TVIRIuBNGHdpcIuFU1czhSlegvqv2+nC+nFrkD8M6Xvd3tAlUOkdbMeS6A==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/app-check-compat": {
+ "version": "0.3.14",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-compat/-/app-check-compat-0.3.14.tgz",
+ "integrity": "sha512-kK3bPfojAfXE53W+20rxMqIxrloFswXG9vh4kEdYL6Wa2IB3sD5++2dPiK3yGxl8oQiqS8qL2wcKB5/xLpEVEg==",
+ "dependencies": {
+ "@firebase/app-check": "0.8.7",
+ "@firebase/app-check-types": "0.5.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/app-check-interop-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-interop-types/-/app-check-interop-types-0.3.2.tgz",
+ "integrity": "sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ=="
+ },
+ "node_modules/@firebase/app-check-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-check-types/-/app-check-types-0.5.2.tgz",
+ "integrity": "sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA=="
+ },
+ "node_modules/@firebase/app-compat": {
+ "version": "0.2.40",
+ "resolved": "https://registry.npmjs.org/@firebase/app-compat/-/app-compat-0.2.40.tgz",
+ "integrity": "sha512-2L5MW4MH2ya7Wvw0hzWy3ZWeB4SqC5gYXDAV5AS1lBTL4zL3k8dsqJmry/cFV00GgkCI01WJbcXvFMCXJvgyow==",
+ "dependencies": {
+ "@firebase/app": "0.10.10",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/app-types": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/@firebase/app-types/-/app-types-0.9.2.tgz",
+ "integrity": "sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ=="
+ },
"node_modules/@firebase/auth": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.4.tgz",
- "integrity": "sha512-d2Fw17s5QesojwebrA903el20Li9/YGgkoOGJjagM4I1qAT36APa/FcZ+OX86KxbYKCtQKTMqraU8pxG7C2JWA==",
+ "version": "1.7.8",
+ "resolved": "https://registry.npmjs.org/@firebase/auth/-/auth-1.7.8.tgz",
+ "integrity": "sha512-1KJlDrTrEEFTIBj9MxjAWjQ4skecBD4bmoayQ0l14QDbNc1a8qGbi+MFSJkH7O6VnGE6bTMcWSw6RrQNecqKaw==",
"dependencies": {
- "@firebase/component": "0.6.7",
+ "@firebase/component": "0.6.8",
"@firebase/logger": "0.4.2",
- "@firebase/util": "1.9.6",
+ "@firebase/util": "1.9.7",
"tslib": "^2.1.0",
- "undici": "5.28.4"
+ "undici": "6.19.7"
},
"peerDependencies": {
"@firebase/app": "0.x",
@@ -3719,26 +3804,200 @@
}
}
},
- "node_modules/@firebase/auth/node_modules/undici": {
- "version": "5.28.4",
- "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.4.tgz",
- "integrity": "sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==",
+ "node_modules/@firebase/auth-compat": {
+ "version": "0.5.13",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-compat/-/auth-compat-0.5.13.tgz",
+ "integrity": "sha512-rV6TMxUU6wBBZ2ouDMtjJsJXeewtvYvVzslzt3/P7O/kxiWlreHT/2M/1guMiXKo3zk52XK3GqP0uM2bN7fEow==",
"dependencies": {
- "@fastify/busboy": "^2.0.0"
+ "@firebase/auth": "1.7.8",
+ "@firebase/auth-types": "0.12.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
},
- "engines": {
- "node": ">=14.0"
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/auth-interop-types": {
+ "version": "0.2.3",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-interop-types/-/auth-interop-types-0.2.3.tgz",
+ "integrity": "sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ=="
+ },
+ "node_modules/@firebase/auth-types": {
+ "version": "0.12.2",
+ "resolved": "https://registry.npmjs.org/@firebase/auth-types/-/auth-types-0.12.2.tgz",
+ "integrity": "sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
}
},
"node_modules/@firebase/component": {
- "version": "0.6.7",
- "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.7.tgz",
- "integrity": "sha512-baH1AA5zxfaz4O8w0vDwETByrKTQqB5CDjRls79Sa4eAGAoERw4Tnung7XbMl3jbJ4B/dmmtsMrdki0KikwDYA==",
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/component/-/component-0.6.8.tgz",
+ "integrity": "sha512-LcNvxGLLGjBwB0dJUsBGCej2fqAepWyBubs4jt1Tiuns7QLbXHuyObZ4aMeBjZjWx4m8g1LoVI9QFpSaq/k4/g==",
"dependencies": {
- "@firebase/util": "1.9.6",
+ "@firebase/util": "1.9.7",
"tslib": "^2.1.0"
}
},
+ "node_modules/@firebase/database": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@firebase/database/-/database-1.0.7.tgz",
+ "integrity": "sha512-wjXr5AO8RPxVVg7rRCYffT7FMtBjHRfJ9KMwi19MbOf0vBf0H9YqW3WCgcnLpXI6ehiUcU3z3qgPnnU0nK6SnA==",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "faye-websocket": "0.11.4",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database-compat": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@firebase/database-compat/-/database-compat-1.0.7.tgz",
+ "integrity": "sha512-R/3B+VVzEFN5YcHmfWns3eitA8fHLTL03io+FIoMcTYkajFnrBdS3A+g/KceN9omP7FYYYGTQWF9lvbEx6eMEg==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/database": "1.0.7",
+ "@firebase/database-types": "1.0.4",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/@firebase/database-types": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@firebase/database-types/-/database-types-1.0.4.tgz",
+ "integrity": "sha512-mz9ZzbH6euFXbcBo+enuJ36I5dR5w+enJHHjy9Y5ThCdKUseqfDjW3vCp1YxE9zygFCSjJJ/z1cQ+zodvUcwPQ==",
+ "dependencies": {
+ "@firebase/app-types": "0.9.2",
+ "@firebase/util": "1.9.7"
+ }
+ },
+ "node_modules/@firebase/firestore": {
+ "version": "4.7.1",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore/-/firestore-4.7.1.tgz",
+ "integrity": "sha512-WliQNa8GVcH6EWkH0NAf+uAnxNiBuH+G8Buzr2ZS1NznOhJDK/q6Hsjv5TzNrijLTAdEfj/wk9VEv994KDSjxg==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "@firebase/webchannel-wrapper": "1.0.1",
+ "@grpc/grpc-js": "~1.9.0",
+ "@grpc/proto-loader": "^0.7.8",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "engines": {
+ "node": ">=10.10.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/firestore-compat": {
+ "version": "0.3.36",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore-compat/-/firestore-compat-0.3.36.tgz",
+ "integrity": "sha512-NtoIm7CT9f+SFB7cPMCtyCSxZReh/+SII5X4TQH394S3dwhru9HIfvEOKAMuAnXsSsLH72jXPUgdsEAUqg6Oug==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/firestore": "4.7.1",
+ "@firebase/firestore-types": "3.0.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/firestore-types": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@firebase/firestore-types/-/firestore-types-3.0.2.tgz",
+ "integrity": "sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
+ }
+ },
+ "node_modules/@firebase/functions": {
+ "version": "0.11.7",
+ "resolved": "https://registry.npmjs.org/@firebase/functions/-/functions-0.11.7.tgz",
+ "integrity": "sha512-xaUsGI2kYrI8zJXgrNB7SrJKB8v1vJqR16YYi6g6dFTgBz4+VzWJFqqVU60BbdAWm6fXnUrg9gjlJQeqomT2Vg==",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/auth-interop-types": "0.2.3",
+ "@firebase/component": "0.6.8",
+ "@firebase/messaging-interop-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/functions-compat": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@firebase/functions-compat/-/functions-compat-0.3.13.tgz",
+ "integrity": "sha512-qcZvJO2ed6PAD+18DanVztw7WyQVQK43HoRhxusHAwDFvK/xY+mcGpj+IpfdxTNMBGCOIxKFp4Xqk/c2nubBlQ==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/functions": "0.11.7",
+ "@firebase/functions-types": "0.6.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/functions-types": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@firebase/functions-types/-/functions-types-0.6.2.tgz",
+ "integrity": "sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w=="
+ },
+ "node_modules/@firebase/installations": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/installations/-/installations-0.6.8.tgz",
+ "integrity": "sha512-57V374qdb2+wT5v7+ntpLXBjZkO6WRgmAUbVkRfFTM/4t980p0FesbqTAcOIiM8U866UeuuuF8lYH70D3jM/jQ==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/installations-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/installations-compat/-/installations-compat-0.2.8.tgz",
+ "integrity": "sha512-pI2q8JFHB7yIq/szmhzGSWXtOvtzl6tCUmyykv5C8vvfOVJUH6mP4M4iwjbK8S1JotKd/K70+JWyYlxgQ0Kpyw==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/installations-types": "0.5.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/installations-types": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/@firebase/installations-types/-/installations-types-0.5.2.tgz",
+ "integrity": "sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x"
+ }
+ },
"node_modules/@firebase/logger": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/@firebase/logger/-/logger-0.4.2.tgz",
@@ -3747,14 +4006,183 @@
"tslib": "^2.1.0"
}
},
+ "node_modules/@firebase/messaging": {
+ "version": "0.12.10",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging/-/messaging-0.12.10.tgz",
+ "integrity": "sha512-fGbxJPKpl2DIKNJGhbk4mYPcM+qE2gl91r6xPoiol/mN88F5Ym6UeRdMVZah+pijh9WxM55alTYwXuW40r1Y2Q==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/messaging-interop-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "idb": "7.1.1",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/messaging-compat": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging-compat/-/messaging-compat-0.2.10.tgz",
+ "integrity": "sha512-FXQm7rcowkDm8kFLduHV35IRYCRo+Ng0PIp/t1+EBuEbyplaKkGjZ932pE+owf/XR+G/60ku2QRBptRGLXZydg==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/messaging": "0.12.10",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/messaging-interop-types": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@firebase/messaging-interop-types/-/messaging-interop-types-0.2.2.tgz",
+ "integrity": "sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA=="
+ },
+ "node_modules/@firebase/performance": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmjs.org/@firebase/performance/-/performance-0.6.8.tgz",
+ "integrity": "sha512-F+alziiIZ6Yn8FG47mxwljq+4XkgkT2uJIFRlkyViUQRLzrogaUJW6u/+6ZrePXnouKlKIwzqos3PVJraPEcCA==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/performance-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/performance-compat/-/performance-compat-0.2.8.tgz",
+ "integrity": "sha512-o7TFClRVJd3VIBoY7KZQqtCeW0PC6v9uBzM6Lfw3Nc9D7hM6OonqecYvh7NwJ6R14k+xM27frLS4BcCvFHKw2A==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/performance": "0.6.8",
+ "@firebase/performance-types": "0.2.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/performance-types": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/@firebase/performance-types/-/performance-types-0.2.2.tgz",
+ "integrity": "sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA=="
+ },
+ "node_modules/@firebase/remote-config": {
+ "version": "0.4.8",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config/-/remote-config-0.4.8.tgz",
+ "integrity": "sha512-AMLqe6wfIRnjc6FkCWOSUjhc1fSTEf8o+cv1NolFvbiJ/tU+TqN4pI7pT+MIKQzNiq5fxLehkOx+xtAQBxPJKQ==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/installations": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/remote-config-compat": {
+ "version": "0.2.8",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config-compat/-/remote-config-compat-0.2.8.tgz",
+ "integrity": "sha512-UxSFOp6dzFj2AHB8Bq/BYtbq5iFyizKx4Rd6WxAdaKYM8cnPMeK+l2v+Oogtjae+AeyHRI+MfL2acsfVe5cd2A==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/remote-config": "0.4.8",
+ "@firebase/remote-config-types": "0.3.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/remote-config-types": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/@firebase/remote-config-types/-/remote-config-types-0.3.2.tgz",
+ "integrity": "sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA=="
+ },
+ "node_modules/@firebase/storage": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/@firebase/storage/-/storage-0.13.1.tgz",
+ "integrity": "sha512-L6AJ5tWgHSi2g/gbc/2Pbm3qxmoEg9THmPIOpRsLwuz9LPeWbhyMQeGlqxWqtZGQO/z/LMjGYadNlupQj0HNfw==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0",
+ "undici": "6.19.7"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x"
+ }
+ },
+ "node_modules/@firebase/storage-compat": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/@firebase/storage-compat/-/storage-compat-0.3.11.tgz",
+ "integrity": "sha512-EEa9jgm/aRVIGSD0ByYAsZ0tvEKfVwSp9uFoa/97BISGWGjSNPIWjenaDvpDZ7aL8OxaGIpwuk700aHy7/T0Ug==",
+ "dependencies": {
+ "@firebase/component": "0.6.8",
+ "@firebase/storage": "0.13.1",
+ "@firebase/storage-types": "0.8.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "peerDependencies": {
+ "@firebase/app-compat": "0.x"
+ }
+ },
+ "node_modules/@firebase/storage-types": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/@firebase/storage-types/-/storage-types-0.8.2.tgz",
+ "integrity": "sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==",
+ "peerDependencies": {
+ "@firebase/app-types": "0.x",
+ "@firebase/util": "1.x"
+ }
+ },
"node_modules/@firebase/util": {
- "version": "1.9.6",
- "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.6.tgz",
- "integrity": "sha512-IBr1MZbp4d5MjBCXL3TW1dK/PDXX4yOGbiwRNh1oAbE/+ci5Uuvy9KIrsFYY80as1I0iOaD5oOMA9Q8j4TJWcw==",
+ "version": "1.9.7",
+ "resolved": "https://registry.npmjs.org/@firebase/util/-/util-1.9.7.tgz",
+ "integrity": "sha512-fBVNH/8bRbYjqlbIhZ+lBtdAAS4WqZumx03K06/u7fJSpz1TGjEMm1ImvKD47w+xaFKIP2ori6z8BrbakRfjJA==",
"dependencies": {
"tslib": "^2.1.0"
}
},
+ "node_modules/@firebase/vertexai-preview": {
+ "version": "0.0.3",
+ "resolved": "https://registry.npmjs.org/@firebase/vertexai-preview/-/vertexai-preview-0.0.3.tgz",
+ "integrity": "sha512-KVtUWLp+ScgiwkDKAvNkVucAyhLVQp6C6lhnVEuIg4mWhWcS3oerjAeVhZT4uNofKwWxRsOaB2Yec7DMTXlQPQ==",
+ "dependencies": {
+ "@firebase/app-check-interop-types": "0.3.2",
+ "@firebase/component": "0.6.8",
+ "@firebase/logger": "0.4.2",
+ "@firebase/util": "1.9.7",
+ "tslib": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ },
+ "peerDependencies": {
+ "@firebase/app": "0.x",
+ "@firebase/app-types": "0.x"
+ }
+ },
+ "node_modules/@firebase/webchannel-wrapper": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@firebase/webchannel-wrapper/-/webchannel-wrapper-1.0.1.tgz",
+ "integrity": "sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ=="
+ },
"node_modules/@graphql-typed-document-node/core": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz",
@@ -3764,6 +4192,35 @@
"graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0"
}
},
+ "node_modules/@grpc/grpc-js": {
+ "version": "1.9.15",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.9.15.tgz",
+ "integrity": "sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==",
+ "dependencies": {
+ "@grpc/proto-loader": "^0.7.8",
+ "@types/node": ">=12.12.47"
+ },
+ "engines": {
+ "node": "^8.13.0 || >=10.10.0"
+ }
+ },
+ "node_modules/@grpc/proto-loader": {
+ "version": "0.7.13",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz",
+ "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
+ "dependencies": {
+ "lodash.camelcase": "^4.3.0",
+ "long": "^5.0.0",
+ "protobufjs": "^7.2.5",
+ "yargs": "^17.7.2"
+ },
+ "bin": {
+ "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/@hapi/hoek": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz",
@@ -4817,6 +5274,60 @@
"node": ">=14"
}
},
+ "node_modules/@protobufjs/aspromise": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+ "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
+ },
+ "node_modules/@protobufjs/base64": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+ "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
+ },
+ "node_modules/@protobufjs/codegen": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+ "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
+ },
+ "node_modules/@protobufjs/eventemitter": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+ "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
+ },
+ "node_modules/@protobufjs/fetch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+ "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.1",
+ "@protobufjs/inquire": "^1.1.0"
+ }
+ },
+ "node_modules/@protobufjs/float": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+ "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
+ },
+ "node_modules/@protobufjs/inquire": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+ "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
+ },
+ "node_modules/@protobufjs/path": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+ "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
+ },
+ "node_modules/@protobufjs/pool": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+ "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
+ },
+ "node_modules/@protobufjs/utf8": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
+ },
"node_modules/@radix-ui/react-compose-refs": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.0.tgz",
@@ -11540,6 +12051,17 @@
"reusify": "^1.0.4"
}
},
+ "node_modules/faye-websocket": {
+ "version": "0.11.4",
+ "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz",
+ "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==",
+ "dependencies": {
+ "websocket-driver": ">=0.5.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/fb-watchman": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz",
@@ -11785,6 +12307,40 @@
"micromatch": "^4.0.2"
}
},
+ "node_modules/firebase": {
+ "version": "10.13.1",
+ "resolved": "https://registry.npmjs.org/firebase/-/firebase-10.13.1.tgz",
+ "integrity": "sha512-L5BSkmvB2dzCUMpr8i/O8WMJC3Nqj5Ld8Wj/qnak+tz2Ga+JH6/FO93xArg9IGhktCrPXVODoWp6t9ybdgmXCA==",
+ "dependencies": {
+ "@firebase/analytics": "0.10.7",
+ "@firebase/analytics-compat": "0.2.13",
+ "@firebase/app": "0.10.10",
+ "@firebase/app-check": "0.8.7",
+ "@firebase/app-check-compat": "0.3.14",
+ "@firebase/app-compat": "0.2.40",
+ "@firebase/app-types": "0.9.2",
+ "@firebase/auth": "1.7.8",
+ "@firebase/auth-compat": "0.5.13",
+ "@firebase/database": "1.0.7",
+ "@firebase/database-compat": "1.0.7",
+ "@firebase/firestore": "4.7.1",
+ "@firebase/firestore-compat": "0.3.36",
+ "@firebase/functions": "0.11.7",
+ "@firebase/functions-compat": "0.3.13",
+ "@firebase/installations": "0.6.8",
+ "@firebase/installations-compat": "0.2.8",
+ "@firebase/messaging": "0.12.10",
+ "@firebase/messaging-compat": "0.2.10",
+ "@firebase/performance": "0.6.8",
+ "@firebase/performance-compat": "0.2.8",
+ "@firebase/remote-config": "0.4.8",
+ "@firebase/remote-config-compat": "0.2.8",
+ "@firebase/storage": "0.13.1",
+ "@firebase/storage-compat": "0.3.11",
+ "@firebase/util": "1.9.7",
+ "@firebase/vertexai-preview": "0.0.3"
+ }
+ },
"node_modules/flat-cache": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
@@ -12417,6 +12973,11 @@
"node": ">= 0.8"
}
},
+ "node_modules/http-parser-js": {
+ "version": "0.5.8",
+ "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz",
+ "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q=="
+ },
"node_modules/http-proxy-agent": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
@@ -16087,6 +16648,11 @@
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"license": "MIT"
},
+ "node_modules/lodash.camelcase": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+ "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA=="
+ },
"node_modules/lodash.debounce": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
@@ -16282,6 +16848,11 @@
"node": ">=6"
}
},
+ "node_modules/long": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
+ "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q=="
+ },
"node_modules/loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -18188,6 +18759,29 @@
"react-is": "^16.13.1"
}
},
+ "node_modules/protobufjs": {
+ "version": "7.4.0",
+ "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
+ "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@protobufjs/aspromise": "^1.1.2",
+ "@protobufjs/base64": "^1.1.2",
+ "@protobufjs/codegen": "^2.0.4",
+ "@protobufjs/eventemitter": "^1.1.0",
+ "@protobufjs/fetch": "^1.1.0",
+ "@protobufjs/float": "^1.0.2",
+ "@protobufjs/inquire": "^1.1.0",
+ "@protobufjs/path": "^1.1.2",
+ "@protobufjs/pool": "^1.1.0",
+ "@protobufjs/utf8": "^1.1.0",
+ "@types/node": ">=13.7.0",
+ "long": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
"node_modules/psl": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
@@ -18432,7 +19026,6 @@
"version": "0.74.2",
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.74.2.tgz",
"integrity": "sha512-EBMBjPPL4/GjHMP4NqsZabT3gI5WU9cSmduABGAGrd8uIcmTZ5F2Ng9k6gFmRm7n8e8CULxDNu98ZpQfBjl7Bw==",
- "license": "MIT",
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native-community/cli": "13.6.8",
@@ -18492,6 +19085,7 @@
"version": "3.4.11",
"resolved": "https://registry.npmjs.org/react-native-dotenv/-/react-native-dotenv-3.4.11.tgz",
"integrity": "sha512-6vnIE+WHABSeHCaYP6l3O1BOEhWxKH6nHAdV7n/wKn/sciZ64zPPp2NUdEUf1m7g4uuzlLbjgr+6uDt89q2DOg==",
+ "dev": true,
"dependencies": {
"dotenv": "^16.4.5"
},
@@ -20784,10 +21378,9 @@
}
},
"node_modules/undici": {
- "version": "6.19.2",
- "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.2.tgz",
- "integrity": "sha512-JfjKqIauur3Q6biAtHJ564e3bWa8VvT+7cSiOJHFbX4Erv6CLGDpg8z+Fmg/1OI/47RA+GI2QZaF48SSaLvyBA==",
- "license": "MIT",
+ "version": "6.19.7",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-6.19.7.tgz",
+ "integrity": "sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==",
"engines": {
"node": ">=18.17"
}
@@ -21106,6 +21699,27 @@
"node": ">=12"
}
},
+ "node_modules/websocket-driver": {
+ "version": "0.7.4",
+ "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz",
+ "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==",
+ "dependencies": {
+ "http-parser-js": ">=0.5.1",
+ "safe-buffer": ">=5.1.0",
+ "websocket-extensions": ">=0.1.1"
+ },
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "node_modules/websocket-extensions": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz",
+ "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
"node_modules/whatwg-encoding": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
diff --git a/client/package.json b/client/package.json
index e98a5f52..9bdcab1b 100644
--- a/client/package.json
+++ b/client/package.json
@@ -33,11 +33,11 @@
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.6",
"expo-web-browser": "~13.0.3",
+ "firebase": "^10.13.1",
"pullstate": "^1.25.0",
"react": "18.2.0",
"react-dom": "18.2.0",
- "react-native": "0.74.2",
- "react-native-dotenv": "^3.4.11",
+ "react-native": "^0.74.2",
"react-native-feather": "^1.1.2",
"react-native-gesture-handler": "~2.16.1",
"react-native-reanimated": "~3.10.1",
@@ -51,12 +51,13 @@
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"@types/react-test-renderer": "^18.0.7",
+ "eslint": "^8.57.0",
+ "eslint-config-expo": "^7.0.0",
"jest": "^29.2.1",
"jest-expo": "~51.0.1",
+ "react-native-dotenv": "^3.4.11",
"react-test-renderer": "18.2.0",
- "typescript": "~5.3.3",
- "eslint": "^8.57.0",
- "eslint-config-expo": "^7.0.0"
+ "typescript": "~5.3.3"
},
"private": true
}
diff --git a/client/tsconfig.json b/client/tsconfig.json
index 0613da3c..350d5a11 100644
--- a/client/tsconfig.json
+++ b/client/tsconfig.json
@@ -6,18 +6,11 @@
"esModuleInterop": true,
"baseUrl": ".",
"paths": {
- "@firebase/auth": [
- "./node_modules/@firebase/auth/dist/index.rn.d.ts"
- ],
- "@app/*": [
- "app/*"
- ],
- "@env": [
- ".env"
- ],
- "typeRoots": [
- "./app/types"
- ]
- }
- }
-}
\ No newline at end of file
+ "@firebase/auth": ["./node_modules/@firebase/auth/dist/index.rn.d.ts"],
+ "@app/*": ["app/*"]
+ },
+ "typeRoots": ["./app/types"]
+ },
+ "include": ["**/*.ts", "**/*.tsx", "env.d.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/server/src/actions/deleteConnectedUser.ts b/server/src/actions/deleteConnectedUser.ts
index bb8d6488..216c475f 100644
--- a/server/src/actions/deleteConnectedUser.ts
+++ b/server/src/actions/deleteConnectedUser.ts
@@ -2,12 +2,5 @@
import { connectedUsersCollection } from '../utilities/firebaseInit'
export const deleteConnectedUserByUID = async (socketID: string) => {
- try {
await connectedUsersCollection.doc(socketID).delete()
- return true
-
- } catch (error) {
- console.error(error.message)
- return false
- }
}
diff --git a/server/src/actions/getConnectedUsers.ts b/server/src/actions/getConnectedUsers.ts
index 30724cc5..489f9a1e 100644
--- a/server/src/actions/getConnectedUsers.ts
+++ b/server/src/actions/getConnectedUsers.ts
@@ -2,64 +2,58 @@ import { distanceBetween, geohashForLocation, geohashQueryBounds } from 'geofire
import { connectedUsersCollection } from '../utilities/firebaseInit'
export const getConnectedUser = async (socketID: string) => {
- try {
- const user = await connectedUsersCollection.doc(socketID).get();
- return user.data();
- } catch (error) {
- console.error("getConnectedUser failed:", error.message);
- return false;
- }
+ const user = await connectedUsersCollection.doc(socketID).get();
+ return user.data();
}
-export const findNearbyUsers = async (centerLat: number, centerLon: number, radius: number) => {
-// Return an array of nearby userIds (which are also socket ids) given a center latitude and longitude.
-// Latitude and longitude values use degrees with the same bounds as GeoPoints. Radius values use meters.
-// Credit to https://firebase.google.com/docs/firestore/solutions/geoqueries for doing the heavy lifting for us.
-
-// Additionally, GeoPoints and storing a 'center' array leads to type issues, so similar GeoPoint checks are performed.
-
- try {
- if (centerLat < -90 || centerLat > 90) throw Error("centerLat does not fit GeoPoint bounds.")
- if (centerLon < -180 || centerLon > 180) throw Error("centerLon does not fit GeoPoint bounds.")
-
- const originHash = geohashForLocation([centerLat, centerLon])
- const bounds = geohashQueryBounds([centerLat, centerLon], radius)
- const promises = []
-
- for (const b of bounds) {
+export const findNearbyUsers = async (
+ centerLat: number, centerLon: number, radius: number
+) => {
+ // Return an array of nearby userIds (which are also socket ids) given a center
+ // latitude and longitude. Latitude and longitude values use degrees with the
+ // same bounds as GeoPoints. Radius values use meters. Credit to
+ // https://firebase.google.com/docs/firestore/solutions/geoqueries for doing
+ // the heavy lifting for us. Additionally, GeoPoints and storing a 'center'
+ // array leads to type issues, so similar GeoPoint checks are performed.
+
+ if (centerLat < -90 || centerLat > 90)
+ throw Error("centerLat does not fit GeoPoint bounds.");
+ if (centerLon < -180 || centerLon > 180)
+ throw Error("centerLon does not fit GeoPoint bounds.");
+
+ const originHash = geohashForLocation([centerLat, centerLon]);
+ const bounds = geohashQueryBounds([centerLat, centerLon], radius);
+ const promises = [];
+
+ for (const b of bounds) {
const q = connectedUsersCollection
.orderBy('location.geohash')
.startAt(b[0])
- .endAt(b[1])
-
- promises.push(q.get())
- }
+ .endAt(b[1]);
- // Collect query results and append into a single array
- const snapshots = await Promise.all(promises)
+ promises.push(q.get());
+ }
- const matchingDocs = []
-
- for (const snap of snapshots) {
+ // Collect query results and append into a single array
+ const snapshots = await Promise.all(promises);
+ const matchingDocs = [];
+ for (const snap of snapshots) {
for (const doc of snap.docs) {
- const lat = doc.get('location.lat')
- const lon = doc.get('location.lon')
-
- // We have to filter out a few false positives due to GeoHash
- // accuracy, but most will match
- const distanceInKm = distanceBetween([lat, lon], [centerLat, centerLon])
- const distanceInM = distanceInKm * 1000
- if (distanceInM <= radius) {
- matchingDocs.push(doc.get('socketId'))
- }
+ const lat = doc.get('location.lat');
+ const lon = doc.get('location.lon');
+
+ // We have to filter out a few false positives due to GeoHash
+ // accuracy, but most will match
+ const distanceInKm = distanceBetween([lat, lon], [centerLat, centerLon]);
+ if (distanceInKm * 1000 <= radius) {
+ matchingDocs.push(doc.get('socketId'));
+ }
}
- }
+ }
- console.log(`getNearbyUsers(): ${matchingDocs.length} users found within ${radius} meters of ${centerLat}, ${centerLon}`)
- console.log(matchingDocs)
- return matchingDocs
- } catch (error) {
- console.error("getNearbyUsers() failed.", error.message)
- }
+ console.log(`getNearbyUsers(): ${matchingDocs.length} users found within`,
+ `${radius} meters of ${centerLat}, ${centerLon}`);
+ console.log(matchingDocs);
+ return matchingDocs;
}
diff --git a/server/src/actions/updateConnectedUser.ts b/server/src/actions/updateConnectedUser.ts
index c05421f1..fb64b2f5 100644
--- a/server/src/actions/updateConnectedUser.ts
+++ b/server/src/actions/updateConnectedUser.ts
@@ -2,37 +2,19 @@ import { geohashForLocation} from 'geofire-common'
import { connectedUsersCollection } from '../utilities/firebaseInit'
export const toggleUserConnectionStatus = async (socketID: string) => {
- try {
- let status = connectedUsersCollection.doc(socketID).isConnected
- // Flip the connection status
- status = !status
-
- await connectedUsersCollection.doc(socketID).update({ isConnected: status })
- return true
- } catch (error) {
- console.error(error.message)
- return false
- }
+ let status = connectedUsersCollection.doc(socketID).isConnected;
+ await connectedUsersCollection.doc(socketID).update({ isConnected: !status });
}
export const updateUserLocation = async (socketID: string, lat: number, lon: number) => {
- try {
- const newHash = geohashForLocation([lat, lon])
-
- await connectedUsersCollection.doc(socketID).update({ "location.lat": lat, "location.lon": lon, "location.geohash": newHash })
- return true
- } catch (error) {
- console.error(error.message)
- return false
- }
+ const newHash = geohashForLocation([lat, lon]);
+ await connectedUsersCollection.doc(socketID).update({
+ "location.lat": lat,
+ "location.lon": lon,
+ "location.geohash": newHash,
+ });
}
export const updateUserDisplayName = async (socketID: string, displayName: string) => {
- try {
- await connectedUsersCollection.doc(socketID).update({ displayName: displayName })
- return true
- } catch (error) {
- console.error(error.message)
- return false
- }
+ await connectedUsersCollection.doc(socketID).update({ displayName: displayName });
}
diff --git a/server/src/index.ts b/server/src/index.ts
index 7c268d57..82e1abae 100644
--- a/server/src/index.ts
+++ b/server/src/index.ts
@@ -105,7 +105,14 @@ io.on("connection", async (socket: any) => {
socket.on("disconnect", () => {
console.log(`[WS] User <${socket.id}> exited.`);
- deleteConnectedUserByUID(socket.id);
+ try {
+ deleteConnectedUserByUID(socket.id);
+ } catch (error) {
+ console.error(
+ `[WS] Error disconnecting user <${socket.id}>.\n\t`,
+ error.message
+ );
+ }
});
socket.on("ping", (ack) => {
// The (ack) parameter stands for "acknowledgement." This function sends a message back to the originating socket.
@@ -125,21 +132,24 @@ io.on("connection", async (socket: any) => {
});
socket.on("updateLocation", async (location, ack) => {
console.log(`[WS] Recieved new location from user <${socket.id}>.`);
+ let lat: number, lon: number;
+ try {
+ lat = Number(location.lat);
+ lon = Number(location.lon);
+ } catch {
+ console.error("[WS] Error calling updateLocation: Invalid [lat/lon].");
+ return;
+ }
+ defaultConnectedUser.location.lat = lat;
+ defaultConnectedUser.location.lon = lon;
try {
- const lat = Number(location.lat);
- const lon = Number(location.lon);
- defaultConnectedUser.location.lat = lat;
- defaultConnectedUser.location.lon = lon;
- const success = await updateUserLocation(socket.id, lat, lon);
- if (success) {
- console.log("[WS] Location updated in database successfully.");
- if (ack) ack("location updated");
- } else {
- throw new Error("updateUserLocation() failed.");
- }
+ await updateUserLocation(socket.id, lat, lon);
} catch (error) {
console.error("[WS] Error calling updateLocation:", error.message);
+ return;
}
+ console.log("[WS] Location updated in database successfully.");
+ if (ack) ack("location updated");
});
});
socketServer.listen(socket_port, () => {
diff --git a/server/src/routes/error/error.ts b/server/src/routes/error/error.ts
index 2f717bcd..6c37f1e7 100644
--- a/server/src/routes/error/error.ts
+++ b/server/src/routes/error/error.ts
@@ -2,26 +2,24 @@ import { Router } from "express";
const errorRouter = Router();
-
-// Error handling
-errorRouter.get("*", (req, res) => {
- res.json("404: Path could not be found! COULD NOT {GET}");
- res.status(404);
+errorRouter.get("*", (_, res) => {
+ res.json("404: Path could not be found! COULD NOT {GET}");
+ res.status(404);
});
-errorRouter.post("*", (req, res) => {
- res.json("404: Path could not be found! COULD NOT {POST}");
- res.status(404);
+errorRouter.post("*", (_, res) => {
+ res.json("404: Path could not be found! COULD NOT {POST}");
+ res.status(404);
});
-errorRouter.put("*", (req, res) => {
- res.json("404: Path could not be found! COULD NOT {PUT}");
- res.status(404);
+errorRouter.put("*", (_, res) => {
+ res.json("404: Path could not be found! COULD NOT {PUT}");
+ res.status(404);
});
-errorRouter.delete("*", (req, res) => {
-res.json("404: Path could not be found! COULD NOT {DELETE}");
- res.status(404);
+errorRouter.delete("*", (_, res) => {
+ res.json("404: Path could not be found! COULD NOT {DELETE}");
+ res.status(404);
});
-export default errorRouter;
\ No newline at end of file
+export default errorRouter;
diff --git a/server/src/routes/user/create.ts b/server/src/routes/user/create.ts
index 4e3e67bc..2538c460 100644
--- a/server/src/routes/user/create.ts
+++ b/server/src/routes/user/create.ts
@@ -4,26 +4,32 @@ import { createUser } from "../../actions/createConnectedUser";
const createUserRoute = Router();
createUserRoute.post("/users", async (req, res) => {
- try {
- const status = await createUser({
- uid: req.body.uid,
- socketId: req.body.socketId,
- location: {
- lat: Number(req.body.location.lat),
- lon: Number(req.body.location.lon),
- geohash: req.body.location.geohash,
- },
- });
- if (status === false) throw new Error("Error creating user: ");
- res.json("Operation was handled successfully.");
- console.log("[EXP] Request returned successfully.");
- } catch (error) {
- console.error(
- "[EXP] Error returning request :\n",
- error.message
- );
- res.json(`Operation failed.`);
- }
+ let status: boolean;
+ try {
+ status = await createUser({
+ uid: req.body.uid,
+ socketId: req.body.socketId,
+ location: {
+ lat: Number(req.body.location.lat),
+ lon: Number(req.body.location.lon),
+ geohash: req.body.location.geohash,
+ },
+ });
+ } catch (error) {
+ console.error(
+ "[EXP] Error processing request :\n\t",
+ error.message
+ );
+ res.status(500).json(`Operation failed.`);
+ return;
+ }
+ if (status) {
+ console.log("[EXP] Request returned successfully.");
+ res.json("Operation was handled successfully.");
+ } else {
+ console.error("[EXP] Error returning request ");
+ res.status(500).json(`Operation failed.`);
+ }
});
-export default createUserRoute;
\ No newline at end of file
+export default createUserRoute;
diff --git a/server/src/routes/user/delete.ts b/server/src/routes/user/delete.ts
index 926ecbb6..c5ba6643 100644
--- a/server/src/routes/user/delete.ts
+++ b/server/src/routes/user/delete.ts
@@ -4,24 +4,26 @@ import { deleteConnectedUserByUID } from "../../actions/deleteConnectedUser";
const deleteUserRoute = Router();
deleteUserRoute.delete("/users", async (req, res) => {
- let query = "";
- try {
- query = "?userId";
- const userId = req.query.userId;
- if (typeof userId != "string") throw Error(" [userId] is not a string.");
-
- const success = await deleteConnectedUserByUID(userId);
- if (!success) throw Error(" deleteUserById() failed.");
-
- console.log(`[EXP] Request returned successfully.`);
- res.json(`Operation was handled successfully.`);
- } catch (error) {
- console.error(
- `[EXP] Error returning request :\n`,
- error.message
- );
- res.json(`Operation failed.`);
- }
+ const userId = req.query.userId;
+ if (typeof userId != "string") {
+ console.error(
+ `[EXP] Error returning request :\n\t`,
+ "[userId] is not a string."
+ );
+ res.status(400).json(`Operation failed.`);
+ return;
+ }
+ try {
+ await deleteConnectedUserByUID(userId);
+ } catch (error) {
+ console.error(
+ `[EXP] Error returning request :\n\t`,
+ error.message
+ );
+ res.status(500).json(`Operation failed.`);
+ }
+ console.log(`[EXP] Request returned successfully.`);
+ res.json(`Operation was handled successfully.`);
});
export default deleteUserRoute;
diff --git a/server/src/routes/user/nearby.ts b/server/src/routes/user/nearby.ts
index 43c9763f..0a23568f 100644
--- a/server/src/routes/user/nearby.ts
+++ b/server/src/routes/user/nearby.ts
@@ -4,38 +4,65 @@ import { findNearbyUsers, getConnectedUser } from "../../actions/getConnectedUse
const nearbyUserRoute = Router();
nearbyUserRoute.get("/users", async (req, res) => {
- let query = "";
+ if (req.query.lat && req.query.lon && req.query.radius) {
+ // Looks up all users close to a geographic location extended by a radius (in meters).
+
+ let lat: number, lon: number, radius: number;
+ try {
+ lat = Number(req.query.lat);
+ lon = Number(req.query.lon);
+ radius = Number(req.query.radius);
+ } catch {
+ console.error(
+ `[EXP] Error returning request :\n\t`,
+ "[lat/lon/radius] are not valid numbers."
+ );
+ res.status(400).json(`Operation failed.`);
+ return;
+ }
+
+ let userIds;
+ try {
+ userIds = await findNearbyUsers(lat, lon, radius);
+ } catch (error) {
+ console.error(
+ `[EXP] Error returning request :\n\t`,
+ error.message
+ );
+ res.status(500).json(`Operation failed.`);
+ return;
+ }
+ res.json(userIds);
+ } else if (req.query.userId) {
+ const userId = req.query.userId;
+ if (typeof userId != "string") {
+ console.error(
+ `[EXP] Error returning request :\n\t`,
+ "[userId] is not a valid string."
+ );
+ res.status(400).json(`Operation failed.`);
+ return;
+ }
+
+ let user;
try {
- if (req.query.lat && req.query.lon && req.query.radius) {
- // Looks up all users close to a geographic location extended by a radius (in meters).
- query = "?lat&lon&radius";
-
- const lat = Number(req.query.lat);
- const lon = Number(req.query.lon);
- const radius = Number(req.query.radius);
-
- const userIds = await findNearbyUsers(lat, lon, radius);
- res.json(userIds);
- } else if (req.query.userId) {
- query = "?userId";
- const userId = req.query.userId;
- if (typeof userId != "string") throw Error(" [userId] is not a string.");
-
- const user = await getConnectedUser(userId);
- if (user) {
- res.json(user);
- } else {
- // getConnectedUserDisplayName() will return false is an error is thrown, and print it to console.
- throw Error("getConnectedUser() failed.");
- }
- }
+ user = await getConnectedUser(userId);
} catch (error) {
console.error(
- `[EXP] Error returning request :\n`,
+ `[EXP] Error returning request :\n\t`,
error.message
);
- res.json(`Operation failed.`);
+ res.status(500).json(`Operation failed.`);
+ return;
}
+ res.json(user);
+ } else {
+ console.error(
+ `[EXP] Error returning request :\n\t`,
+ "No valid target specification."
+ );
+ res.status(400).json(`Operation failed.`);
+ }
});
export default nearbyUserRoute;
diff --git a/server/src/routes/user/update.ts b/server/src/routes/user/update.ts
index 6d068a46..a65eef02 100644
--- a/server/src/routes/user/update.ts
+++ b/server/src/routes/user/update.ts
@@ -1,50 +1,78 @@
import { Router } from "express";
-import { toggleUserConnectionStatus, updateUserDisplayName, updateUserLocation } from "../../actions/updateConnectedUser";
+import {
+ toggleUserConnectionStatus,
+ updateUserDisplayName,
+ updateUserLocation
+} from "../../actions/updateConnectedUser";
const updateUserRoute = Router();
updateUserRoute.put("/users", async (req, res) => {
- let query = "";
+ const baseErrorMsg = `[EXP] Error processing request :\n\t`;
+ let userId = null;
+ if (req.query.userId) {
+ userId = req.query.userId;
+ if (typeof userId != "string") {
+ console.error(baseErrorMsg, "Invalid [userId].");
+ res.status(400).json(`Operation failed.`);
+ return;
+ }
+ }
+ if (req.query.userId && req.query.toggleConnection) {
+ // Note: toggleConnection should be assigned 'true', but it at least
+ // needs to contain any value. We don't perform a check on this
+ // parameter for this reason.
+ try {
+ await toggleUserConnectionStatus(userId);
+ } catch (error) {
+ console.error(baseErrorMsg, error.message);
+ res.status(500).json(`Operation failed.`);
+ return;
+ }
+ res.json("Operation was handled successfully.");
+ } else if (req.query.userId && req.query.lat && req.query.lon) {
+ let lat: number, lon: number;
+ try {
+ lat = Number(req.query.lat);
+ lon = Number(req.query.lon);
+ } catch {
+ console.error(baseErrorMsg, "[lat/lon] are not valid numbers.");
+ res.status(400).json(`Operation failed.`);
+ return;
+ }
+
+ try {
+ await updateUserLocation(userId, lat, lon);
+ } catch (error) {
+ console.error(baseErrorMsg, error.message);
+ res.status(500).json(`Operation failed.`);
+ return;
+ }
+ res.json("Operation was handled successfully.");
+ } else if (req.query.userId && req.query.displayName) {
+ const displayName = req.query.displayName;
+ if (typeof displayName != "string") {
+ console.error(baseErrorMsg, "Invalid [displayName].");
+ res.status(400).json(`Operation failed.`);
+ return;
+ }
try {
- if (req.query.userId && req.query.toggleConnection) {
- // Note: toggleConnection should be assigned 'true', but it at least needs to contain any value. We don't perform a check on this parameter for this reason.
- query = "?userId&toggleConnection";
- const userId = req.query.userId;
- if (typeof userId != "string") throw Error(" [userId] is not a string.");
-
- const success = await toggleUserConnectionStatus(userId);
- if (!success) throw Error(" toggleUserConnectionStatus() failed.");
- } else if (req.query.userId && req.query.lat && req.query.lon) {
- query = "?userId&lat&lon";
- const userId = req.query.userId;
- const lat = Number(req.query.lat);
- const lon = Number(req.query.lon);
- if (typeof userId != "string") throw Error(" [userId] is not a string.");
- if (typeof lat != "number") throw Error(" [lat] is not a number.");
- if (typeof lon != "number") throw Error(" [lon] is not a number.");
-
- const success = await updateUserLocation(userId, lat, lon);
- if (!success) throw Error(" toggleUserConnectionStatus() failed.");
- } else if (req.query.userId && req.query.displayName) {
- query = "?userId&displayName";
- const userId = req.query.userId;
- if (typeof userId != "string") throw Error(" [userId] is not a string.");
- const displayName = req.query.displayName;
- if (typeof displayName != "string")
- throw Error(" [displayName] is not a string.");
-
- const success = await updateUserDisplayName(userId, displayName);
- if (!success) throw Error("updateDisplayName() failed.");
- }
- console.log(`[EXP] Request returned successfully.`);
- res.json(`Operation was handled successfully.`);
+ await updateUserDisplayName(userId, displayName);
} catch (error) {
- console.error(
- `[EXP] Error returning request :\n`,
- error.message
- );
- res.json(`Operation failed.`);
+ console.error(baseErrorMsg, error.message);
+ res.status(500).json(`Operation failed.`);
+ return;
}
- });
+ res.json("Operation was handled successfully.");
+ } else {
+ console.error(
+ `[EXP] Error returning request :\n\t`,
+ "No valid target specification."
+ );
+ res.status(400).json(`Operation failed.`);
+ return;
+ }
+ console.log(`[EXP] Request returned successfully.`);
+});
-export default updateUserRoute;
\ No newline at end of file
+export default updateUserRoute;