From 92cdcc6300778a60fc9af3d6081da6dc0a38d98f Mon Sep 17 00:00:00 2001 From: Lucas Bubner Date: Mon, 14 Aug 2023 22:06:13 +0930 Subject: [PATCH] Channel add/remove functionality --- src/App.tsx | 12 +-- src/Firebase.ts | 27 ++++--- src/chat/Chat.tsx | 33 ++++---- src/chat/FileUploads.tsx | 20 ++--- src/chat/Login.tsx | 20 ++--- src/chat/Message.tsx | 12 +-- src/chat/MessageBar.tsx | 12 ++- src/chat/Msgman.tsx | 22 +++--- src/css/Channels.css | 8 +- src/css/index.css | 2 +- src/index.tsx | 2 +- src/layout/Channels.tsx | 43 +++++++---- src/layout/Navbar.tsx | 18 ++--- src/layout/Scroll.tsx | 4 +- src/windows/About.tsx | 98 ++++++++++++------------ src/windows/Admin.tsx | 16 ++-- src/windows/BBQ.tsx | 22 +++--- src/windows/MDTable.tsx | 158 +++++++++++++++++++-------------------- src/windows/Users.tsx | 6 +- 19 files changed, 277 insertions(+), 258 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f9e536a..482f27b 100755 --- a/src/App.tsx +++ b/src/App.tsx @@ -7,10 +7,10 @@ import "./css/App.css"; // Firebase imports and configuration -import {useEffect, useState} from "react"; -import {useAuthState} from "react-firebase-hooks/auth"; -import {auth, db, useAuthStateChanged} from "./Firebase"; -import {onValue, ref} from "firebase/database"; +import { useEffect, useState } from "react"; +import { useAuthState } from "react-firebase-hooks/auth"; +import { auth, db, useAuthStateChanged } from "./Firebase"; +import { onValue, ref } from "firebase/database"; // Import application login and chatroom windows import Chat from "./chat/Chat"; @@ -54,7 +54,7 @@ function App() { }, [online]); return online ? ( -
{user ? : }
+
{user ? : }
) : ( <>
@@ -63,7 +63,7 @@ function App() {
{longConnect && (

- This seems to be taking a while.
Please check your internet connection. + This seems to be taking a while.
Please check your internet connection.

)} diff --git a/src/Firebase.ts b/src/Firebase.ts index f09c5ad..36dc0cd 100755 --- a/src/Firebase.ts +++ b/src/Firebase.ts @@ -5,9 +5,9 @@ */ // https://firebase.google.com/docs/web/setup#available-libraries -import {useEffect} from "react"; -import {getApp, initializeApp} from "firebase/app"; -import {getAuth, GoogleAuthProvider, onAuthStateChanged, signInWithPopup} from "firebase/auth"; +import { useEffect } from "react"; +import { getApp, initializeApp } from "firebase/app"; +import { getAuth, GoogleAuthProvider, onAuthStateChanged, signInWithPopup } from "firebase/auth"; import { child, get, @@ -21,8 +21,8 @@ import { set, update, } from "firebase/database"; -import {deleteObject, getStorage, listAll, ref as sref} from "firebase/storage"; -import {getFileURL} from "./chat/Message"; +import { deleteObject, getStorage, listAll, ref as sref } from "firebase/storage"; +import { getFileURL } from "./chat/Message"; let app; @@ -38,7 +38,7 @@ try { databaseURL: "https://bunyipbellower-default-rtdb.asia-southeast1.firebasedatabase.app", storageBucket: "bunyipbellower.appspot.com", messagingSenderId: "1021407241018", - appId: "1:1021407241018:web:381d79b51804b683166603" + appId: "1:1021407241018:web:381d79b51804b683166603", }; app = initializeApp(firebaseConfig); } @@ -50,10 +50,15 @@ const db = getDatabase(app); // Store what channel the user is currently in export let currentChannel = "main"; + export function setCurrentChannel(channel: string): void { currentChannel = channel; } +export async function removeChannel(channel: string): Promise { + return await remove(ref(db, `messages/${channel}`)); +} + // Define structure of user data from Firebase export interface UserData { email: string; // Primary key @@ -63,8 +68,8 @@ export interface UserData { online: | boolean | { - lastseen: number; - }; + lastseen: number; + }; uid: string | undefined; name: string | undefined; pfp: string | undefined; @@ -97,8 +102,8 @@ function errorHandler(err: any): void { } else { alert( "Sorry! An error occurred attempting to perform the operation you were requesting. Error message:\n\n" + - err + - "\n\nYour window will be reloaded in 5 seconds." + err + + "\n\nYour window will be reloaded in 5 seconds." ); } @@ -362,4 +367,4 @@ export async function clearDatabases(): Promise { }); } -export {auth, db, storage}; +export { auth, db, storage }; diff --git a/src/chat/Chat.tsx b/src/chat/Chat.tsx index 2dff4b1..2cf4f2b 100644 --- a/src/chat/Chat.tsx +++ b/src/chat/Chat.tsx @@ -5,13 +5,13 @@ * @author Lachlan Paul, 2023 */ -import {auth, db, getData, MessageData, toCommas, currentChannel} from "../Firebase"; -import {createRef, useEffect, useRef, useState} from "react"; -import {onValue, ref} from "firebase/database"; +import { auth, db, getData, MessageData, toCommas, currentChannel } from "../Firebase"; +import { createRef, useEffect, useRef, useState } from "react"; +import { onValue, ref } from "firebase/database"; import Message from "./Message"; import Navbar from "../layout/Navbar"; import MessageBar from "./MessageBar"; -import Channels from "../layout/Channels" +import Channels from "../layout/Channels"; import "../css/Chat.css"; function Chat() { @@ -26,7 +26,7 @@ function Chat() { function updatePagination() { setPaginationIndex((prev) => prev + 1); - pdummy.current?.scrollIntoView({behavior: "auto"}); + pdummy.current?.scrollIntoView({ behavior: "auto" }); } useEffect(() => { @@ -91,7 +91,7 @@ function Chat() { const lastMessageTimestamp = lastMessageObject.createdAt; // Check if the last message has not been seen yet if (lastMessage.current !== lastMessageTimestamp) { - dummy.current.scrollIntoView({behavior: "auto"}); + dummy.current.scrollIntoView({ behavior: "auto" }); if (lastMessageTimestamp > lastSeenTimestampRef.current) { // Enable notifications if they are not on the page setNewMessage(true); @@ -154,21 +154,22 @@ function Chat() { {authorised && ( <> {/* Navbar element with profile information */} - +
- {/* Menu element for changing channels */} - + {/* Menu element for changing channels */} + {/* Allow space for Navbar to fit */} -




+




{/* Load more button to support pagination */} {messages && Object.keys(messages).length > paginationIndex * PAGINATION_LIMIT ? ( -
)} diff --git a/src/chat/FileUploads.tsx b/src/chat/FileUploads.tsx index 4b010a9..7265cc5 100755 --- a/src/chat/FileUploads.tsx +++ b/src/chat/FileUploads.tsx @@ -3,11 +3,11 @@ * @author Lucas Bubner, 2023 */ -import {ChangeEventHandler, ClipboardEvent, useCallback, useEffect, useRef, useState} from "react"; -import {storage, uploadFileMsg} from "../Firebase"; -import {getDownloadURL, ref, uploadBytesResumable, UploadTask, UploadTaskSnapshot} from "firebase/storage"; +import { ChangeEventHandler, ClipboardEvent, useCallback, useEffect, useRef, useState } from "react"; +import { storage, uploadFileMsg } from "../Firebase"; +import { getDownloadURL, ref, uploadBytesResumable, UploadTask, UploadTaskSnapshot } from "firebase/storage"; import Popup from "reactjs-popup"; -import {PopupActions} from "reactjs-popup/dist/types"; +import { PopupActions } from "reactjs-popup/dist/types"; import "../css/FileUploads.css"; import "../css/CommonPopup.css"; @@ -152,7 +152,7 @@ function FileUploads() { const tclose = () => tref.current?.close(); return ( - } onClose={resetElement}> + } onClose={resetElement}>
@@ -162,7 +162,7 @@ function FileUploads() { {isFileUploaded ? (
File uploaded.
) : !isClipboard ? ( - + ) : (
File supplied by message box clipboard paste. @@ -170,10 +170,10 @@ function FileUploads() { )} {isFilePicked && selectedFile != null && !isFileUploaded && (
-
+

- File name: {selectedFile.name}
- Filetype: {selectedFile.type || "unknown"}
+ File name: {selectedFile.name}
+ Filetype: {selectedFile.type || "unknown"}
Size in bytes: {formatBytes(selectedFile.size)}

@@ -186,7 +186,7 @@ function FileUploads() {

)} -
+
{isFileUploading && !isFileUploaded && (
diff --git a/src/chat/Login.tsx b/src/chat/Login.tsx index 70749bb..36b29f3 100755 --- a/src/chat/Login.tsx +++ b/src/chat/Login.tsx @@ -5,7 +5,7 @@ * @author Lachlan Paul, 2023 */ -import {signInWithGoogle} from "../Firebase"; +import { signInWithGoogle } from "../Firebase"; import "../css/Login.css"; function Login() { @@ -24,27 +24,27 @@ function Login() {
  • - Background animation + Background animation
    - Murray Bridge Bunyip club + Murray Bridge Bunyip club

    Welcome to the

    Bunyip Bellower

    DEVELOPER-ONLY RESTRICTED -

    +

    -

    +

    - Application developed by
    - Lucas Bubner @hololb
    - Lachlan Paul @BanjoTheBot
    + Application developed by
    + Lucas Bubner @hololb
    + Lachlan Paul @BanjoTheBot
    -
    +
    - Copyright (c) Lucas Bubner, Lachlan Paul, 2023
    + Copyright (c) Lucas Bubner, Lachlan Paul, 2023
    Source code
    diff --git a/src/chat/Message.tsx b/src/chat/Message.tsx index 8659e6f..bdc5eb1 100644 --- a/src/chat/Message.tsx +++ b/src/chat/Message.tsx @@ -5,8 +5,8 @@ * @author Lachlan Paul, 2023 */ -import {useMemo, useState} from "react"; -import {auth, MessageData} from "../Firebase"; +import { useMemo, useState } from "react"; +import { auth, MessageData } from "../Firebase"; import Msgman from "./Msgman"; import "../css/App.css"; import "../css/Message.css"; @@ -22,10 +22,10 @@ export const getFileURL = (fileURL: string) => { return fileURL.slice(fileURL.indexOf(":") + 1); }; -const filter = new Filter({placeHolder: "♥"}); +const filter = new Filter({ placeHolder: "♥" }); function Message(props: { message: MessageData; key: string }) { - const {message} = props; + const { message } = props; const [isHovering, setIsHovering] = useState(false); const handleMouseOver = () => setIsHovering(true); const handleMouseOut = () => setIsHovering(false); @@ -74,7 +74,7 @@ function Message(props: { message: MessageData; key: string }) { {/* Display the proper formatted date and time metadata with each message */} {message.photoURL !== "sys" && ( -

    {timestamp.toLocaleString("en-AU", {hour12: true})}

    +

    {timestamp.toLocaleString("en-AU", { hour12: true })}

    )}
    @@ -137,7 +137,7 @@ function Message(props: { message: MessageData; key: string }) { <message deleted>

    )} - {message.photoURL !== "sys" && } + {message.photoURL !== "sys" && }
    ); } diff --git a/src/chat/MessageBar.tsx b/src/chat/MessageBar.tsx index 3a1daed..0510fa2 100644 --- a/src/chat/MessageBar.tsx +++ b/src/chat/MessageBar.tsx @@ -5,10 +5,10 @@ */ import "../css/MessageBar.css"; -import {ChangeEvent, FormEvent, useEffect, useState} from "react"; +import { ChangeEvent, FormEvent, useEffect, useState } from "react"; import FileUploads from "./FileUploads"; import Scroll from "../layout/Scroll"; -import {auth, getData, isMessageOverLimit, toCommas, uploadMsg, UserData} from "../Firebase"; +import { auth, getData, isMessageOverLimit, toCommas, uploadMsg, UserData } from "../Firebase"; function MessageBar() { const [formVal, setFormVal] = useState(""); @@ -61,7 +61,7 @@ function MessageBar() {
    {writePerms ? ( <> - + handleMessageChange(e)} @@ -77,14 +77,12 @@ function MessageBar() { ) : (
    -

    - You do not have permission to send any messages. -

    +

    You do not have permission to send any messages.

    )}
    - +
    ); } diff --git a/src/chat/Msgman.tsx b/src/chat/Msgman.tsx index db721a2..abdad3a 100644 --- a/src/chat/Msgman.tsx +++ b/src/chat/Msgman.tsx @@ -5,13 +5,13 @@ import "../css/Msgman.css"; import "../css/CommonPopup.css"; -import {useEffect, useRef, useState} from "react"; -import {auth, deleteMsg, getData, MessageData, toCommas, updateMsg, UserData} from "../Firebase"; -import {getFileURL} from "./Message"; +import { useEffect, useRef, useState } from "react"; +import { auth, deleteMsg, getData, MessageData, toCommas, updateMsg, UserData } from "../Firebase"; +import { getFileURL } from "./Message"; import Popup from "reactjs-popup"; -import {PopupActions} from "reactjs-popup/dist/types"; +import { PopupActions } from "reactjs-popup/dist/types"; -function Msgman({id, isActive}: { id: string; isActive: boolean }) { +function Msgman({ id, isActive }: { id: string; isActive: boolean }) { const [shouldDisplay, setShouldDisplay] = useState(false); const tref = useRef(null); const tclose = () => tref.current?.close(); @@ -98,7 +98,7 @@ function Msgman({id, isActive}: { id: string; isActive: boolean }) { return ( } + trigger={ -
    +
    -
    +
    )} {(isAdmin || isAuthorised) && !isRetracted && ( <> -
    +
    )} -
    +
    diff --git a/src/css/Channels.css b/src/css/Channels.css index 841947f..cd55250 100644 --- a/src/css/Channels.css +++ b/src/css/Channels.css @@ -3,7 +3,7 @@ * Author: Lachlan Paul, 2023 */ -.menu{ +.menu { position: fixed; background: rgb(0, 52, 14); height: 100%; @@ -11,7 +11,7 @@ z-index: 1; } -.buttons{ +.buttons { height: 50px; width: 90%; border: none; @@ -21,6 +21,6 @@ margin: 7px; } -.buttons:hover{ +.buttons:hover { background-color: rgb(70, 170, 10); -} \ No newline at end of file +} diff --git a/src/css/index.css b/src/css/index.css index d26f2b7..f303c75 100755 --- a/src/css/index.css +++ b/src/css/index.css @@ -9,7 +9,7 @@ body { background-color: #2c3032; color: #ffffff; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", - "Droid Sans", "Helvetica Neue", sans-serif; + "Droid Sans", "Helvetica Neue", sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } diff --git a/src/index.tsx b/src/index.tsx index 2b4395e..09990cc 100755 --- a/src/index.tsx +++ b/src/index.tsx @@ -12,6 +12,6 @@ import App from "./App"; const root = ReactDOM.createRoot(document.getElementById("root")!); root.render( - + ); diff --git a/src/layout/Channels.tsx b/src/layout/Channels.tsx index 29ba5fb..0ade176 100644 --- a/src/layout/Channels.tsx +++ b/src/layout/Channels.tsx @@ -5,31 +5,46 @@ */ import "../css/Channels.css"; -import {setCurrentChannel} from "../Firebase"; +import { Fragment, useState, useEffect } from "react"; +import { onValue, ref } from "firebase/database"; +import { setCurrentChannel, db, removeChannel } from "../Firebase"; function Channels() { + const [channels, setChannels] = useState>([]); + useEffect(() => { + const unsubscribe = onValue(ref(db, "messages"), (snapshot) => { + const data = snapshot.val(); + if (data) { + setChannels(Object.keys(data)); + } + }); + return () => unsubscribe(); + }, []); return ( - <> -
    +
  • - - +
    { e.preventDefault(); setCurrentChannel((document.getElementById("channelName") as HTMLInputElement).value); (document.getElementById("channelName") as HTMLInputElement).value = ""; }}> + +
    + {channels.map((channel) => ( + + + + + ))}
    - ); - } +} - // New channels must be added manually - /** - * main: Bunyips Robotics Club - * sec: Test Channel - */ - const channels: Array = ["main", "sec"]; -export default Channels; \ No newline at end of file +export default Channels; diff --git a/src/layout/Navbar.tsx b/src/layout/Navbar.tsx index ef4f7ac..3683c56 100644 --- a/src/layout/Navbar.tsx +++ b/src/layout/Navbar.tsx @@ -5,9 +5,9 @@ */ import "../css/Navbar.css"; -import {auth, db, signOut, startMonitoring, UserData, validateUsers} from "../Firebase"; -import {onValue, ref} from "firebase/database"; -import {useEffect, useState} from "react"; +import { auth, db, signOut, startMonitoring, UserData, validateUsers } from "../Firebase"; +import { onValue, ref } from "firebase/database"; +import { useEffect, useState } from "react"; import BBQ from "../windows/BBQ"; import Users from "../windows/Users"; @@ -28,7 +28,7 @@ function Navbar() { const date = new Date(); const timer = setInterval(() => { date.setTime(Date.now()); - setCurrentTime(date.toLocaleTimeString("en-AU", {hour12: true})); + setCurrentTime(date.toLocaleTimeString("en-AU", { hour12: true })); }, 1000); return () => { clearInterval(timer); @@ -81,14 +81,14 @@ function Navbar() { alt={`Profile of ${auth.currentUser?.displayName}`} />

    {auth.currentUser?.displayName}

    - await signOut()}/> - - + await signOut()} /> + +

    Bunyip Bellower

    - Lucas Bubner, Lachlan Paul, 2023 -
    + Lucas Bubner, Lachlan Paul, 2023 +
    {currentTime ? currentTime : "..."}, {userData ? onlineUsers.length + " user(s) online" : "..."}

    diff --git a/src/layout/Scroll.tsx b/src/layout/Scroll.tsx index 26eb95a..ebddf51 100644 --- a/src/layout/Scroll.tsx +++ b/src/layout/Scroll.tsx @@ -3,7 +3,7 @@ * @author Lucas Bubner, 2023 */ -import {useEffect, useState} from "react"; +import { useEffect, useState } from "react"; import "../css/Scroll.css"; function Scroll() { @@ -26,7 +26,7 @@ function Scroll() { }); } - return <>{showBtn && } nested> <> -
    +

    The Bunyip Bellower Project

    @@ -23,57 +23,59 @@ function About() {

    - - - - - - - - - - - - + + + + + + + + + + + +
    - Profile of Lucas Bubner - Lucas Bubner - - @hololb - - Lead programmer and application backend manager
    - Profile of Lachlan Paul - Lachlan Paul - - @BanjoTheBot - - Application and frontend designer
    + Profile of Lucas Bubner + Lucas Bubner + + @hololb + + Lead programmer and application backend manager
    + Profile of Lachlan Paul + Lachlan Paul + + @BanjoTheBot + + Application and frontend designer
    -
    +
    GitHub source window.open("https://github.com/Murray-Bridge-Bunyips/BunyipBellower", "_blank")} + onClick={() => + window.open("https://github.com/Murray-Bridge-Bunyips/BunyipBellower", "_blank") + } />   ||   window.open("https://getbootstrap.com/", "_blank")} />
    -
    +
    Copyright (c) Lucas Bubner, Lachlan Paul, 2023 under the{" "} <> -
    +
    {isAdmin ? (
    @@ -174,7 +174,7 @@ function Admin() { })}
    -
    +
    @@ -190,9 +190,7 @@ function Admin() { × -

    - Insufficient permissions to access the admin module. -

    +

    Insufficient permissions to access the admin module.

    )}
    diff --git a/src/windows/BBQ.tsx b/src/windows/BBQ.tsx index 71fb037..ab70e84 100644 --- a/src/windows/BBQ.tsx +++ b/src/windows/BBQ.tsx @@ -3,9 +3,9 @@ * @author Lachlan Paul, 2023 */ -import {useRef} from "react"; +import { useRef } from "react"; import Popup from "reactjs-popup"; -import {PopupActions} from "reactjs-popup/dist/types"; +import { PopupActions } from "reactjs-popup/dist/types"; import Admin from "./Admin"; // I could go for a import "../css/BBQ.css"; // bacon burger @@ -18,26 +18,26 @@ function BBQ() { const tclose = () => tref.current?.close(); return ( - } nested> + } nested> <> -
    +
    - +
    -
    +
    - +
    -
    +
    -
    +
    - +
    -
    +
    diff --git a/src/windows/MDTable.tsx b/src/windows/MDTable.tsx index e74eec1..21bcc06 100644 --- a/src/windows/MDTable.tsx +++ b/src/windows/MDTable.tsx @@ -4,9 +4,9 @@ * @author Lachlan Paul, 2023 */ -import {useRef} from "react"; +import { useRef } from "react"; import Popup from "reactjs-popup"; -import {PopupActions} from "reactjs-popup/dist/types"; +import { PopupActions } from "reactjs-popup/dist/types"; import "../css/CommonPopup.css"; import ReactMarkdown from "react-markdown"; import gfm from "remark-gfm"; @@ -17,90 +17,90 @@ function MDTable() { return ( Markdown Commands} nested> <> -
    +
    - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    ElementWhen typing messageResult
    ElementWhen typing messageResult
    Heading 1# Big Text - # Big Text -
    Heading 2## Slightly Less Big Text - ## Slightly Less Big Text -
    Heading 3### Less Big Text - ### Less Big Text -
    Heading 4#### Even Less Big Text - #### Even Less Big Text -
    Heading 5##### Even Even Less Big Text - ##### Even Even Less Big Text -
    Heading 6###### Even Even Even Less Big Text - ###### Even Even Even Less Big Text -
    Bold LettersA **bold** word - A **bold** word -
    ItalicsBut *maybe* - But *maybe* -
    Code Block`print("Hello World!")` - ```print("Hello World!")``` -
    Strikethrough~~Help~~ - ~~Help~~ -
    Heading 1# Big Text + # Big Text +
    Heading 2## Slightly Less Big Text + ## Slightly Less Big Text +
    Heading 3### Less Big Text + ### Less Big Text +
    Heading 4#### Even Less Big Text + #### Even Less Big Text +
    Heading 5##### Even Even Less Big Text + ##### Even Even Less Big Text +
    Heading 6###### Even Even Even Less Big Text + ###### Even Even Even Less Big Text +
    Bold LettersA **bold** word + A **bold** word +
    ItalicsBut *maybe* + But *maybe* +
    Code Block`print("Hello World!")` + ```print("Hello World!")``` +
    Strikethrough~~Help~~ + ~~Help~~ +
    -
    +

    Any invalid text/markdown will be converted to "Gracious Professionalism!"

    diff --git a/src/windows/Users.tsx b/src/windows/Users.tsx index dcafde2..ac81c12 100644 --- a/src/windows/Users.tsx +++ b/src/windows/Users.tsx @@ -2,10 +2,10 @@ * User presence manager for client information, and user count dialogues. * @author Lucas Bubner, 2023 */ -import {useRef} from "react"; -import {auth, toDots, UserData} from "../Firebase"; +import { useRef } from "react"; +import { auth, toDots, UserData } from "../Firebase"; import Popup from "reactjs-popup"; -import {PopupActions} from "reactjs-popup/dist/types"; +import { PopupActions } from "reactjs-popup/dist/types"; import "../css/Users.css"; import "../css/CommonPopup.css";