Compare commits
115 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48cc4d2227 | |||
| 584510d0f3 | |||
|
12cfc6f6a0
|
|||
|
2ec3b93190
|
|||
|
961e2b4587
|
|||
|
eaadade0ed
|
|||
|
564524772e
|
|||
|
b2c21d0782
|
|||
|
fa29041489
|
|||
|
296f21a082
|
|||
| ed3449a1f7 | |||
| 9f84ee233d | |||
| 1396d03b76 | |||
| ba0575f315 | |||
| 644206635f | |||
| 2b6bc641d4 | |||
| 0962df2187 | |||
|
7212a532f6
|
|||
| 85cd7c20f4 | |||
| 844bb14040 | |||
| 2e0ac0ba0f | |||
|
ea3647ecd3
|
|||
|
2796172d30
|
|||
|
0c8ca18326
|
|||
|
b1009b186e
|
|||
| 72ae128975 | |||
|
9802ea8096
|
|||
|
aa49341937
|
|||
|
29d6833e42
|
|||
|
d26e7d4290
|
|||
|
1f22895904
|
|||
|
319f066edf
|
|||
|
c77f16d3fc
|
|||
| c8d7e40203 | |||
| e1572cd84a | |||
| 1e5f462abd | |||
| 973096d480 | |||
| 5aa1c888e1 | |||
| 594c09ba65 | |||
| a883e3112e | |||
| b87c6ee600 | |||
| 94b8591643 | |||
| b7a30c3d6d | |||
| daa9de4142 | |||
| 08b57dc2b2 | |||
| e179e8cdef | |||
| 57e61e362a | |||
|
0042244b80
|
|||
| e8dfc7bf88 | |||
| 72bcccfe2f | |||
| 2c457ab531 | |||
| 1120f5eb6e | |||
| f16b77c94b | |||
| 62a813d0ad | |||
| 151de4ebfd | |||
| 199c283b0e | |||
|
60bdf36274
|
|||
|
a7eb9b942b
|
|||
|
7cd1e9b5da
|
|||
|
afd5dbaa9f
|
|||
|
eeb6b856e6
|
|||
|
9ee8bf3376
|
|||
|
848d588bf4
|
|||
| 7ca4b62848 | |||
| 8cd5fd8783 | |||
| afd72ec72b | |||
| 0895e93f6c | |||
| 67ec566728 | |||
|
8737d10a1e
|
|||
|
d9bca7f1ff
|
|||
|
4b9b46f10d
|
|||
|
bafcd88fa1
|
|||
| ac7317a0b7 | |||
| 034b217916 | |||
| ef3f7fa174 | |||
|
17d2ce436a
|
|||
|
6dbc0a2e8e
|
|||
|
43e9045b0a
|
|||
|
5ca9c4222c
|
|||
|
5e8a5c89b3
|
|||
| dc5469fc70 | |||
|
ac19919c51
|
|||
| 7d2b653953 | |||
| 6dea6cc168 | |||
| 75e3476d48 | |||
| c73f85a411 | |||
| 5aba1549d4 | |||
| 3f68e43efd | |||
| b24a2b5254 | |||
| b38f12a6ab | |||
| b07a333459 | |||
| 13e5f319c7 | |||
| 4391a072cc | |||
| 66c9de922d | |||
| 245ae616b1 | |||
| 3afbe17a21 | |||
| 8d3283ef04 | |||
| 8862f7b94c | |||
| 6e5af78586 | |||
| ca731147f2 | |||
| a8dc4d8460 | |||
| 6a0d3f3834 | |||
| 04cdc500b2 | |||
| 8cd7cafdb5 | |||
| ec2fc15a3f | |||
| d7a2382cb5 | |||
| 69d5bfe9a9 | |||
| c555a59cf8 | |||
| d20aecdbe8 | |||
| 57a8415e52 | |||
|
f19b09c5fd
|
|||
|
c6056c385b
|
|||
|
7420cc63fb
|
|||
|
c528c6bacf
|
|||
|
daedd0b068
|
@@ -1,29 +0,0 @@
|
||||
name: Frontend Next.Js Build Validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- "main"
|
||||
|
||||
jobs:
|
||||
nextjs:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 21.6.2
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install frontend-next/
|
||||
|
||||
- name: Lint
|
||||
run: npm --prefix frontend-next/ run lint
|
||||
|
||||
- name: Build Frontend
|
||||
run: npm --prefix frontend-next/ run build
|
||||
@@ -1,3 +1,7 @@
|
||||
# Firebase Stuff
|
||||
firebase-admin-key.json
|
||||
firebase*.json
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
@@ -127,4 +131,7 @@ dist
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.*
|
||||
.pnp.*
|
||||
|
||||
# Am I the only mac user
|
||||
**/.DS_Store
|
||||
@@ -1,4 +1,4 @@
|
||||
# ChatMaps
|
||||

|
||||
Main repo for ChatMaps, our COS420 Project.
|
||||
|
||||
ChatMaps is a web-based social networking service that allows users to connect to others in their local geographic area. It will implement an interactable mapping utility to show general user locations relative to other users, as well as a chat room feature that allows users to start public conversations based on a specified topic. ChatMaps is primarily intended for use in densely populated areas, such as college campuses or metropolitan areas, so people of similar interests can start conversations. The goal of this project is to create a web app that plots locations, gives a radius of the local area, and connects users into different topic-based chat rooms.
|
||||
@@ -9,12 +9,14 @@ This app shares some similarities to other social networks that implement locati
|
||||
|
||||
The live version of this app can be found at:
|
||||
|
||||
http://chatmaps.nicholaspease.com
|
||||
https://chatma.ps/
|
||||
|
||||
A local version can be run with:
|
||||
|
||||
cd frontend-next/
|
||||
|
||||
npm run build
|
||||
|
||||
npm run dev
|
||||
|
||||
then navigating to:
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
node_modules/
|
||||
@@ -1,16 +0,0 @@
|
||||
## Server Update Hook
|
||||
The purpose of this file is to enable automatic server updates when a pull request to the main branch succeeds. This is accomplished via a workflow sent from Github to the server which then reloads both itself and the frontend.
|
||||
|
||||
## API Endpoints
|
||||
Base Url: ```https://chatmaps.nicholaspease.com/api/v1/```
|
||||
|
||||
|Route|Method|Response Type|Use|Responses|
|
||||
|-----|------|-------------|---|---------|
|
||||
|```/``` |GET |Plain Text |Heartbeat|200 - "OK"|
|
||||
|```/deploy```|POST|Plain Text |Server Update Trigger|200 - "OK" - Server Online / Updated
|
||||
|
||||
## Files
|
||||
|File|Purpose|
|
||||
|----|-------|
|
||||
|api.js|NodeJS API using Express|
|
||||
|README.md|API Reference|
|
||||
@@ -1,32 +0,0 @@
|
||||
// Physical Server Update Hook
|
||||
// Imports
|
||||
// Requires express, body-parser, child_process
|
||||
const express = require("express")
|
||||
const bodyParser = require("body-parser")
|
||||
const { exec } = require("child_process")
|
||||
|
||||
// Start the express app and initialize
|
||||
const app = express()
|
||||
app.use(bodyParser.urlencoded({ extended: false }));
|
||||
app.use(bodyParser.json()); // This processes all the POST data as JSON
|
||||
|
||||
// Uses nodejs and express as "basic" backend server
|
||||
// Recieves webhook from Github at https://chatmaps.nicholaspease.com/
|
||||
app.post("/api/v1/deploy", function (req, res) {
|
||||
// Webhook returns all pull request actions
|
||||
// Limit to "closed", "merged", and the target branch being "main"
|
||||
if (req.body.action == "closed" && req.body.pull_request.merged == true && req.body.pull_request.base.ref == "main") {
|
||||
// (re)Start all the systemd files
|
||||
exec("systemctl restart frontend-next.service", (error, stdout, stderr) => {});
|
||||
exec("systemctl restart server_update_hook.service", (error, stdout, stderr) => {});
|
||||
}
|
||||
res.send("OK")
|
||||
})
|
||||
|
||||
// Generic endpoint, useful to test if updater is alive
|
||||
app.get('/api/v1', (req, res) => {
|
||||
res.send('OK')
|
||||
})
|
||||
|
||||
// Server runs on port 8000, exposed on server at /api/v1
|
||||
app.listen(8000)
|
||||
@@ -1,728 +0,0 @@
|
||||
{
|
||||
"name": "backend",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.2",
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
"version": "1.3.8",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
|
||||
"integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
|
||||
"dependencies": {
|
||||
"mime-types": "~2.1.34",
|
||||
"negotiator": "0.6.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/array-flatten": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||
"integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz",
|
||||
"integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.5",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.2",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
"integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/call-bind": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz",
|
||||
"integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"set-function-length": "^1.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "0.5.4",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||
"integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
|
||||
"dependencies": {
|
||||
"safe-buffer": "5.2.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/content-type": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
|
||||
"integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie": {
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz",
|
||||
"integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/cookie-signature": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dependencies": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/define-data-property": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
|
||||
"integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0",
|
||||
"es-errors": "^1.3.0",
|
||||
"gopd": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/destroy": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
|
||||
"integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
"integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
|
||||
},
|
||||
"node_modules/encodeurl": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
|
||||
"integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz",
|
||||
"integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.2.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/es-errors": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
|
||||
"integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
|
||||
},
|
||||
"node_modules/etag": {
|
||||
"version": "1.8.1",
|
||||
"resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
|
||||
"integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/express": {
|
||||
"version": "4.18.2",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz",
|
||||
"integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==",
|
||||
"dependencies": {
|
||||
"accepts": "~1.3.8",
|
||||
"array-flatten": "1.1.1",
|
||||
"body-parser": "1.20.1",
|
||||
"content-disposition": "0.5.4",
|
||||
"content-type": "~1.0.4",
|
||||
"cookie": "0.5.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"finalhandler": "1.2.0",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"merge-descriptors": "1.0.1",
|
||||
"methods": "~1.1.2",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"path-to-regexp": "0.1.7",
|
||||
"proxy-addr": "~2.0.7",
|
||||
"qs": "6.11.0",
|
||||
"range-parser": "~1.2.1",
|
||||
"safe-buffer": "5.2.1",
|
||||
"send": "0.18.0",
|
||||
"serve-static": "1.15.0",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"type-is": "~1.6.18",
|
||||
"utils-merge": "1.0.1",
|
||||
"vary": "~1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
"integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"content-type": "~1.0.4",
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"on-finished": "2.4.1",
|
||||
"qs": "6.11.0",
|
||||
"raw-body": "2.5.1",
|
||||
"type-is": "~1.6.18",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8",
|
||||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/express/node_modules/raw-body": {
|
||||
"version": "2.5.1",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz",
|
||||
"integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/finalhandler": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz",
|
||||
"integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"on-finished": "2.4.1",
|
||||
"parseurl": "~1.3.3",
|
||||
"statuses": "2.0.1",
|
||||
"unpipe": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/forwarded": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
|
||||
"integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/fresh": {
|
||||
"version": "0.5.2",
|
||||
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
|
||||
"integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/function-bind": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
|
||||
"integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-intrinsic": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz",
|
||||
"integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==",
|
||||
"dependencies": {
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"has-proto": "^1.0.1",
|
||||
"has-symbols": "^1.0.3",
|
||||
"hasown": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/gopd": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz",
|
||||
"integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==",
|
||||
"dependencies": {
|
||||
"get-intrinsic": "^1.1.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-property-descriptors": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
|
||||
"integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
|
||||
"dependencies": {
|
||||
"es-define-property": "^1.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz",
|
||||
"integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/has-symbols": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz",
|
||||
"integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==",
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/hasown": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz",
|
||||
"integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==",
|
||||
"dependencies": {
|
||||
"function-bind": "^1.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/http-errors": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
|
||||
"integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
|
||||
"dependencies": {
|
||||
"depd": "2.0.0",
|
||||
"inherits": "2.0.4",
|
||||
"setprototypeof": "1.2.0",
|
||||
"statuses": "2.0.1",
|
||||
"toidentifier": "1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
"integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
|
||||
"dependencies": {
|
||||
"safer-buffer": ">= 2.1.2 < 3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
},
|
||||
"node_modules/ipaddr.js": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
|
||||
"integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
"integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
"integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w=="
|
||||
},
|
||||
"node_modules/methods": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
|
||||
"integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
|
||||
"integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
|
||||
"bin": {
|
||||
"mime": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-types": {
|
||||
"version": "2.1.35",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
||||
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
||||
"dependencies": {
|
||||
"mime-db": "1.52.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
|
||||
},
|
||||
"node_modules/negotiator": {
|
||||
"version": "0.6.3",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||
"integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.1",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz",
|
||||
"integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==",
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/on-finished": {
|
||||
"version": "2.4.1",
|
||||
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
|
||||
"integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
|
||||
"dependencies": {
|
||||
"ee-first": "1.1.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/path-to-regexp": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
"integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
|
||||
"dependencies": {
|
||||
"forwarded": "0.2.0",
|
||||
"ipaddr.js": "1.9.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
"integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==",
|
||||
"dependencies": {
|
||||
"side-channel": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
"integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/raw-body": {
|
||||
"version": "2.5.2",
|
||||
"resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
|
||||
"integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
|
||||
"dependencies": {
|
||||
"bytes": "3.1.2",
|
||||
"http-errors": "2.0.0",
|
||||
"iconv-lite": "0.4.24",
|
||||
"unpipe": "1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
]
|
||||
},
|
||||
"node_modules/safer-buffer": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||
},
|
||||
"node_modules/send": {
|
||||
"version": "0.18.0",
|
||||
"resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz",
|
||||
"integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==",
|
||||
"dependencies": {
|
||||
"debug": "2.6.9",
|
||||
"depd": "2.0.0",
|
||||
"destroy": "1.2.0",
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"etag": "~1.8.1",
|
||||
"fresh": "0.5.2",
|
||||
"http-errors": "2.0.0",
|
||||
"mime": "1.6.0",
|
||||
"ms": "2.1.3",
|
||||
"on-finished": "2.4.1",
|
||||
"range-parser": "~1.2.1",
|
||||
"statuses": "2.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/send/node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
|
||||
},
|
||||
"node_modules/serve-static": {
|
||||
"version": "1.15.0",
|
||||
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
|
||||
"integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==",
|
||||
"dependencies": {
|
||||
"encodeurl": "~1.0.2",
|
||||
"escape-html": "~1.0.3",
|
||||
"parseurl": "~1.3.3",
|
||||
"send": "0.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/set-function-length": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.1.tgz",
|
||||
"integrity": "sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==",
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"get-intrinsic": "^1.2.3",
|
||||
"gopd": "^1.0.1",
|
||||
"has-property-descriptors": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/setprototypeof": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
|
||||
"integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
|
||||
},
|
||||
"node_modules/side-channel": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.5.tgz",
|
||||
"integrity": "sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ==",
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.6",
|
||||
"es-errors": "^1.3.0",
|
||||
"get-intrinsic": "^1.2.4",
|
||||
"object-inspect": "^1.13.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
"integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/toidentifier": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
|
||||
"integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
|
||||
"engines": {
|
||||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
"integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
|
||||
"dependencies": {
|
||||
"media-typer": "0.3.0",
|
||||
"mime-types": "~2.1.24"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
|
||||
"engines": {
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
"integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"body-parser": "^1.20.2",
|
||||
"express": "^4.18.2"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
## systemd (systemctl) Files
|
||||
This folder contains all the files required by systemctl on the server for updating and running the backend/frontend. These are copied onto the server and placed in the ```/etc/systemd/system``` folder.
|
||||
|
||||
## Files
|
||||
|Filename|Connected Service|
|
||||
|--------|-----------------|
|
||||
|frontend-next.service|Folder: frontend-next|
|
||||
|server_update_hook.service|Folder: backend/server_update_hook|
|
||||
@@ -1,19 +0,0 @@
|
||||
[Unit]
|
||||
Description=ChatMaps Frontend Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
WorkingDirectory=/root/ChatMaps/frontend-next
|
||||
ExecStartPre=/usr/bin/git pull
|
||||
ExecStartPre=/usr/bin/git checkout main
|
||||
ExecStartPre=/usr/bin/npm install
|
||||
ExecStartPre=/usr/bin/npm run build
|
||||
ExecStart=/usr/bin/npm run start
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,18 +0,0 @@
|
||||
[Unit]
|
||||
Description=ChatMaps Backend Server Updater / Restarter
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
WorkingDirectory=/root/ChatMaps/backend/server_update_hook
|
||||
ExecStartPre=/usr/bin/git pull
|
||||
ExecStartPre=/usr/bin/git checkout main
|
||||
ExecStartPre=/usr/bin/npm install
|
||||
ExecStart=/usr/bin/node api.js
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
|
After Width: | Height: | Size: 5.5 MiB |
|
After Width: | Height: | Size: 4.5 MiB |
|
After Width: | Height: | Size: 4.7 MiB |
@@ -1,3 +1,8 @@
|
||||
{
|
||||
"extends": "next/core-web-vitals"
|
||||
"extends": "next/core-web-vitals",
|
||||
"rules": {
|
||||
"no-unused-vars": ["warn", { "vars": "all", "args": "after-used", "ignoreRestSiblings": false }],
|
||||
"jsx-a11y/alt-text": "off",
|
||||
"@next/next/no-img-element": "off"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,9 +9,13 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"firebase": "^10.6.0",
|
||||
"next": "^14.1.0",
|
||||
"pigeon-maps": "^0.21.3",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0"
|
||||
"react-beforeunload": "^2.6.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hook-form": "^7.50.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^10.0.1",
|
||||
|
||||
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 964 B |
|
After Width: | Height: | Size: 63 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
After Width: | Height: | Size: 26 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>
|
||||
|
Before Width: | Height: | Size: 629 B |
@@ -0,0 +1,19 @@
|
||||
import { initializeApp, getApps, getApp } from "firebase/app";
|
||||
import { getAuth } from "firebase/auth";
|
||||
import { getDatabase} from "firebase/database"
|
||||
|
||||
var config = {
|
||||
apiKey: "AIzaSyDbDPjQGt-lIjNPeTG-Q5AECM1m0vtOr2c",
|
||||
authDomain: "chatmaps-3e7fa.firebaseapp.com",
|
||||
projectId: "chatmaps-3e7fa",
|
||||
storageBucket: "chatmaps-3e7fa.appspot.com",
|
||||
messagingSenderId: "771010649524",
|
||||
appId: "1:771010649524:web:b6e66d3457820c817b26e1",
|
||||
databaseURL: "https://chatmaps-3e7fa-default-rtdb.firebaseio.com/",
|
||||
}
|
||||
|
||||
var app = getApps().length > 0 ? getApp() : initializeApp(config);
|
||||
var auth = getAuth(app);
|
||||
var database = getDatabase(app);
|
||||
|
||||
export { auth, app, database };
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Inter } from "next/font/google";
|
||||
import "../globals.css";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata = {
|
||||
title: "ChatMaps: Home",
|
||||
description: "ChatMaps: Social Media for College Students",
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,292 @@
|
||||
"use client";
|
||||
// System Imports
|
||||
import { useState, useEffect } from "react";
|
||||
import { auth, database } from "../api/firebase-config";
|
||||
import { ref, onValue, set, remove, get } from "firebase/database";
|
||||
import { useBeforeunload } from "react-beforeunload";
|
||||
import {useRouter} from "next/navigation";
|
||||
import {Marker} from "pigeon-maps";
|
||||
import {onAuthStateChanged, signOut} from "firebase/auth"
|
||||
|
||||
// Refactored Component Imports
|
||||
// Data Structure Imports
|
||||
import { ChatRoomSidebar, Member } from "../../components/app/datatypes";
|
||||
|
||||
// Header Import
|
||||
import { Header } from "../../components/app/header";
|
||||
|
||||
// Main Tab Imports
|
||||
import { MainTabChatRoom } from "../../components/app/main_tab/chat";
|
||||
import { MainTabHome } from "../../components/app/main_tab/home";
|
||||
|
||||
// Sidebar Imports
|
||||
import {Home_Sidebar} from "../../components/app/sidebar/home";
|
||||
import { Chat_Sidebar } from "../../components/app/sidebar/chat";
|
||||
import { Profile_Sidebar } from "../../components/app/sidebar/profile";
|
||||
|
||||
// Contains most everything for the app homepage
|
||||
function Home() {
|
||||
// It's time to document and change these awful variable names
|
||||
// State variables for app page
|
||||
const [mainTab, setMainTab] = useState("home"); // Primary tab
|
||||
const [tab, setTab] = useState("nearby"); // Sidebar Tab
|
||||
const [chatRoomObj, setChatRoomObj] = useState(null); // Current chatroom object
|
||||
const [myRoomsObj, setMyRoomsObj] = useState(null); // My Rooms Object
|
||||
const [myRooms, setRoomData] = useState(null); // Current user saved rooms list
|
||||
const [isRoomLoading, setRoomLoading] = useState(true); // myRooms loading variable, true = myRooms loading, false = finished loading
|
||||
const [isMyRoom, setIsMyRoom] = useState(false); // Is current room in myRooms? true, false
|
||||
const [location, setLocation] = useState(null); // location variable [lat,long]
|
||||
const [loadingLoc, setLoadingLoc] = useState(true); // location variable loading, true = loading, false = finished loading
|
||||
const [nearby, setNearby] = useState(null); // nearby rooms array
|
||||
const [loadingNearby, setLoadingNearby] = useState(true); // loading nearby rooms array, true = loading, false = finished loading
|
||||
const [chatroomOnline, setChatRoomOnline] = useState(null); // holds online users
|
||||
const [chatroomUsers, setChatroomUsers] = useState(null); // holds all chatroom users
|
||||
const [chatroomUsersLoading, setChatroomUsersLoading] = useState(true);
|
||||
const [markers, setMarkers] = useState([]);
|
||||
const [isAuthenticated, setAuth] = useState(false)
|
||||
const [user, setUser] = useState(null)
|
||||
|
||||
// Authentication
|
||||
useEffect(() => {
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
if (user) {
|
||||
get(ref(database, `users/${user.uid}`))
|
||||
.then((userData) => {
|
||||
userData = userData.val()
|
||||
console.log(userData)
|
||||
if (userData) {
|
||||
setUser(userData)
|
||||
setAuth(true)
|
||||
} else {
|
||||
window.location.href = "/onboarding"
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
setAuth(false)
|
||||
window.location.href = "/login"
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
|
||||
// Grabs user data, saves to user, then lists the users saved rooms
|
||||
useEffect(() => {
|
||||
if (user) {
|
||||
onValue(ref(database, "/users/" + user.uid + "/rooms"), (snapshot) => {
|
||||
setRoomLoading(true);
|
||||
var rooms = snapshot.val();
|
||||
setMyRoomsObj(rooms);
|
||||
var roomArr = [];
|
||||
var markerArr = markers;
|
||||
for (var room in rooms) {
|
||||
var newRoom = (
|
||||
<ChatRoomSidebar
|
||||
roomObj={rooms[room]}
|
||||
key={rooms[room].timestamp}
|
||||
click={selectChatRoom}
|
||||
/>
|
||||
);
|
||||
markerArr.push(
|
||||
<Marker
|
||||
width={30}
|
||||
anchor={[rooms[room].latitude, rooms[room].longitude]}
|
||||
color="blue"
|
||||
/>
|
||||
);
|
||||
roomArr.push(newRoom);
|
||||
}
|
||||
setMarkers(markerArr);
|
||||
setRoomData(roomArr);
|
||||
setRoomLoading(false);
|
||||
});
|
||||
}
|
||||
},[user]);
|
||||
|
||||
// Grabs the user location
|
||||
useEffect(() => {
|
||||
if ("geolocation" in navigator && user) {
|
||||
// Retrieve latitude & longitude coordinates from `navigator.geolocation` Web API
|
||||
navigator.geolocation.getCurrentPosition(({ coords }) => {
|
||||
|
||||
setLocation(coords)
|
||||
setLoadingLoc(false)
|
||||
var path = String(coords.latitude.toFixed(2)).replace(".","")+"/"+String(coords.longitude.toFixed(2)).replace(".","")
|
||||
var markersArr = markers
|
||||
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
|
||||
var nearbyArr = []
|
||||
if (snapshot.exists()) {
|
||||
var data = snapshot.val();
|
||||
for (var room in data) {
|
||||
nearbyArr.push(
|
||||
<ChatRoomSidebar roomObj={data[room]} click={selectChatRoom} />
|
||||
);
|
||||
// TODO: RANDOM LAST DIGIT TO MOVE AROUND THE MAP
|
||||
markersArr.push(
|
||||
<Marker
|
||||
width={30}
|
||||
anchor={[data[room].latitude, data[room].longitude]}
|
||||
color="blue"
|
||||
/>
|
||||
);
|
||||
}
|
||||
setMarkers(markersArr);
|
||||
setLoadingNearby(false);
|
||||
setNearby(nearbyArr);
|
||||
} else {
|
||||
setLoadingNearby(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
},[user]);
|
||||
|
||||
|
||||
// Dont Double Send Leaving Message
|
||||
useEffect(() => {
|
||||
if (myRoomsObj && chatRoomObj) {
|
||||
var roomName = chatRoomObj.name + "-" + chatRoomObj.timestamp;
|
||||
if (myRooms != null && roomName in myRoomsObj) {
|
||||
// its in there
|
||||
setIsMyRoom(true);
|
||||
} else {
|
||||
// its not in there
|
||||
setIsMyRoom(false);
|
||||
}
|
||||
}
|
||||
}, [chatRoomObj]);
|
||||
|
||||
// Selects chat room
|
||||
function selectChatRoom(roomObj) {
|
||||
// Path of chatroom
|
||||
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
|
||||
|
||||
setChatRoomObj(roomObj);
|
||||
|
||||
// Send entered message
|
||||
var payload = {
|
||||
body: "entered",
|
||||
user: user.username,
|
||||
isSystem: true,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${path}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
|
||||
// Code for Room Data
|
||||
set(ref(database, `/rooms/${path}/users/online/${user.uid}`), user);
|
||||
onValue(ref(database, `/rooms/${path}`), (snapshot) => {
|
||||
setChatRoomOnline(null);
|
||||
setChatroomUsers(null);
|
||||
|
||||
// Active users list
|
||||
if (
|
||||
snapshot.val().hasOwnProperty("users") &&
|
||||
snapshot.val().users.hasOwnProperty("online")
|
||||
) {
|
||||
var activeUsers = [];
|
||||
var activeUsersJSON = snapshot.val().users.online;
|
||||
for (var user in activeUsersJSON)
|
||||
activeUsers.push(<Member memberObj={activeUsersJSON[user]} />);
|
||||
setChatRoomOnline(activeUsers);
|
||||
}
|
||||
|
||||
// Users who added to "my rooms"
|
||||
console.log(
|
||||
snapshot.val().hasOwnProperty("users") &&
|
||||
snapshot.val().users.hasOwnProperty("all")
|
||||
);
|
||||
if (
|
||||
snapshot.val().hasOwnProperty("users") &&
|
||||
snapshot.val().users.hasOwnProperty("all")
|
||||
) {
|
||||
setChatroomUsersLoading(true);
|
||||
var allUsers = [];
|
||||
var allUsersJSON = snapshot.val().users.all;
|
||||
for (var user in allUsersJSON)
|
||||
allUsers.push(<Member memberObj={allUsersJSON[user]} />);
|
||||
setChatroomUsers(allUsers);
|
||||
setChatroomUsersLoading(false);
|
||||
}
|
||||
});
|
||||
setMainTab("chat");
|
||||
}
|
||||
|
||||
// Fires to tell other uses that you are leaving the room
|
||||
useBeforeunload(() => {
|
||||
if (chatRoomObj && mainTab == "chat") {
|
||||
var payload = {
|
||||
body: "left",
|
||||
user: user.username,
|
||||
isSystem: true,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp
|
||||
}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
remove(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp
|
||||
}/users/online/${userID}`
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
{isAuthenticated && (
|
||||
<div className="grid grid-cols-4 auto-cols-max overflow-hidden">
|
||||
{/* Left Side of Page */}
|
||||
<div className="col-span-3 h-dvh">
|
||||
{/* Header */}
|
||||
<Header mainTab={mainTab} isMyRoom={isMyRoom} chatRoomObj={chatRoomObj} setChatRoomObj={setChatRoomObj} setMainTab={setMainTab} setIsMyRoom={setIsMyRoom} user={user}/>
|
||||
{/* Main Page Section */}
|
||||
<div className="mr-2 h-[calc(100%-110px)]">
|
||||
{mainTab == "home" && !loadingLoc && (
|
||||
<MainTabHome loc={location} markers={markers} user={user}/>
|
||||
)}
|
||||
{mainTab == "home" && loadingLoc && (
|
||||
<MainTabHome loc={null} markers={markers} user={user}/>
|
||||
)}
|
||||
{mainTab == "chat" && <MainTabChatRoom roomObj={chatRoomObj} user={user}/>}
|
||||
</div>
|
||||
</div>
|
||||
{/* Sidebar (Right Side of Page) */}
|
||||
{mainTab == "home" && (
|
||||
<Home_Sidebar tab={tab} nearby={nearby} loadingNearby={loadingNearby} setTab={setTab} isRoomLoading={isRoomLoading} myRooms={myRooms} loadingLoc={loadingLoc} location={location}/>
|
||||
)}
|
||||
{mainTab == "chat" && (
|
||||
<Chat_Sidebar chatRoomObj={chatRoomObj} chatroomOnline={chatroomOnline} chatroomUsersLoading={chatroomUsersLoading} chatroomUsers={chatroomUsers}/>
|
||||
)}
|
||||
{mainTab == "profile" && (
|
||||
<Profile_Sidebar/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Home;
|
||||
|
Before Width: | Height: | Size: 25 KiB |
@@ -2,17 +2,20 @@
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
main {
|
||||
body {
|
||||
background-color: aliceblue;
|
||||
text-align: center;
|
||||
text-wrap:pretty;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: rgb(205, 205, 205);
|
||||
background: #dee0e0;
|
||||
border-color: black;
|
||||
border: 5px;
|
||||
border-radius: 5px;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
filter: drop-shadow(0 25px 25px rgb(0 0 0 / 0.15));
|
||||
}
|
||||
|
||||
button:hover {
|
||||
@@ -23,5 +26,12 @@ input {
|
||||
border: 1px solid black;
|
||||
border-radius: 4px;
|
||||
padding: 10px 10px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
input.err {
|
||||
border: 2px solid red;
|
||||
border-radius: 4px;
|
||||
padding: 10px 10px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
@@ -5,13 +5,15 @@ const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata = {
|
||||
title: "ChatMaps",
|
||||
description: "ChatMaps: Social Media for College Students",
|
||||
description: "ChatMaps: Social Media for College Students"
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>{children}</body>
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Inter } from "next/font/google";
|
||||
import "../globals.css";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata = {
|
||||
title: "ChatMaps: Login",
|
||||
description: "ChatMaps: Social Media for College Students",
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
"use client";
|
||||
import { useForm, Form } from "react-hook-form";
|
||||
import { useRouter } from "next/navigation";
|
||||
import "../globals.css"
|
||||
|
||||
// Firebase imports
|
||||
import {auth} from "../api/firebase-config";
|
||||
import { setPersistence, signInWithEmailAndPassword, browserSessionPersistence } from "firebase/auth";
|
||||
|
||||
function Login() {
|
||||
var router = useRouter();
|
||||
//var { register, handleSubmit } = useForm();
|
||||
var { register, control, setError, handleSubmit, formState: { errors, isSubmitting, isSubmitted } } = useForm()
|
||||
|
||||
function authenticate(data) {
|
||||
setPersistence(auth, browserSessionPersistence)
|
||||
.then(() => {
|
||||
signInWithEmailAndPassword(auth,data.email,data.password)
|
||||
.then((userCredential) => {
|
||||
if (userCredential.user) {
|
||||
router.push("/app")
|
||||
} else {
|
||||
const formError = { type: "server", message: "Username or Password Incorrect" }
|
||||
// set same error in both:
|
||||
setError('password', formError)
|
||||
setError('email', formError)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<a href="/"><img src="logos/logo_transparent_inverse.png"/></a>
|
||||
<span className="text-[36px]">
|
||||
Chat with friends!
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="text-[24px] mt-[25px] mb-2">Login</h3>
|
||||
{(errors.email && errors.password) && <div className="text-[red] mb-2 text-[18px] text-bold">Invalid Email or Password.</div>}
|
||||
<Form onSubmit={handleSubmit(authenticate)} encType={'application/json'}
|
||||
control={control}
|
||||
>
|
||||
<input type="email" id="email" className={(errors.email && errors.password) && "err"} {...register("email", { required: true })} placeholder="Enter Email Address"/><br/>
|
||||
<input type="password" id="password" name="password" className={(errors.email && errors.password) && "err"} {...register("password", { required: true })} placeholder="Enter Password"/><br/>
|
||||
<button className="inline-flex items-center px-4 py-2 transition ease-in-out duration-150 bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">
|
||||
{(isSubmitting || isSubmitted) && <span className="inline-block">
|
||||
<svg class="animate-spin -ml-1 mr-3 h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" strokeWidth="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
</span> }
|
||||
Log In
|
||||
</button>
|
||||
<br/>Need an account? <a href="/register">Sign Up</a><br/>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Login;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Inter } from "next/font/google";
|
||||
import "../globals.css";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata = {
|
||||
title: "ChatMaps: Onboarding",
|
||||
description: "ChatMaps: Social Media for College Students"
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
"use client";
|
||||
import "../globals.css"
|
||||
import { useForm } from "react-hook-form";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { ref, set } from "firebase/database";
|
||||
import {auth, database} from "../api/firebase-config"
|
||||
import {onAuthStateChanged} from "firebase/auth"
|
||||
|
||||
function createUser(data) {
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
if (user.uid) {
|
||||
console.log(user)
|
||||
data.uid = user.uid
|
||||
data.defined = true
|
||||
data.email = user.email
|
||||
set(ref(database, `users/${user.uid}`), data);
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
function Onboarding() {
|
||||
var router = useRouter();
|
||||
var { register, handleSubmit } = useForm();
|
||||
|
||||
function Onboard(data) {
|
||||
createUser(data)
|
||||
router.push("/app");
|
||||
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<img src="logos/logo_transparent_inverse.png"/>
|
||||
<span className="text-[36px]">
|
||||
Chat with friends!
|
||||
</span>
|
||||
<div className="m-5">
|
||||
Welcome to ChatMaps! We are excited to have you join our community!<br/>First we just need a little bit of information from you to get started.
|
||||
</div>
|
||||
<form action="#" onSubmit={handleSubmit(Onboard)}>
|
||||
<input type="text" {...register("username")} placeholder="Display Name"/><br/>
|
||||
<input type="text" {...register("firstName")} placeholder="First Name"/><br/>
|
||||
<input type="text" {...register("lastName")} placeholder="Last Name"/><br/>
|
||||
<button type="submit" className="inline-flex items-center px-4 py-2 transition ease-in-out duration-150 bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Onboarding;
|
||||
@@ -1,27 +1,74 @@
|
||||
export default function Home() {
|
||||
return (
|
||||
<main className="flex min-h-screen flex-col justify-between p-24">
|
||||
<h1>Welcome to ChatMaps.</h1>
|
||||
"use client"
|
||||
import { useState, useEffect } from 'react'
|
||||
|
||||
import { auth, database } from "./api/firebase-config";
|
||||
import { ref, get} from "firebase/database";
|
||||
import {onAuthStateChanged} from "firebase/auth"
|
||||
|
||||
|
||||
<div id="room">
|
||||
<label>Room </label>
|
||||
<select>
|
||||
<option>Room 1</option>
|
||||
<option>Room 2</option>
|
||||
<option>Room 3</option>
|
||||
</select>
|
||||
<button>Join Room</button>
|
||||
</div>
|
||||
function Home() {
|
||||
const [isLoadingLoc, setLoadingLoc] = useState(true)
|
||||
const [roomCount, setRoomCount] = useState(null)
|
||||
const [isAuthenticated, setAuth] = useState(false)
|
||||
const [userID, setUserID] = useState(null)
|
||||
|
||||
// Authentication
|
||||
useEffect(() => {
|
||||
onAuthStateChanged(auth, (user) => {
|
||||
if (user) {
|
||||
setUserID(user.uid)
|
||||
setAuth(true)
|
||||
} else {
|
||||
setAuth(false)
|
||||
}
|
||||
})
|
||||
}, [])
|
||||
|
||||
<div id="message">
|
||||
<label>Enter a message</label>
|
||||
<input />
|
||||
<button>Send</button>
|
||||
</div>
|
||||
|
||||
|
||||
</main>
|
||||
);
|
||||
useEffect(() => {
|
||||
if('geolocation' in navigator) {
|
||||
// Retrieve latitude & longitude coordinates from `navigator.geolocation` Web API
|
||||
navigator.geolocation.getCurrentPosition(({ coords }) => {
|
||||
var path = String(coords.latitude.toFixed(2)).replace(".","")+"/"+String(coords.longitude.toFixed(2)).replace(".","")
|
||||
get(ref(database, `/rooms/${path}`)).then((snapshot) => {
|
||||
if (snapshot.exists()) {
|
||||
var count = 0
|
||||
for (var room in snapshot.val()) {
|
||||
count += 1
|
||||
}
|
||||
setRoomCount(count)
|
||||
} else {
|
||||
console.log("No rooms nearby")
|
||||
setRoomCount(0)
|
||||
}
|
||||
setLoadingLoc(false)
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<img src="logos/logo_transparent_inverse.png"/>
|
||||
<span className="text-[36px]">
|
||||
Chat with friends!
|
||||
</span>
|
||||
<div className="m-5">
|
||||
{(!isAuthenticated) &&
|
||||
<div>
|
||||
<a href="/login"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Login</button></a>
|
||||
<a href="/register"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Sign Up</button></a>
|
||||
</div>
|
||||
}
|
||||
{isAuthenticated && <a href="/app"><button className="bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">Continue to App</button></a>}
|
||||
{(!isLoadingLoc && roomCount == 1) && <div className='text-[24px] pt-10'>Join others in {roomCount} room near you!</div>}
|
||||
{(!isLoadingLoc && roomCount != 1 && roomCount != 0) && <div className='text-[24px] pt-10'>Join others in {roomCount} rooms near you!</div>}
|
||||
{(isLoadingLoc || (roomCount == 0 && !isLoadingLoc)) && <div className='text-[24px] pt-10'>Start the conversation today!</div>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home;
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Inter } from "next/font/google";
|
||||
import "../globals.css";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
export const metadata = {
|
||||
title: "ChatMaps: Register",
|
||||
description: "ChatMaps: Social Media for College Students",
|
||||
};
|
||||
|
||||
export default function RootLayout({ children }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<body className={inter.className}>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
"use client";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useForm, Form } from "react-hook-form";
|
||||
import "../globals.css"
|
||||
import { useState } from "react";
|
||||
|
||||
import { createUserWithEmailAndPassword, signInWithEmailAndPassword, setPersistence, browserSessionPersistence } from "firebase/auth";
|
||||
import {auth} from "../api/firebase-config";
|
||||
|
||||
async function Signup(data) {
|
||||
var userCredential = await createUserWithEmailAndPassword(auth,data.email,data.password);
|
||||
if (userCredential.user) {
|
||||
setPersistence(auth, browserSessionPersistence)
|
||||
.then(() => {
|
||||
signInWithEmailAndPassword(auth,data.email,data.password)
|
||||
.then((res) => {
|
||||
console.log(res)
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function Register() {
|
||||
var router = useRouter();
|
||||
var { register, control, formState: { errors } } = useForm()
|
||||
var emailRegex = /(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/
|
||||
var [passwordMismatch, setPasswordMismatch] = useState(false);
|
||||
const passwordMatch = (data) => {
|
||||
return data.password === data.passwordCheck;
|
||||
};
|
||||
|
||||
function onSubmit({data}) {
|
||||
if (passwordMatch(data)) {
|
||||
setPasswordMismatch(false);
|
||||
if (Signup(data)) {
|
||||
router.push("/onboarding");
|
||||
}
|
||||
|
||||
} else{
|
||||
setPasswordMismatch(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="grid h-screen place-items-center">
|
||||
<div>
|
||||
<a href="/"><img src="logos/logo_transparent_inverse.png"/></a>
|
||||
<span className="text-[36px]">
|
||||
Chat with friends!
|
||||
</span>
|
||||
<div>
|
||||
<h3 className="text-[24px] mt-[15px]">Register</h3>
|
||||
<Form onSubmit={onSubmit}
|
||||
encType={'application/json'}
|
||||
control={control}
|
||||
>
|
||||
<input type="email" {...register("email", {required: true, pattern: emailRegex})} className={errors.email && "err"} placeholder="Enter Email Address"/><br/>
|
||||
<input type="password" {...register("password", {required: true})} className={errors.password && errors.password.type == 'required' && "err"} placeholder="Enter Password"/><br/>
|
||||
<input type ="password" {...register("passwordCheck", {required: false})} className ={errors.passwordCheck && errors.passwordCheck.type == 'required' && "err"} placeholder="Re-enter Password"/><br/>
|
||||
{passwordMismatch && <p className="text-red-500">Passwords do not match</p>}
|
||||
<button type="submit" className="bg-[#dee0e0] m-5 bg-cyan-500 text-white font-bold py-2 px-4 rounded-full">
|
||||
Register</button><br/>
|
||||
Have an account? <a href="/login">Log In</a>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Register;
|
||||
@@ -0,0 +1,142 @@
|
||||
import { Map, Marker, ZoomControl } from "pigeon-maps";
|
||||
|
||||
// Colors for Messages
|
||||
const userColors = [
|
||||
"#ff80ed",
|
||||
"#065535",
|
||||
"#133337",
|
||||
"#ffc0cb",
|
||||
"#e6e6fa",
|
||||
"#ffd700",
|
||||
"#ffa500",
|
||||
"#0000ff",
|
||||
"#1b85b8",
|
||||
"#5a5255",
|
||||
"#559e83",
|
||||
"#ae5a41",
|
||||
"#c3cb71",
|
||||
];
|
||||
|
||||
// Chat Message
|
||||
export function Chat({ chatObj }) {
|
||||
let dateOptions = {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
};
|
||||
|
||||
const generateColor = (user_str) => {
|
||||
// hashes username for consistent colors, maybe all functionality to pick color later
|
||||
let hash = 0;
|
||||
for (let i = 0; i < user_str.length; i++) {
|
||||
hash = user_str.charCodeAt(i) + (hash * 32 - hash);
|
||||
}
|
||||
const index = Math.abs(hash) % userColors.length;
|
||||
return index;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
|
||||
<div>
|
||||
<span style={{ color: userColors[generateColor(chatObj.user)] }}>
|
||||
{chatObj.user}
|
||||
</span>
|
||||
: {chatObj.body}
|
||||
</div>
|
||||
<div className="text-right text-[#d1d1d1]">
|
||||
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// System Chat Message
|
||||
export function SystemMessage({ chatObj }) {
|
||||
let dateOptions = {
|
||||
weekday: "long",
|
||||
year: "numeric",
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
};
|
||||
|
||||
const generateColor = (user_str) => {
|
||||
// hashes username for consistent colors, maybe all functionality to pick color later
|
||||
let hash = 0;
|
||||
for (let i = 0; i < user_str.length; i++) {
|
||||
hash = user_str.charCodeAt(i) + (hash * 32 - hash);
|
||||
}
|
||||
const index = Math.abs(hash) % userColors.length;
|
||||
return index;
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="width-[100%] bg-white rounded-lg mt-1 text-left p-1 grid grid-cols-2 mr-2">
|
||||
<div className="text-[#d1d1d1]">
|
||||
<span style={{ color: userColors[generateColor(chatObj.user)] }}>
|
||||
{chatObj.user}
|
||||
</span>{" "}
|
||||
has {chatObj.body} the room.
|
||||
</div>
|
||||
<div className="text-right text-[#d1d1d1]">
|
||||
{new Date(chatObj.timestamp).toLocaleString(dateOptions)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Member for Active/Room members in sidebar
|
||||
export function Member({ memberObj }) {
|
||||
return (
|
||||
<div className="cursor-pointer g-[aliceblue] rounded-lg m-3 shadow-xl p-2">
|
||||
{memberObj.username}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Chat Room Object for myRooms and Nearby in sidebar
|
||||
export function ChatRoomSidebar({ roomObj, click }) {
|
||||
// TODO: Gross fix but it works
|
||||
function clicker() {
|
||||
click(roomObj);
|
||||
}
|
||||
return (
|
||||
<div
|
||||
onClick={clicker}
|
||||
className="border-[black] border-1 shadow-lg p-2 m-2 rounded-lg cursor-pointer"
|
||||
>
|
||||
<div className="col-span-2">
|
||||
<div className="font-bold">{roomObj.name}</div>
|
||||
<div className="italic">{roomObj.description}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Map module for main page and chat room sidebar
|
||||
// TODO: MAKE NOT MOVABLE
|
||||
export function Geo({ loc, zoom, locMarker, markers }) {
|
||||
if (loc) {
|
||||
return (
|
||||
<Map center={[loc.latitude, loc.longitude]} defaultZoom={zoom}>
|
||||
{markers && markers}
|
||||
{locMarker && (
|
||||
<Marker
|
||||
width={30}
|
||||
anchor={[loc.latitude, loc.longitude]}
|
||||
color="red"
|
||||
/>
|
||||
)}
|
||||
{zoom && <ZoomControl />}
|
||||
</Map>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<Map className="rounded-lg" defaultCenter={[0, 0]} defaultZoom={zoom} />
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
import { auth, database } from "../../app/api/firebase-config";
|
||||
import { ref, set, remove } from "firebase/database";
|
||||
import {signOut} from "firebase/auth";
|
||||
|
||||
function logout() {
|
||||
console.log("Fire")
|
||||
signOut(auth)
|
||||
}
|
||||
|
||||
// Closes chat room
|
||||
function closeChatRoom(roomObj, setChatRoomObj, setMainTab, user) {
|
||||
var path = roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp;
|
||||
var payload = {
|
||||
body: "left",
|
||||
user: user.username,
|
||||
isSystem: true,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${path}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
remove(ref(database, `/rooms/${path}/users/online/${user.uid}`));
|
||||
setChatRoomObj(null);
|
||||
setMainTab("home");
|
||||
}
|
||||
|
||||
// Adds room to myRooms
|
||||
function addToMyRooms(chatRoomObj, setIsMyRoom, user) {
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/users/${user.uid}/rooms/${chatRoomObj.name}-${chatRoomObj.timestamp}`
|
||||
),
|
||||
{
|
||||
name: chatRoomObj.name,
|
||||
path: chatRoomObj.path,
|
||||
timestamp: chatRoomObj.timestamp,
|
||||
description: chatRoomObj.description,
|
||||
longitude: chatRoomObj.longitude,
|
||||
latitude: chatRoomObj.latitude,
|
||||
}
|
||||
);
|
||||
var path =
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp;
|
||||
set(ref(database, `/rooms/${path}/users/all/${user.uid}`), user);
|
||||
setIsMyRoom(true);
|
||||
}
|
||||
|
||||
// Deletes saved room from myRooms
|
||||
function removeFromMyRooms(chatRoomObj, setIsMyRoom, user) {
|
||||
var path =
|
||||
chatRoomObj.path +
|
||||
"/" +
|
||||
chatRoomObj.name +
|
||||
"-" +
|
||||
chatRoomObj.timestamp;
|
||||
remove(
|
||||
ref(
|
||||
database,
|
||||
`/users/${user.uid}/rooms/${chatRoomObj.name}-${chatRoomObj.timestamp}`
|
||||
)
|
||||
);
|
||||
remove(ref(database, `/rooms/${path}/users/all/${user.uid}`));
|
||||
setIsMyRoom(false);
|
||||
}
|
||||
|
||||
export function Header({mainTab, isMyRoom, chatRoomObj, setChatRoomObj, setMainTab, setIsMyRoom, user}) {
|
||||
return (
|
||||
<div className="m-2 rounded-lg h-[63px] bg-white shadow-2xl grid grid-cols-2 p-1">
|
||||
<div className="h-[60px]">
|
||||
<a href="/">
|
||||
<img
|
||||
src="logos/logo_transparent_inverse.png"
|
||||
className="h-[60px]"
|
||||
/>
|
||||
</a>
|
||||
</div>
|
||||
<div className="h-[60px] p-4">
|
||||
{mainTab == "chat" && isMyRoom == false && (
|
||||
<a
|
||||
onClick={() => {
|
||||
addToMyRooms(chatRoomObj, setIsMyRoom, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5"
|
||||
>
|
||||
Add to "My Rooms"
|
||||
</a>
|
||||
)}
|
||||
{mainTab == "chat" && isMyRoom == true && (
|
||||
<a
|
||||
onClick={() => {
|
||||
removeFromMyRooms(chatRoomObj, setIsMyRoom, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5"
|
||||
>
|
||||
Remove from "My Rooms"
|
||||
</a>
|
||||
)}
|
||||
{mainTab == "chat" && (
|
||||
<a
|
||||
onClick={() => {
|
||||
closeChatRoom(chatRoomObj, setChatRoomObj, setMainTab, user);
|
||||
}}
|
||||
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5"
|
||||
>
|
||||
Close Chat
|
||||
</a>
|
||||
)}
|
||||
<a
|
||||
onClick={logout}
|
||||
href="/"
|
||||
className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full"
|
||||
>
|
||||
Sign Out
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import { Chat, SystemMessage} from "../datatypes"
|
||||
import { useState, useEffect } from "react";
|
||||
import { Form, useForm } from "react-hook-form";
|
||||
import { ref, onValue, set} from "firebase/database";
|
||||
import { database } from "../../../app/api/firebase-config";
|
||||
|
||||
|
||||
// Chatroom Module for Primary Tab
|
||||
export function MainTabChatRoom({ roomObj, user }) {
|
||||
var { register, control, reset, handleSubmit } = useForm();
|
||||
const [chats, setData] = useState(null);
|
||||
const [isLoading, setLoading] = useState(true);
|
||||
|
||||
// Message updater
|
||||
useEffect(() => {
|
||||
onValue(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp
|
||||
}/chats`
|
||||
),
|
||||
(snapshot) => {
|
||||
var chatsArr = [];
|
||||
var messages = snapshot.val();
|
||||
for (var message in messages) {
|
||||
if (messages[message].isSystem) {
|
||||
chatsArr.push(
|
||||
<SystemMessage
|
||||
chatObj={messages[message]}
|
||||
key={messages[message].timestamp}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
chatsArr.push(
|
||||
<Chat
|
||||
chatObj={messages[message]}
|
||||
key={messages[message].timestamp}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
setData(chatsArr.reverse());
|
||||
setLoading(false);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function sendMessage(data) {
|
||||
reset();
|
||||
var payload = {
|
||||
body: data.message,
|
||||
user: user.username,
|
||||
isSystem: false,
|
||||
timestamp: new Date().getTime(),
|
||||
};
|
||||
set(
|
||||
ref(
|
||||
database,
|
||||
`/rooms/${
|
||||
roomObj.path + "/" + roomObj.name + "-" + roomObj.timestamp
|
||||
}/chats/${new Date().getTime()}-${user.username}`
|
||||
),
|
||||
payload
|
||||
);
|
||||
}
|
||||
|
||||
if (isLoading) return <div>Loading</div>;
|
||||
if (!chats) return <div>No Chats</div>;
|
||||
return (
|
||||
<div className="m-1 h-[100%] rounded-lg">
|
||||
<div className="h-[90%] m-4 overflow-y-auto flex flex-col-reverse">
|
||||
{chats}
|
||||
</div>
|
||||
<div className="m-2 h-[10%] w-[100%] bg-white rounded-lg">
|
||||
<Form
|
||||
onSubmit={handleSubmit(sendMessage)}
|
||||
control={control}
|
||||
className="w-[100%] p-[0px]"
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
{...register("message")}
|
||||
placeholder="Enter message"
|
||||
className="w-[83%] border-[0px] mt-[8px] mb-[8px]"
|
||||
/>
|
||||
<button className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5 w-[8%]">
|
||||
Send
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import {Geo} from "../datatypes"
|
||||
|
||||
// Module for Welcome Message on main tab landing page
|
||||
function WelcomeMessage({user}) {
|
||||
return (
|
||||
<div className="bg-white rounded-lg m-2 mt-4 text-left p-2 pl-5">
|
||||
<div>
|
||||
Welcome, {user.firstName} {user.lastName} ({user.username})
|
||||
</div>
|
||||
<div>Lets see what's happening in your area.</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// Primary App Landing Page
|
||||
export function MainTabHome({ loc, markers, user }) {
|
||||
return (
|
||||
<>
|
||||
<WelcomeMessage user={user}/>
|
||||
<div className="h-[calc(100%-110px)] m-5 rounded-lg">
|
||||
<Geo
|
||||
loc={loc}
|
||||
zoom={14}
|
||||
movable={true}
|
||||
locMarker={true}
|
||||
markers={markers}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { Geo } from "../datatypes";
|
||||
|
||||
export function Chat_Sidebar({chatRoomObj, chatroomOnline, chatroomUsersLoading, chatroomUsers}) {
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className="m-2 h-[98%] grid grid-cols-1">
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl relative">
|
||||
<div className="w-[100%] h-[100%] opacity-50 absolute rounded-lg z-10">
|
||||
<Geo
|
||||
loc={{
|
||||
latitude: parseFloat(chatRoomObj.latitude.toFixed(2)),
|
||||
longitude: parseFloat(chatRoomObj.longitude.toFixed(2)),
|
||||
}}
|
||||
zoom={12}
|
||||
movable={false}
|
||||
marker={false}
|
||||
/>
|
||||
</div>
|
||||
<div className="z-10 top-0 left-0 w-[100%] h-[100%] absolute text-left pl-3 pt-2">
|
||||
<span className="font-bold text-[24px]">
|
||||
{chatRoomObj.name}
|
||||
</span>
|
||||
<br />
|
||||
{chatRoomObj.description}
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl">
|
||||
<div>Online Members</div>
|
||||
{chatroomOnline}
|
||||
</div>
|
||||
<div className="bg-white rounded-lg m-2 shadow-2xl">
|
||||
<div>All Members</div>
|
||||
{!chatroomUsersLoading && chatroomUsers}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
import { Form, useForm } from "react-hook-form";
|
||||
import { database } from "../../../app/api/firebase-config";
|
||||
import { ref, set } from "firebase/database";
|
||||
|
||||
// CreateRoom Module for Sidebar Create Tab
|
||||
function CreateRoom({ loc }) {
|
||||
var { register, control, reset, handleSubmit } = useForm();
|
||||
|
||||
function createRoom(data) {
|
||||
reset();
|
||||
var path =
|
||||
String(loc.latitude.toFixed(2)).replace(".", "") +
|
||||
"/" +
|
||||
String(loc.longitude.toFixed(2)).replace(".", "");
|
||||
var timestamp = new Date().getTime();
|
||||
var payload = {
|
||||
name: data.name,
|
||||
description: data.description,
|
||||
timestamp: timestamp,
|
||||
latitude: loc.latitude,
|
||||
longitude: loc.longitude,
|
||||
path: path,
|
||||
};
|
||||
set(ref(database, `/rooms/${path}/${data.name}-${timestamp}`), payload);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<Form control={control} onSubmit={handleSubmit(createRoom)}>
|
||||
<input
|
||||
{...register("name")}
|
||||
placeholder="Room Name"
|
||||
className="mt-2"
|
||||
/>
|
||||
<input
|
||||
{...register("description")}
|
||||
placeholder="Room Description"
|
||||
className="mt-2"
|
||||
/>
|
||||
<br />
|
||||
<div className="mt-3 mb-2">
|
||||
Creating room near ({loc.latitude.toFixed(2)},{" "}
|
||||
{loc.longitude.toFixed(2)})
|
||||
</div>
|
||||
<button className="p-2 cursor-pointer bg-[#dee0e0] bg-cyan-500 text-white font-bold rounded-full mr-5">
|
||||
Create
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Home_Sidebar({tab, nearby, loadingNearby, setTab, isRoomLoading, myRooms, loadingLoc, location}) {
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className="bg-white shadow-2xl rounded-lg m-2 h-[98%]">
|
||||
<div className="p-2">
|
||||
<div className="p-1 rounded-lg grid grid-cols-3 bg-white">
|
||||
<div
|
||||
className={
|
||||
tab == "nearby"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("nearby");
|
||||
}}
|
||||
>
|
||||
Nearby
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
tab == "rooms"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("rooms");
|
||||
}}
|
||||
>
|
||||
My Rooms
|
||||
</div>
|
||||
<div
|
||||
className={
|
||||
tab == "create"
|
||||
? "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0] bg-[#D3D3D3]"
|
||||
: "select-none p-1 cursor-pointer rounded-lg hover:bg-[#C0C0C0]"
|
||||
}
|
||||
onClick={() => {
|
||||
setTab("create");
|
||||
}}
|
||||
>
|
||||
Create
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{tab == "nearby" && (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<div>
|
||||
{!nearby && !loadingNearby && (
|
||||
<div>
|
||||
No Nearby Rooms
|
||||
<br />
|
||||
Create One?
|
||||
</div>
|
||||
)}
|
||||
{loadingNearby && <div>Loading...</div>}
|
||||
{nearby}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tab == "rooms" && (
|
||||
<div className="overflow-y-auto h-[90%]">
|
||||
<div>
|
||||
{isRoomLoading && <div>Loading</div>}
|
||||
{!myRooms && !isRoomLoading && <div>No User Saved Rooms</div>}
|
||||
{myRooms}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{tab == "create" && !loadingLoc && <CreateRoom loc={location} />}
|
||||
{tab == "create" && loadingLoc && <div>Loading...</div>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
export function Profile_Sidebar() {
|
||||
return (
|
||||
<div className="h-dvh">
|
||||
<div className=" bg-white m-2 h-[98%]">Profile</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -5,14 +5,5 @@ module.exports = {
|
||||
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
backgroundImage: {
|
||||
"gradient-radial": "radial-gradient(var(--tw-gradient-stops))",
|
||||
"gradient-conic":
|
||||
"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
};
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"name": "ChatMaps",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||