Avoid using this instance until migration is complete. For updates and information contact insert#0751

Verified Commit 67bbfed0 authored by insert's avatar insert 🎺

Add notifications.

parent 64034028
Pipeline #295 failed with stage
in 1 minute and 5 seconds
......@@ -68,8 +68,19 @@
font-size: 1rem;
}
noscript > div > a {
align-self: center;
margin-top: 20px;
padding: 8px 10px;
width: 100px;
font-weight: 600;
background: #7B68EE;
border-radius: 4px;
text-decoration: none;
color: white;
transition: background-color .2s;
}
noscript > div > a:hover {background-color: #6253be}
noscript > div > a:active {background-color: #493e8e}
</style>
</head>
<body ontouchstart="">
......@@ -79,7 +90,7 @@
<h1>Well, this is really awkward...</h1>
<p>Seems like your JavaScript is disabled.</p>
<p>You'll need to enable JavaScript to run this app.</p>
<a>Support</a>
<a href="https://support.riotchat.gq" target="_blank">Get Support</a>
</div>
</noscript>
<div id="root"></div>
......
......@@ -7,6 +7,7 @@ import Login from './pages/Login';
import { Instance } from './internal/Client';
import Chat from './pages/Chat';
import { Settings } from './pages/Settings';
import ErrorBoundary from './components/util/ErrorBoundary';
export enum Page {
NONE = 0,
......@@ -94,16 +95,18 @@ export default function App() {
} as CSSProperties;
return (
<div className={`theme-${theme} ${styles.app}`} style={style}>
<Helmet>
<meta name="theme-color" content={accent} />
</Helmet>
<AppContext.Provider value={states}>
{ page & Page.LOAD ? <Load waitForClient={ready} /> : null }
{ page & Page.LOGIN ? <Login />: null }
{ page & (Page.APP | Page.SETTINGS) ? <Chat /> : null }
{ page & Page.SETTINGS ? <Settings /> : null }
</AppContext.Provider>
</div>
<ErrorBoundary>
<div className={`theme-${theme} ${styles.app}`} style={style}>
<Helmet>
<meta name="theme-color" content={accent} />
</Helmet>
<AppContext.Provider value={states}>
{ page & Page.LOAD ? <Load waitForClient={ready} /> : null }
{ page & Page.LOGIN ? <Login />: null }
{ page & (Page.APP | Page.SETTINGS) ? <Chat /> : null }
{ page & Page.SETTINGS ? <Settings /> : null }
</AppContext.Provider>
</div>
</ErrorBoundary>
);
}
\ No newline at end of file
.root {
color: var(--body-text);
display: block;
//flex-direction: column;
margin: 0 auto;
text-align: center;
//min-width: 600px;
max-width: 600px;
padding: 10px;
.note {
font-size: 14px;
}
pre {
background: var(--secondary);
padding: 4px;
border-radius: 2px;
text-align: left;
white-space: pre-wrap; /* css-3 */
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
white-space: -pre-wrap; /* Opera 4-6 */
white-space: -o-pre-wrap; /* Opera 7 */
word-wrap: break-word; /* Internet Explorer 5.5+ */
user-select: text;
}
}
\ No newline at end of file
import React, { Component } from 'react';
import styles from './ErrorBoundary.module.scss';
import { Icon } from '../ui/elements/Icon';
export default class ErrorBoundary extends Component<{}, { error?: Error }> {
constructor() {
super({ });
this.state = { };
}
componentDidCatch(error: Error) {
this.setState({ error });
}
render() {
if (this.state.error) {
return (
<div className={styles.root}>
<Icon icon='xRegular' />
<h1>Well, this is really awkward...</h1>
<p>Something went wrong.</p>
{this.state.error.name}: { this.state.error.message }
{ this.state.error.stack && <pre><code>{ this.state.error.stack.split('\n').slice(0, 5).join('\n') }</code></pre> }
<div className={styles.note}>Riot crashed unexpectedly. A crash report has been dispatched.</div>
</div>
);
}
return this.props.children;
}
}
\ No newline at end of file
......@@ -4,7 +4,70 @@ type ShareData = {
url? : string;
};
interface Navigator
{
share? : (data? : ShareData) => Promise<void>;
interface Navigator {
share?: (data? : ShareData) => Promise<void>;
}
type NotificationPermission = "default" | "denied" | "granted";
type NotificationDirection = "auto" | "ltr" | "rtl";
interface NotificationPermissionCallback {
(permission: NotificationPermission): void;
}
interface NotificationOptions {
dir?: NotificationDirection;
lang?: string;
body?: string;
tag?: string;
image?: string;
icon?: string;
badge?: string;
sound?: string;
vibrate?: number | number[],
timestamp?: number,
renotify?: boolean;
silent?: boolean;
requireInteraction?: boolean;
data?: any;
actions?: NotificationAction[]
}
interface NotificationAction {
action: string;
title: string;
icon?: string;
}
declare class Notification extends EventTarget {
constructor(title: string, options?: NotificationOptions);
static readonly permission: NotificationPermission;
static requestPermission(): Promise<NotificationPermission>;
static requestPermission(deprecatedCallback: (permission: NotificationPermission) => void): void;
static readonly maxActions: number;
onclick: EventListenerOrEventListenerObject;
onerror: EventListenerOrEventListenerObject;
close(): void;
readonly title: string;
readonly dir: NotificationDirection;
readonly lang: string;
readonly body: string;
readonly tag: string;
readonly image: string;
readonly icon: string;
readonly badge: string;
readonly sound: string;
readonly vibrate: number[];
readonly timestamp: number;
readonly renotify: boolean;
readonly silent: boolean;
readonly requireInteraction: boolean;
readonly data: any;
readonly actions: NotificationAction[]
}
\ No newline at end of file
import React, { createContext, useState, memo } from 'react';
import React, { createContext, useState, memo, useEffect } from 'react';
import Helmet from 'react-helmet';
import styles from './Chat.module.scss';
......@@ -11,9 +11,12 @@ import Channel from './chat/Channel';
import { useVar } from '../components/util/CSS';
import Friends from './friends/Friends';
import MediaQuery from 'react-responsive';
import Notification from '../components/ui/components/Notification';
import Notif from '../components/ui/components/Notification';
import { UpdateEmitter } from '..';
import { ModalContext, useModals, Modals } from './chat/modals/ModalContext';
import { Message } from 'riotchat.js/dist/internal/Message';
import { Instance } from '../internal/Client';
import { DMChannel, GroupChannel } from 'riotchat.js/dist/internal/Channel';
export enum Page {
GUILD = 0x1, // switches to guild specific sidebar
......@@ -46,11 +49,37 @@ const updateStrings = [
"A new update is ready for you, chief."
];
function Notify(title?: string, notif?: NotificationOptions, onclick?: () => void) {
if (!('Notification' in window)) {
alert("This browser does not support desktop notifications!");
}
else if (Notification.permission === "granted") {
if (title) new Notification(title, notif).onclick = onclick || null;
}
else if (Notification.permission !== "denied") {
Notification.requestPermission().then(permission => {
if (permission === "granted") {
if (title) new Notification(title, notif).onclick = onclick || null;
}
});
}
}
let messageDing = new Audio('/message.mp3');
const Chat = memo(() => {
let [ page, setPage ] = useState<Page>(Page.HOME);
let [ channel, setChannel ] = useState<string>();
let [ drawer, setDrawer ] = useState(false);
function switchTo(page: Page, channel?: string) {
setChannel(channel);
setPage(page);
setDrawer(false);
}
let [ updateAvailable, setUpdate ] = useState(false);
let [ updateString, setUpdateString ] = useState<string>();
......@@ -60,15 +89,24 @@ const Chat = memo(() => {
setUpdateString(updateStrings[Math.floor(Math.random() * updateStrings.length)]);
});
Notify();
function onMessage(msg: Message) {
messageDing.play();
Notify(msg.author.username, {
body: msg.content,
icon: msg.author.avatarURL,
silent: true
}, () => switchTo(msg.channel instanceof DMChannel ? Page.DM
: msg.channel instanceof GroupChannel ? Page.GROUP : Page.GUILD, msg.channel.id));
}
useEffect(() => Instance.client.on('message', onMessage));
let states = {
page, setPage,
channel, setChannel,
setDrawer,
switch: (page: Page, channel?: string) => {
setChannel(channel);
setPage(page);
setDrawer(false);
}
switch: switchTo
} as any;
let body;
......@@ -122,10 +160,10 @@ const Chat = memo(() => {
}
</MediaQuery>
<div className={styles.main}>
{ updateAvailable && <Notification isElement={true} type='update' centerText={true}>
{ updateAvailable && <Notif isElement={true} type='update' centerText={true}>
{updateString}
<button onClick={() => window.location.reload()}>Update</button>
</Notification> }
</Notif> }
{body}
</div>
</div>
......
......@@ -18,6 +18,8 @@ export default function Friend(props: FriendProps) {
function onDecline() {}
function onCancel() {}
throw new Error('test');
return (
<div className={styles.friend} onClick={props.onClick}>
<div className={styles.name}>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment