mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2024-11-27 03:48:52 +03:00
notification mark read
This commit is contained in:
parent
102ef5b4a1
commit
6f690734c5
14 changed files with 89 additions and 41 deletions
|
@ -177,6 +177,7 @@ impl Data {
|
|||
mailer: Self::get_mailer(),
|
||||
};
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
init.join().unwrap();
|
||||
|
||||
Arc::new(data)
|
||||
|
|
|
@ -17,48 +17,28 @@
|
|||
|
||||
const ROUTES = {
|
||||
registerUser: '/api/v1/signup',
|
||||
|
||||
loginUser: '/api/v1/signin',
|
||||
|
||||
signoutUser: '/api/v1/signout',
|
||||
|
||||
deleteAccount: '/api/v1/account/delete',
|
||||
|
||||
usernameExists: '/api/v1/account/username/exists',
|
||||
|
||||
emailExists: '/api/v1/account/email/exists',
|
||||
|
||||
healthCheck: '/api/v1/meta/health',
|
||||
|
||||
buildDetails: '/api/v1/meta/build',
|
||||
|
||||
addDomain: '/api/v1/mcaptcha/domain/add',
|
||||
|
||||
challengeDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/get',
|
||||
|
||||
proveDomain: '/api/v1/mcaptcha/domain/domain/verify/challenge/prove',
|
||||
|
||||
deleteDomain: '/api/v1/mcaptcha/domain/delete',
|
||||
|
||||
addToken: '/api/v1/mcaptcha/domain/token/add',
|
||||
|
||||
updateTokenKey: '/api/v1/mcaptcha/domain/token/update',
|
||||
|
||||
getTokenKey: '/api/v1/mcaptcha/domain/token/get',
|
||||
|
||||
deleteToken: '/api/v1/mcaptcha/domain/token/delete',
|
||||
|
||||
addTokenLevels: '/api/v1/mcaptcha/domain/token/levels/add',
|
||||
|
||||
updateTokenLevels: '/api/v1/mcaptcha/domain/token/levels/update',
|
||||
|
||||
deleteTokenLevels: '/api/v1/mcaptcha/domain/token/levels/delete',
|
||||
|
||||
getTokenLevels: '/api/v1/mcaptcha/domain/token/levels/get',
|
||||
|
||||
getTokenDuration: '/api/v1/mcaptcha/domain/token/token/get',
|
||||
|
||||
updateTokenDuration: '/api/v1/mcaptcha/domain/token/token/update',
|
||||
markNotificationRead: '/api/v1/notifications/read',
|
||||
};
|
||||
|
||||
export default ROUTES;
|
||||
|
|
|
@ -23,6 +23,7 @@ import * as panel from './panel/ts/index';
|
|||
import * as addSiteKey from './panel/sitekey/add/ts';
|
||||
import * as editSitekey from './panel/sitekey/edit/';
|
||||
import * as listSitekeys from './panel/sitekey/list/ts';
|
||||
import * as notidications from './panel/notifications/ts';
|
||||
import {MODE} from './logger';
|
||||
import log from './logger';
|
||||
|
||||
|
@ -50,9 +51,10 @@ const router = new Router();
|
|||
router.register(VIEWS.panelHome, panel.index);
|
||||
router.register(VIEWS.registerUser, register.index);
|
||||
router.register(VIEWS.loginUser, login.index);
|
||||
router.register(VIEWS.notifications, notidications.index);
|
||||
router.register(VIEWS.listSitekey, listSitekeys.index);
|
||||
router.register(VIEWS.addSiteKey, addSiteKey.index);
|
||||
router.register(VIEWS.editSitekey("[A-Z,a-z,0-9]+"), editSitekey.index);
|
||||
router.register(VIEWS.editSitekey('[A-Z),a-z,0-9]+'), editSitekey.index);
|
||||
|
||||
try {
|
||||
router.route();
|
||||
|
|
|
@ -53,6 +53,7 @@ include!("./navbar/index.html"); .>
|
|||
</td>
|
||||
<td class="sitekey-list__key">
|
||||
<div class="sitekey-list__edit">
|
||||
<. let key = format!("/sitekey/{}", &sitekey.key); .>
|
||||
<. include!("./sitekey/view/__edit-sitekey-icon.html"); .>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -14,7 +14,7 @@ include!("../navbar/index.html"); .>
|
|||
</thead>
|
||||
<tbody class="notification__body">
|
||||
<. for notification in n.iter() { .>
|
||||
<tr class="notification__item">
|
||||
<tr class="notification__item" id="notification__item-<.= notification.id .>">
|
||||
<td>
|
||||
<h3 class="notification__item-heading">
|
||||
<.= notification.heading .>
|
||||
|
@ -34,6 +34,7 @@ include!("../navbar/index.html"); .>
|
|||
src="<.= crate::FILES
|
||||
.get("./static/cache/img/svg/check.svg")
|
||||
.unwrap() .>"
|
||||
data-id="<.= notification.id .>"
|
||||
alt="Mark Read"
|
||||
/>
|
||||
</button>
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
}
|
||||
|
||||
.notification__mark-read-btn:hover {
|
||||
cursor: grab;
|
||||
cursor: pointer;
|
||||
background-color: $light-grey;
|
||||
}
|
||||
|
||||
|
|
55
templates/panel/notifications/ts/index.ts
Normal file
55
templates/panel/notifications/ts/index.ts
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import genJsonPayload from '../../../utils/genJsonPayload';
|
||||
import createError from '../../../components/error';
|
||||
|
||||
import ROUTES from '../../../api/v1/routes';
|
||||
|
||||
const BTN = document.querySelectorAll('.notification__mark-read-btn');
|
||||
const TABLE_BODY = document.querySelector('.notification__body');
|
||||
|
||||
const notification_record = (id: number) =>
|
||||
<HTMLElement>TABLE_BODY.querySelector(`#notification__item-${id}`);
|
||||
|
||||
const markRead = async (e: Event) => {
|
||||
const element = <HTMLElement>e.target;
|
||||
|
||||
const id = Number.parseInt(element.dataset.id);
|
||||
|
||||
const payload = {
|
||||
id,
|
||||
};
|
||||
|
||||
const res = await fetch(ROUTES.markNotificationRead, genJsonPayload(payload));
|
||||
if (res.ok) {
|
||||
notification_record(id).remove();
|
||||
} else {
|
||||
const err = await res.json();
|
||||
createError(err.error);
|
||||
}
|
||||
};
|
||||
|
||||
const addMarkReadEventListenet = () => {
|
||||
BTN.forEach(btn => {
|
||||
btn.addEventListener('click', markRead, true);
|
||||
});
|
||||
};
|
||||
|
||||
export const index = () => {
|
||||
addMarkReadEventListenet();
|
||||
};
|
|
@ -30,8 +30,7 @@ import createError from '../../../components/error';
|
|||
|
||||
import VIEWS from '../../../views/v1/routes';
|
||||
|
||||
const BTN = <HTMLElement>document.querySelector('.sitekey-form__submit');
|
||||
const key = BTN.dataset.sitekey;
|
||||
const BTN_CLASS = document.querySelector('sitekey-form__submit');
|
||||
|
||||
const submit = async (e: Event) => {
|
||||
e.preventDefault();
|
||||
|
@ -44,6 +43,9 @@ const submit = async (e: Event) => {
|
|||
const levels = LEVELS.getLevels();
|
||||
console.debug(`[form submition]: levels: ${levels}`);
|
||||
|
||||
const btn = <HTMLElement>document.querySelector(`${BTN_CLASS}`);
|
||||
const key = btn.dataset.sitekey;
|
||||
|
||||
const payload = {
|
||||
levels,
|
||||
duration,
|
||||
|
|
|
@ -44,6 +44,7 @@ include!("../../navbar/index.html"); .>
|
|||
</td>
|
||||
<td class="sitekey-list__key">
|
||||
<div class="sitekey-list__edit">
|
||||
<. let key = format!("/sitekey/{}", &sitekey.key); .>
|
||||
<. include!("../view/__edit-sitekey-icon.html"); .>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<a href="./edit/">
|
||||
<a href="<.= key .>/edit/">
|
||||
<img class="sitekey-form__edit" src="<.=
|
||||
crate::FILES.get("./static/cache/img/svg/edit.svg").unwrap() .>" alt="Edit
|
||||
sitekey" />
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
/>
|
||||
</a>
|
||||
<. if READONLY { .>
|
||||
<. let key = "."; .>
|
||||
<. include!("./__edit-sitekey-icon.html"); .>
|
||||
<. } .>
|
||||
</h1>
|
||||
|
|
|
@ -27,7 +27,7 @@ const panelResult = 'hello from panel';
|
|||
const panelRoute = '/panel';
|
||||
const panel = () => (result.result = panelResult);
|
||||
|
||||
const settingsRoute = '/settings/';
|
||||
const settingsRoute = '/sitekey/';
|
||||
const settingsResult = 'hello from settings';
|
||||
const settings = () => (result.result = settingsResult);
|
||||
|
||||
|
@ -41,9 +41,9 @@ const emptyUriErr = 'uri is empty';
|
|||
const unregisteredRouteErr = "Route isn't registered";
|
||||
|
||||
const router = new Router();
|
||||
router.register(patternRoute, pattern);
|
||||
router.register(panelRoute, panel);
|
||||
router.register(settingsRoute, settings);
|
||||
router.register(patternRoute, pattern);
|
||||
|
||||
it('checks if Router works', () => {
|
||||
window.history.pushState({}, '', examplePatternRoute);
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** Removes trailing slashed from URI */
|
||||
/** Removes trailing slash from URI */
|
||||
const normalizeUri = (uri: string) => {
|
||||
uri = uri.trim();
|
||||
if (uri.length == 0) {
|
||||
|
@ -54,7 +54,7 @@ export class Router {
|
|||
register(uri: string, fn: () => void) {
|
||||
uri = normalizeUri(uri);
|
||||
|
||||
let pattern = new RegExp(`^${uri}(.*)`);
|
||||
let pattern = new RegExp(`^${uri}$`);
|
||||
|
||||
let patterString = pattern.toString();
|
||||
if (
|
||||
|
@ -83,18 +83,21 @@ export class Router {
|
|||
route() {
|
||||
const path = normalizeUri(window.location.pathname);
|
||||
|
||||
let fn: () => void | undefined;
|
||||
let fn: undefined | (() => void);
|
||||
|
||||
this.routes.forEach(route => {
|
||||
if (path.match(route.pattern)) {
|
||||
fn = route.fn;
|
||||
if (
|
||||
this.routes.find(route => {
|
||||
if (path.match(route.pattern)) {
|
||||
fn = route.fn;
|
||||
return true;
|
||||
}
|
||||
})
|
||||
) {
|
||||
if (fn === undefined) {
|
||||
throw new Error("Route isn't registered");
|
||||
} else {
|
||||
return fn();
|
||||
}
|
||||
});
|
||||
|
||||
if (fn === undefined) {
|
||||
throw new Error("Route isn't registered");
|
||||
}
|
||||
|
||||
return fn();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ const ROUTES = {
|
|||
signoutUser: '/api/v1/signout',
|
||||
panelHome: '/',
|
||||
docsHome: '/docs/',
|
||||
notifications: '/notifications',
|
||||
listSitekey: '/sitekeys/',
|
||||
viewSitekey: (key: string) => `/sitekey/${key}/`,
|
||||
editSitekey: (key: string) => `/sitekey/${key}/edit/`,
|
||||
|
|
Loading…
Reference in a new issue