From aa1dd93c4151245482b2bbe2f3e6c9818ef047ad Mon Sep 17 00:00:00 2001 From: bsourisse Date: Fri, 3 Mar 2023 11:53:55 +0100 Subject: [PATCH 01/24] ux: rename category to prepare Apprise integration --- .../UserSettings/EmailAlertSettings/EmailAlertSettings.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Containers/UserSettings/EmailAlertSettings/EmailAlertSettings.js b/Containers/UserSettings/EmailAlertSettings/EmailAlertSettings.js index 3781162..c51fdf2 100644 --- a/Containers/UserSettings/EmailAlertSettings/EmailAlertSettings.js +++ b/Containers/UserSettings/EmailAlertSettings/EmailAlertSettings.js @@ -10,7 +10,7 @@ import { SpinnerCircularFixed } from 'spinners-react'; import Error from '../../../Components/UI/Error/Error'; import Switch from '../../../Components/UI/Switch/Switch'; -export default function EmailAlertSettings(props) { +export default function EmailAlertSettings() { //Var const toastOptions = { position: 'top-right', @@ -111,7 +111,7 @@ export default function EmailAlertSettings(props) { {/* EMAIL ALERT */}
-

Alerting

+

Email alert

@@ -127,7 +127,7 @@ export default function EmailAlertSettings(props) { onChangeSwitchHandler({ emailAlert: e }) From c5267fba0308825ae8a6a6e2771e31b8eea94549 Mon Sep 17 00:00:00 2001 From: bsourisse Date: Fri, 3 Mar 2023 12:25:54 +0100 Subject: [PATCH 02/24] feat: create the apprise category in user settings --- .../AppriseAlertSettings.js | 78 +++++++++++++++++++ Containers/UserSettings/UserSettings.js | 2 + .../UserSettings/UserSettings.module.css | 8 ++ 3 files changed, 88 insertions(+) create mode 100644 Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js diff --git a/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js b/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js new file mode 100644 index 0000000..0d0389e --- /dev/null +++ b/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js @@ -0,0 +1,78 @@ +//Lib +import { useEffect } from 'react'; +import { toast } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; +import classes from '../UserSettings.module.css'; +import { useState } from 'react'; +import { SpinnerCircularFixed } from 'spinners-react'; + +//Components +import Error from '../../../Components/UI/Error/Error'; +import Switch from '../../../Components/UI/Switch/Switch'; + +export default function AppriseAlertSettings() { + return ( + <> + {/* APPRISE ALERT */} +
+
+

Apprise alert

+
+
+
+ + // onChangeSwitchHandler({ emailAlert: e }) + // } + /> +
+
+ Apprise URLs +
+ +
+
+ Use{' '} + + Apprise URLs + {' '} + to send a notification to any service. +
+ +
+
+
+ + ); +} diff --git a/Containers/UserSettings/UserSettings.js b/Containers/UserSettings/UserSettings.js index c324aa0..0b82d94 100644 --- a/Containers/UserSettings/UserSettings.js +++ b/Containers/UserSettings/UserSettings.js @@ -7,6 +7,7 @@ import EmailSettings from './EmailSettings/EmailSettings'; import PasswordSettings from './PasswordSettings/PasswordSettings'; import UsernameSettings from './UsernameSettings/UsernameSettings'; import EmailAlertSettings from './EmailAlertSettings/EmailAlertSettings'; +import AppriseAlertSettings from './AppriseAlertSettings/AppriseAlertSettings'; export default function UserSettings(props) { return ( @@ -23,6 +24,7 @@ export default function UserSettings(props) { +
); } diff --git a/Containers/UserSettings/UserSettings.module.css b/Containers/UserSettings/UserSettings.module.css index f0caa71..1c6ab20 100644 --- a/Containers/UserSettings/UserSettings.module.css +++ b/Containers/UserSettings/UserSettings.module.css @@ -59,6 +59,14 @@ width: 100%; height: auto; color: #494b7a; + font-family: var( + --pure-material-font, + 'Roboto', + 'Segoe UI', + BlinkMacSystemFont, + system-ui, + -apple-system + ); } .bwFormWrapper p { From 6da2d97787ca3d9a02462c273d99d0899e778ecb Mon Sep 17 00:00:00 2001 From: bsourisse Date: Fri, 3 Mar 2023 15:39:50 +0100 Subject: [PATCH 03/24] ui: improve user settings design --- Containers/UserSettings/UserSettings.module.css | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Containers/UserSettings/UserSettings.module.css b/Containers/UserSettings/UserSettings.module.css index 1c6ab20..2d6b7f5 100644 --- a/Containers/UserSettings/UserSettings.module.css +++ b/Containers/UserSettings/UserSettings.module.css @@ -12,7 +12,7 @@ width: 100%; margin: 40px 20px 0px 5px; text-align: left; - padding: 32px 24px; + padding: 28px 24px; animation: entrance ease-in 0.3s 1 normal none; border-bottom: 1px solid #e5e7eb; } @@ -38,6 +38,7 @@ .settingCategory h2 { color: #494b7a; margin: 0; + font-size: 1.3em; } .setting { @@ -90,13 +91,20 @@ margin: 0; margin-bottom: 0px; outline: 0; - padding: 15px; + padding: 10px; width: 100%; background-color: #f5f5f5; border-radius: 5px; /* color: #1b1340; */ color: #494b7a; box-shadow: 0 1px 0 rgba(0, 0, 0, 0.03) inset; + font-family: ( + --pure-material-font, + 'Roboto', + 'Segoe UI', + BlinkMacSystemFont, + system-ui + ); } .bwForm textarea { From 20ccb274666c34a10c03bc135f70904d1698538c Mon Sep 17 00:00:00 2001 From: bsourisse Date: Fri, 3 Mar 2023 16:11:38 +0100 Subject: [PATCH 04/24] feat: apprise notification switch + api --- .../AppriseAlertSettings.js | 105 ++++++++++++++++-- pages/api/account/getAppriseAlert.js | 63 +++++++++++ pages/api/account/updateAppriseAlert.js | 82 ++++++++++++++ 3 files changed, 240 insertions(+), 10 deletions(-) create mode 100644 pages/api/account/getAppriseAlert.js create mode 100644 pages/api/account/updateAppriseAlert.js diff --git a/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js b/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js index 0d0389e..76926b1 100644 --- a/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js +++ b/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js @@ -11,6 +11,79 @@ import Error from '../../../Components/UI/Error/Error'; import Switch from '../../../Components/UI/Switch/Switch'; export default function AppriseAlertSettings() { + //Var + const toastOptions = { + position: 'top-right', + autoClose: 5000, + hideProgressBar: false, + closeOnClick: true, + pauseOnHover: true, + draggable: true, + progress: undefined, + //Callback > re-enabled button after notification. + onClose: () => setDisabled(false), + }; + + ////State + const [isLoading, setIsLoading] = useState(true); + const [testIsLoading, setTestIsLoading] = useState(false); + const [error, setError] = useState(); + const [disabled, setDisabled] = useState(false); + const [checked, setChecked] = useState(); + const [info, setInfo] = useState(false); + + ////LifeCycle + //Component did mount + useEffect(() => { + const dataFetch = async () => { + try { + const response = await fetch('/api/account/getAppriseAlert', { + method: 'GET', + headers: { + 'Content-type': 'application/json', + }, + }); + setChecked((await response.json()).appriseAlert); + setIsLoading(false); + } catch (error) { + console.log('Fetching Apprise alert setting failed.'); + } + }; + dataFetch(); + }, []); + + ////Functions + const onChangeSwitchHandler = async (data) => { + //Remove old error + setError(); + //Disabled button + setDisabled(true); + const response = await fetch('/api/account/updateAppriseAlert', { + method: 'PUT', + headers: { + 'Content-type': 'application/json', + }, + body: JSON.stringify(data), + }); + const result = await response.json(); + + if (!response.ok) { + setError(result.message); + setTimeout(() => { + setError(); + setDisabled(false); + }, 4000); + } else { + if (data.appriseAlert) { + setChecked(!checked); + toast.success('Apprise notifications enabled.', toastOptions); + } else { + setChecked(!checked); + toast.success('Apprise notifications disabled.', toastOptions); + } + } + }; + return ( <> {/* APPRISE ALERT */} @@ -20,15 +93,26 @@ export default function AppriseAlertSettings() {
- - // onChangeSwitchHandler({ emailAlert: e }) - // } - /> + {isLoading ? ( + + ) : ( + + onChangeSwitchHandler({ appriseAlert: e }) + } + /> + )} +
Apprise URLs
- +
Send a test notification + {error && }
diff --git a/pages/api/account/getAppriseAlert.js b/pages/api/account/getAppriseAlert.js new file mode 100644 index 0000000..dcd2a41 --- /dev/null +++ b/pages/api/account/getAppriseAlert.js @@ -0,0 +1,63 @@ +//Lib +import { promises as fs } from 'fs'; +import path from 'path'; +import { authOptions } from '../auth/[...nextauth]'; +import { unstable_getServerSession } from 'next-auth/next'; + +export default async function handler(req, res) { + if (req.method == 'GET') { + //Verify that the user is logged in. + const session = await unstable_getServerSession(req, res, authOptions); + if (!session) { + res.status(401).json({ message: 'You must be logged in.' }); + return; + } + try { + //Read the users file + //Find the absolute path of the json directory + const jsonDirectory = path.join(process.cwd(), '/config'); + let usersList = await fs.readFile( + jsonDirectory + '/users.json', + 'utf8' + ); + //Parse the usersList + usersList = JSON.parse(usersList); + + //Verify that the user of the session exists + const userIndex = usersList + .map((user) => user.username) + .indexOf(session.user.name); + if (userIndex === -1) { + res.status(400).json({ + message: + 'User is incorrect. Please, logout to update your session.', + }); + return; + } else { + //Send the appriseAlert bool + res.status(200).json({ + appriseAlert: usersList[userIndex].appriseAlert, + }); + return; + } + } catch (error) { + //Log for backend + console.log(error); + //Log for frontend + if (error.code == 'ENOENT') { + res.status(500).json({ + status: 500, + message: 'No such file or directory', + }); + } else { + res.status(500).json({ + status: 500, + message: 'API error, contact the administrator', + }); + } + return; + } + } else { + res.status(405).json({ message: 'Bad request on API' }); + } +} diff --git a/pages/api/account/updateAppriseAlert.js b/pages/api/account/updateAppriseAlert.js new file mode 100644 index 0000000..a3694f0 --- /dev/null +++ b/pages/api/account/updateAppriseAlert.js @@ -0,0 +1,82 @@ +//Lib +import { promises as fs } from 'fs'; +import path from 'path'; +import { authOptions } from '../auth/[...nextauth]'; +import { unstable_getServerSession } from 'next-auth/next'; + +export default async function handler(req, res) { + if (req.method == 'PUT') { + //Verify that the user is logged in. + const session = await unstable_getServerSession(req, res, authOptions); + if (!session) { + res.status(401).json({ message: 'You must be logged in.' }); + return; + } + + //The data we expect to receive + let { appriseAlert } = req.body; + + //Read the users file + //Find the absolute path of the json directory + const jsonDirectory = path.join(process.cwd(), '/config'); + let usersList = await fs.readFile( + jsonDirectory + '/users.json', + 'utf8' + ); + //Parse the usersList + usersList = JSON.parse(usersList); + + //1 : control the data + if (typeof appriseAlert != 'boolean') { + res.status(422).json({ message: 'Unexpected data' }); + return; + } + + //2 : Verify that the user of the session exists + const userIndex = usersList + .map((user) => user.username) + .indexOf(session.user.name); + if (userIndex === -1) { + res.status(400).json({ + message: + 'User is incorrect. Please, logout to update your session.', + }); + return; + } + + //3 : Change the appriseAlert settings + try { + //Modify the appriseAlert bool for the user + let newUsersList = usersList.map((user) => + user.username == session.user.name + ? { ...user, appriseAlert: appriseAlert } + : user + ); + //Stringify the new users list + newUsersList = JSON.stringify(newUsersList); + //Write the new JSON + fs.writeFile(jsonDirectory + '/users.json', newUsersList, (err) => { + if (err) console.log(err); + }); + res.status(200).json({ message: 'Successful API send' }); + } catch (error) { + //Log for backend + console.log(error); + //Log for frontend + if (error.code == 'ENOENT') { + res.status(500).json({ + status: 500, + message: 'No such file or directory', + }); + } else { + res.status(500).json({ + status: 500, + message: 'API error, contact the administrator', + }); + } + return; + } + } else { + res.status(405).json({ message: 'Bad request on API' }); + } +} From 869665f5dcb95a50cca2591e1e58eaac24b83c32 Mon Sep 17 00:00:00 2001 From: bsourisse Date: Fri, 3 Mar 2023 18:46:07 +0100 Subject: [PATCH 05/24] init files for Apprise API --- pages/api/account/sendTestApprise.js | 0 pages/api/account/updateAppriseServices.js | 0 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 pages/api/account/sendTestApprise.js create mode 100644 pages/api/account/updateAppriseServices.js diff --git a/pages/api/account/sendTestApprise.js b/pages/api/account/sendTestApprise.js new file mode 100644 index 0000000..e69de29 diff --git a/pages/api/account/updateAppriseServices.js b/pages/api/account/updateAppriseServices.js new file mode 100644 index 0000000..e69de29 From db42d89dd64f83472d7bec25ed6dce2e5f2de5c0 Mon Sep 17 00:00:00 2001 From: bsourisse Date: Fri, 3 Mar 2023 18:47:03 +0100 Subject: [PATCH 06/24] feat: init textarea for Apprise URLs --- .../AppriseAlertSettings.js | 98 ++++++++++++++++--- .../UserSettings/UserSettings.module.css | 14 +++ 2 files changed, 98 insertions(+), 14 deletions(-) diff --git a/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js b/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js index 76926b1..0cf1e77 100644 --- a/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js +++ b/Containers/UserSettings/AppriseAlertSettings/AppriseAlertSettings.js @@ -5,6 +5,7 @@ import 'react-toastify/dist/ReactToastify.css'; import classes from '../UserSettings.module.css'; import { useState } from 'react'; import { SpinnerCircularFixed } from 'spinners-react'; +import { useForm } from 'react-hook-form'; //Components import Error from '../../../Components/UI/Error/Error'; @@ -23,10 +24,19 @@ export default function AppriseAlertSettings() { //Callback > re-enabled button after notification. onClose: () => setDisabled(false), }; + let appriseURLs; + + const { + register, + handleSubmit, + formState: { errors, isSubmitting, isValid }, + } = useForm({ mode: 'onBlur' }); ////State - const [isLoading, setIsLoading] = useState(true); + const [checkIsLoading, setCheckIsLoading] = useState(true); const [testIsLoading, setTestIsLoading] = useState(false); + const [formIsLoading, setFormIsLoading] = useState(false); + const [formIsSaved, setFormIsSaved] = useState(false); const [error, setError] = useState(); const [disabled, setDisabled] = useState(false); const [checked, setChecked] = useState(); @@ -44,7 +54,7 @@ export default function AppriseAlertSettings() { }, }); setChecked((await response.json()).appriseAlert); - setIsLoading(false); + setCheckIsLoading(false); } catch (error) { console.log('Fetching Apprise alert setting failed.'); } @@ -53,6 +63,7 @@ export default function AppriseAlertSettings() { }, []); ////Functions + //Switch to enable/disable Apprise notifications const onChangeSwitchHandler = async (data) => { //Remove old error setError(); @@ -84,6 +95,38 @@ export default function AppriseAlertSettings() { } }; + //Form submit handler to modify Apprise services + const formSubmitHandler = async (data) => { + console.log(data); + // //Remove old error + // setError(); + // //Loading button on submit to avoid multiple send. + // setFormIsLoading(true); + // //POST API to update Apprise Services + // const response = await fetch('/api/account/updateAppriseServices', { + // method: 'PUT', + // headers: { + // 'Content-type': 'application/json', + // }, + // body: JSON.stringify(data), + // }); + // const result = await response.json(); + + // if (!response.ok) { + // setFormIsLoading(false); + // setError(result.message); + // setTimeout(() => setError(), 4000); + // } else { + // setFormIsLoading(false); + // setInfo(true); + // toast.success('Email edited !', toastOptions); + // } + + //TEST + setFormIsSaved(true); + setTimeout(() => setFormIsSaved(false), 3000); + }; + return ( <> {/* APPRISE ALERT */} @@ -93,7 +136,7 @@ export default function AppriseAlertSettings() {
- {isLoading ? ( + {checkIsLoading ? ( )} - +
+
+ Apprise URLs +
+
+ {formIsLoading && ( + + )} + {formIsSaved && ( +
+ ✅ Apprise configuration has been saved. +
+ )} +
+
-
- Apprise URLs -
- +