chore(refactor): remove `Notifications` component
parent
1e6a3a5160
commit
5b2782678e
|
|
@ -3,3 +3,14 @@ import { createBrowserHistory } from "history";
|
||||||
export const history = createBrowserHistory({
|
export const history = createBrowserHistory({
|
||||||
basename: import.meta.env.BASE_URL,
|
basename: import.meta.env.BASE_URL,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const routeInformation = {
|
||||||
|
getServer: () =>
|
||||||
|
/server\/([0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26})/.exec(
|
||||||
|
history.location.pathname,
|
||||||
|
)?.[1],
|
||||||
|
getChannel: () =>
|
||||||
|
/channel\/([0123456789ABCDEFGHJKMNPQRSTVWXYZ]{26})/.exec(
|
||||||
|
history.location.pathname,
|
||||||
|
)?.[1],
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,296 +0,0 @@
|
||||||
import { Route, Switch, useHistory, useParams } from "react-router-dom";
|
|
||||||
import { Message, User } from "revolt.js";
|
|
||||||
import { decodeTime } from "ulid";
|
|
||||||
|
|
||||||
import { useCallback, useEffect } from "preact/hooks";
|
|
||||||
|
|
||||||
import { useTranslation } from "../../lib/i18n";
|
|
||||||
|
|
||||||
import { useApplicationState } from "../../mobx/State";
|
|
||||||
|
|
||||||
import { useClient } from "../../controllers/client/ClientController";
|
|
||||||
|
|
||||||
const notifications: { [key: string]: Notification } = {};
|
|
||||||
|
|
||||||
async function createNotification(
|
|
||||||
title: string,
|
|
||||||
options: globalThis.NotificationOptions,
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
return new Notification(title, options);
|
|
||||||
} catch (err) {
|
|
||||||
const sw = await navigator.serviceWorker.getRegistration();
|
|
||||||
sw?.showNotification(title, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function Notifier() {
|
|
||||||
const translate = useTranslation();
|
|
||||||
const state = useApplicationState();
|
|
||||||
const notifs = state.notifications;
|
|
||||||
const showNotification = state.settings.get("notifications:desktop");
|
|
||||||
|
|
||||||
const client = useClient();
|
|
||||||
const { guild: guild_id, channel: channel_id } = useParams<{
|
|
||||||
guild: string;
|
|
||||||
channel: string;
|
|
||||||
}>();
|
|
||||||
const history = useHistory();
|
|
||||||
|
|
||||||
const message = useCallback(
|
|
||||||
async (msg: Message) => {
|
|
||||||
if (msg.channel_id === channel_id && document.hasFocus()) return;
|
|
||||||
if (!notifs.shouldNotify(msg)) return;
|
|
||||||
|
|
||||||
state.settings.sounds.playSound("message");
|
|
||||||
if (!showNotification) return;
|
|
||||||
|
|
||||||
const effectiveName = msg.masquerade?.name ?? msg.author?.username;
|
|
||||||
|
|
||||||
let title;
|
|
||||||
switch (msg.channel?.channel_type) {
|
|
||||||
case "SavedMessages":
|
|
||||||
return;
|
|
||||||
case "DirectMessage":
|
|
||||||
title = `@${effectiveName}`;
|
|
||||||
break;
|
|
||||||
case "Group":
|
|
||||||
if (msg.author?._id === "00000000000000000000000000") {
|
|
||||||
title = msg.channel.name;
|
|
||||||
} else {
|
|
||||||
title = `@${effectiveName} - ${msg.channel.name}`;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "TextChannel":
|
|
||||||
title = `@${effectiveName} (#${msg.channel.name}, ${msg.channel.server?.name})`;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
title = msg.channel?._id;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let image;
|
|
||||||
if (msg.attachments) {
|
|
||||||
const imageAttachment = msg.attachments.find(
|
|
||||||
(x) => x.metadata.type === "Image",
|
|
||||||
);
|
|
||||||
if (imageAttachment) {
|
|
||||||
image = client.generateFileURL(imageAttachment, {
|
|
||||||
max_side: 720,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let body, icon;
|
|
||||||
if (msg.content) {
|
|
||||||
body = client.markdownToText(msg.content);
|
|
||||||
|
|
||||||
if (msg.masquerade?.avatar) {
|
|
||||||
icon = client.proxyFile(msg.masquerade.avatar);
|
|
||||||
} else {
|
|
||||||
icon = msg.author?.generateAvatarURL({ max_side: 256 });
|
|
||||||
}
|
|
||||||
} else if (msg.system) {
|
|
||||||
const users = client.users;
|
|
||||||
|
|
||||||
switch (msg.system.type) {
|
|
||||||
case "user_added":
|
|
||||||
case "user_remove":
|
|
||||||
{
|
|
||||||
const user = users.get(msg.system.id);
|
|
||||||
body = translate(
|
|
||||||
`app.main.channel.system.${
|
|
||||||
msg.system.type === "user_added"
|
|
||||||
? "added_by"
|
|
||||||
: "removed_by"
|
|
||||||
}`,
|
|
||||||
{
|
|
||||||
user: user?.username,
|
|
||||||
other_user: users.get(msg.system.by)
|
|
||||||
?.username,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
icon = user?.generateAvatarURL({
|
|
||||||
max_side: 256,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "user_joined":
|
|
||||||
case "user_left":
|
|
||||||
case "user_kicked":
|
|
||||||
case "user_banned":
|
|
||||||
{
|
|
||||||
const user = users.get(msg.system.id);
|
|
||||||
body = translate(
|
|
||||||
`app.main.channel.system.${msg.system.type}`,
|
|
||||||
{ user: user?.username },
|
|
||||||
);
|
|
||||||
icon = user?.generateAvatarURL({
|
|
||||||
max_side: 256,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "channel_renamed":
|
|
||||||
{
|
|
||||||
const user = users.get(msg.system.by);
|
|
||||||
body = translate(
|
|
||||||
`app.main.channel.system.channel_renamed`,
|
|
||||||
{
|
|
||||||
user: users.get(msg.system.by)?.username,
|
|
||||||
name: msg.system.name,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
icon = user?.generateAvatarURL({
|
|
||||||
max_side: 256,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "channel_description_changed":
|
|
||||||
case "channel_icon_changed":
|
|
||||||
{
|
|
||||||
const user = users.get(msg.system.by);
|
|
||||||
body = translate(
|
|
||||||
`app.main.channel.system.${msg.system.type}`,
|
|
||||||
{ user: users.get(msg.system.by)?.username },
|
|
||||||
);
|
|
||||||
icon = user?.generateAvatarURL({
|
|
||||||
max_side: 256,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const notif = await createNotification(title!, {
|
|
||||||
icon,
|
|
||||||
image,
|
|
||||||
body,
|
|
||||||
timestamp: decodeTime(msg._id),
|
|
||||||
tag: msg.channel?._id,
|
|
||||||
badge: "/assets/icons/android-chrome-512x512.png",
|
|
||||||
silent: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
if (notif) {
|
|
||||||
notif.addEventListener("click", () => {
|
|
||||||
window.focus();
|
|
||||||
const id = msg.channel_id;
|
|
||||||
if (id !== channel_id) {
|
|
||||||
const channel = client.channels.get(id);
|
|
||||||
if (channel) {
|
|
||||||
if (channel.channel_type === "TextChannel") {
|
|
||||||
history.push(
|
|
||||||
`/server/${channel.server_id}/channel/${id}`,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
history.push(`/channel/${id}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
notifications[msg.channel_id] = notif;
|
|
||||||
notif.addEventListener(
|
|
||||||
"close",
|
|
||||||
() => delete notifications[msg.channel_id],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[
|
|
||||||
history,
|
|
||||||
showNotification,
|
|
||||||
translate,
|
|
||||||
channel_id,
|
|
||||||
client,
|
|
||||||
notifs,
|
|
||||||
state,
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
const relationship = useCallback(
|
|
||||||
async (user: User) => {
|
|
||||||
if (client.user?.status?.presence === "Busy") return;
|
|
||||||
if (!showNotification) return;
|
|
||||||
|
|
||||||
let event;
|
|
||||||
switch (user.relationship) {
|
|
||||||
case "Incoming":
|
|
||||||
event = translate("notifications.sent_request", {
|
|
||||||
person: user.username,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case "Friend":
|
|
||||||
event = translate("notifications.now_friends", {
|
|
||||||
person: user.username,
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const notif = await createNotification(event, {
|
|
||||||
icon: user.generateAvatarURL({ max_side: 256 }),
|
|
||||||
badge: "/assets/icons/android-chrome-512x512.png",
|
|
||||||
timestamp: +new Date(),
|
|
||||||
});
|
|
||||||
|
|
||||||
notif?.addEventListener("click", () => {
|
|
||||||
history.push(`/friends`);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[client.user?.status?.presence, history, showNotification, translate],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
client.addListener("message", message);
|
|
||||||
client.addListener("user/relationship", relationship);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
client.removeListener("message", message);
|
|
||||||
client.removeListener("user/relationship", relationship);
|
|
||||||
};
|
|
||||||
}, [
|
|
||||||
client,
|
|
||||||
state,
|
|
||||||
guild_id,
|
|
||||||
channel_id,
|
|
||||||
showNotification,
|
|
||||||
notifs,
|
|
||||||
message,
|
|
||||||
relationship,
|
|
||||||
]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
function visChange() {
|
|
||||||
if (document.visibilityState === "visible") {
|
|
||||||
if (notifications[channel_id]) {
|
|
||||||
notifications[channel_id].close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
visChange();
|
|
||||||
|
|
||||||
document.addEventListener("visibilitychange", visChange);
|
|
||||||
return () =>
|
|
||||||
document.removeEventListener("visibilitychange", visChange);
|
|
||||||
}, [guild_id, channel_id]);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function NotificationsComponent() {
|
|
||||||
return (
|
|
||||||
<Switch>
|
|
||||||
<Route path="/server/:server/channel/:channel">
|
|
||||||
<Notifier />
|
|
||||||
</Route>
|
|
||||||
<Route path="/channel/:channel">
|
|
||||||
<Notifier />
|
|
||||||
</Route>
|
|
||||||
<Route path="/">
|
|
||||||
<Notifier />
|
|
||||||
</Route>
|
|
||||||
</Switch>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -39,6 +39,9 @@ export default class State {
|
||||||
locale: LocaleOptions;
|
locale: LocaleOptions;
|
||||||
experiments: Experiments;
|
experiments: Experiments;
|
||||||
layout: Layout;
|
layout: Layout;
|
||||||
|
/**
|
||||||
|
* DEPRECATED
|
||||||
|
*/
|
||||||
private config: ServerConfig;
|
private config: ServerConfig;
|
||||||
notifications: NotificationOptions;
|
notifications: NotificationOptions;
|
||||||
queue: MessageQueue;
|
queue: MessageQueue;
|
||||||
|
|
@ -61,7 +64,7 @@ export default class State {
|
||||||
this.experiments = new Experiments();
|
this.experiments = new Experiments();
|
||||||
this.layout = new Layout();
|
this.layout = new Layout();
|
||||||
this.config = new ServerConfig();
|
this.config = new ServerConfig();
|
||||||
this.notifications = new NotificationOptions();
|
this.notifications = new NotificationOptions(this);
|
||||||
this.queue = new MessageQueue();
|
this.queue = new MessageQueue();
|
||||||
this.settings = new Settings();
|
this.settings = new Settings();
|
||||||
this.sync = new Sync(this);
|
this.sync = new Sync(this);
|
||||||
|
|
@ -159,6 +162,17 @@ export default class State {
|
||||||
// Register listener for incoming packets.
|
// Register listener for incoming packets.
|
||||||
client.addListener("packet", this.onPacket);
|
client.addListener("packet", this.onPacket);
|
||||||
|
|
||||||
|
// Register events for notifications.
|
||||||
|
client.addListener("message", this.notifications.onMessage);
|
||||||
|
client.addListener(
|
||||||
|
"user/relationship",
|
||||||
|
this.notifications.onRelationship,
|
||||||
|
);
|
||||||
|
document.addEventListener(
|
||||||
|
"visibilitychange",
|
||||||
|
this.notifications.onVisibilityChange,
|
||||||
|
);
|
||||||
|
|
||||||
// Sync settings from remote server.
|
// Sync settings from remote server.
|
||||||
state.sync
|
state.sync
|
||||||
.pull(client)
|
.pull(client)
|
||||||
|
|
@ -253,6 +267,15 @@ export default class State {
|
||||||
if (client) {
|
if (client) {
|
||||||
client.removeListener("message", this.queue.onMessage);
|
client.removeListener("message", this.queue.onMessage);
|
||||||
client.removeListener("packet", this.onPacket);
|
client.removeListener("packet", this.onPacket);
|
||||||
|
client.removeListener("message", this.notifications.onMessage);
|
||||||
|
client.removeListener(
|
||||||
|
"user/relationship",
|
||||||
|
this.notifications.onRelationship,
|
||||||
|
);
|
||||||
|
document.removeEventListener(
|
||||||
|
"visibilitychange",
|
||||||
|
this.notifications.onVisibilityChange,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wipe all listeners.
|
// Wipe all listeners.
|
||||||
|
|
@ -293,7 +316,7 @@ export default class State {
|
||||||
this.draft = new Draft();
|
this.draft = new Draft();
|
||||||
this.experiments = new Experiments();
|
this.experiments = new Experiments();
|
||||||
this.layout = new Layout();
|
this.layout = new Layout();
|
||||||
this.notifications = new NotificationOptions();
|
this.notifications = new NotificationOptions(this);
|
||||||
this.queue = new MessageQueue();
|
this.queue = new MessageQueue();
|
||||||
this.settings = new Settings();
|
this.settings = new Settings();
|
||||||
this.sync = new Sync(this);
|
this.sync = new Sync(this);
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,14 @@
|
||||||
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
|
import { action, computed, makeAutoObservable, ObservableMap } from "mobx";
|
||||||
import { Channel, Message, Server } from "revolt.js";
|
import { Channel, Message, Server, User } from "revolt.js";
|
||||||
|
import { decodeTime } from "ulid";
|
||||||
|
|
||||||
|
import { translate } from "preact-i18n";
|
||||||
|
|
||||||
import { mapToRecord } from "../../lib/conversion";
|
import { mapToRecord } from "../../lib/conversion";
|
||||||
|
|
||||||
|
import { history, routeInformation } from "../../context/history";
|
||||||
|
|
||||||
|
import State from "../State";
|
||||||
import Persistent from "../interfaces/Persistent";
|
import Persistent from "../interfaces/Persistent";
|
||||||
import Store from "../interfaces/Store";
|
import Store from "../interfaces/Store";
|
||||||
import Syncable from "../interfaces/Syncable";
|
import Syncable from "../interfaces/Syncable";
|
||||||
|
|
@ -37,22 +43,54 @@ export interface Data {
|
||||||
channel?: Record<string, NotificationState>;
|
channel?: Record<string, NotificationState>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a notification either directly or using service worker.
|
||||||
|
* @param title Notification Title
|
||||||
|
* @param options Notification Options
|
||||||
|
* @returns Notification
|
||||||
|
*/
|
||||||
|
async function createNotification(
|
||||||
|
title: string,
|
||||||
|
options: globalThis.NotificationOptions,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
return new Notification(title, options);
|
||||||
|
} catch (err) {
|
||||||
|
const sw = await navigator.serviceWorker.getRegistration();
|
||||||
|
sw?.showNotification(title, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manages the user's notification preferences.
|
* Manages the user's notification preferences.
|
||||||
*/
|
*/
|
||||||
export default class NotificationOptions
|
export default class NotificationOptions
|
||||||
implements Store, Persistent<Data>, Syncable
|
implements Store, Persistent<Data>, Syncable
|
||||||
{
|
{
|
||||||
|
private state: State;
|
||||||
|
private activeNotifications: Record<string, Notification>;
|
||||||
|
|
||||||
private server: ObservableMap<string, NotificationState>;
|
private server: ObservableMap<string, NotificationState>;
|
||||||
private channel: ObservableMap<string, NotificationState>;
|
private channel: ObservableMap<string, NotificationState>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct new Experiments store.
|
* Construct new Experiments store.
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor(state: State) {
|
||||||
this.server = new ObservableMap();
|
this.server = new ObservableMap();
|
||||||
this.channel = new ObservableMap();
|
this.channel = new ObservableMap();
|
||||||
makeAutoObservable(this);
|
|
||||||
|
makeAutoObservable(this, {
|
||||||
|
onMessage: false,
|
||||||
|
onRelationship: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.state = state;
|
||||||
|
this.activeNotifications = {};
|
||||||
|
|
||||||
|
this.onMessage = this.onMessage.bind(this);
|
||||||
|
this.onRelationship = this.onRelationship.bind(this);
|
||||||
|
this.onVisibilityChange = this.onVisibilityChange.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
|
|
@ -209,6 +247,245 @@ export default class NotificationOptions
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle incoming messages and create a notification.
|
||||||
|
* @param message Message
|
||||||
|
*/
|
||||||
|
async onMessage(message: Message) {
|
||||||
|
// Ignore if we are currently looking and focused on the channel.
|
||||||
|
if (
|
||||||
|
message.channel_id === routeInformation.getChannel() &&
|
||||||
|
document.hasFocus()
|
||||||
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Ignore if muted.
|
||||||
|
if (!this.shouldNotify(message)) return;
|
||||||
|
|
||||||
|
// Play a sound and skip notif if disabled.
|
||||||
|
this.state.settings.sounds.playSound("message");
|
||||||
|
if (!this.state.settings.get("notifications:desktop")) return;
|
||||||
|
|
||||||
|
const effectiveName =
|
||||||
|
message.masquerade?.name ?? message.author?.username;
|
||||||
|
|
||||||
|
let title;
|
||||||
|
switch (message.channel?.channel_type) {
|
||||||
|
case "SavedMessages":
|
||||||
|
return;
|
||||||
|
case "DirectMessage":
|
||||||
|
title = `@${effectiveName}`;
|
||||||
|
break;
|
||||||
|
case "Group":
|
||||||
|
if (message.author?._id === "00000000000000000000000000") {
|
||||||
|
title = message.channel.name;
|
||||||
|
} else {
|
||||||
|
title = `@${effectiveName} - ${message.channel.name}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "TextChannel":
|
||||||
|
title = `@${effectiveName} (#${message.channel.name}, ${message.channel.server?.name})`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
title = message.channel?._id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let image;
|
||||||
|
if (message.attachments) {
|
||||||
|
const imageAttachment = message.attachments.find(
|
||||||
|
(x) => x.metadata.type === "Image",
|
||||||
|
);
|
||||||
|
if (imageAttachment) {
|
||||||
|
image = message.client.generateFileURL(imageAttachment, {
|
||||||
|
max_side: 720,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let body, icon;
|
||||||
|
if (message.content) {
|
||||||
|
body = message.client.markdownToText(message.content);
|
||||||
|
|
||||||
|
if (message.masquerade?.avatar) {
|
||||||
|
icon = message.client.proxyFile(message.masquerade.avatar);
|
||||||
|
} else {
|
||||||
|
icon = message.author?.generateAvatarURL({ max_side: 256 });
|
||||||
|
}
|
||||||
|
} else if (message.system) {
|
||||||
|
const users = message.client.users;
|
||||||
|
|
||||||
|
// ! FIXME: I've had to strip translations while
|
||||||
|
// ! I move stuff into the new project structure
|
||||||
|
switch (message.system.type) {
|
||||||
|
case "user_added":
|
||||||
|
case "user_remove":
|
||||||
|
{
|
||||||
|
const user = users.get(message.system.id);
|
||||||
|
body = `${user?.username} ${
|
||||||
|
message.system.type === "user_added"
|
||||||
|
? "added by"
|
||||||
|
: "removed by"
|
||||||
|
} ${users.get(message.system.by)?.username}`;
|
||||||
|
/*body = translate(
|
||||||
|
`app.main.channel.system.${
|
||||||
|
message.system.type === "user_added"
|
||||||
|
? "added_by"
|
||||||
|
: "removed_by"
|
||||||
|
}`,
|
||||||
|
{
|
||||||
|
user: user?.username,
|
||||||
|
other_user: users.get(message.system.by)
|
||||||
|
?.username,
|
||||||
|
},
|
||||||
|
);*/
|
||||||
|
icon = user?.generateAvatarURL({
|
||||||
|
max_side: 256,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "user_joined":
|
||||||
|
case "user_left":
|
||||||
|
case "user_kicked":
|
||||||
|
case "user_banned":
|
||||||
|
{
|
||||||
|
const user = users.get(message.system.id);
|
||||||
|
body = `${user?.username}`;
|
||||||
|
/*body = translate(
|
||||||
|
`app.main.channel.system.${message.system.type}`,
|
||||||
|
{ user: user?.username },
|
||||||
|
);*/
|
||||||
|
icon = user?.generateAvatarURL({
|
||||||
|
max_side: 256,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "channel_renamed":
|
||||||
|
{
|
||||||
|
const user = users.get(message.system.by);
|
||||||
|
body = `${user?.username} renamed channel to ${message.system.name}`;
|
||||||
|
/*body = translate(
|
||||||
|
`app.main.channel.system.channel_renamed`,
|
||||||
|
{
|
||||||
|
user: users.get(message.system.by)?.username,
|
||||||
|
name: message.system.name,
|
||||||
|
},
|
||||||
|
);*/
|
||||||
|
icon = user?.generateAvatarURL({
|
||||||
|
max_side: 256,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "channel_description_changed":
|
||||||
|
case "channel_icon_changed":
|
||||||
|
{
|
||||||
|
const user = users.get(message.system.by);
|
||||||
|
/*body = translate(
|
||||||
|
`app.main.channel.system.${message.system.type}`,
|
||||||
|
{ user: users.get(message.system.by)?.username },
|
||||||
|
);*/
|
||||||
|
body = `${users.get(message.system.by)?.username}`;
|
||||||
|
icon = user?.generateAvatarURL({
|
||||||
|
max_side: 256,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const notif = await createNotification(title!, {
|
||||||
|
icon,
|
||||||
|
image,
|
||||||
|
body,
|
||||||
|
timestamp: decodeTime(message._id),
|
||||||
|
tag: message.channel?._id,
|
||||||
|
badge: "/assets/icons/android-chrome-512x512.png",
|
||||||
|
silent: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (notif) {
|
||||||
|
notif.addEventListener("click", () => {
|
||||||
|
window.focus();
|
||||||
|
|
||||||
|
const id = message.channel_id;
|
||||||
|
if (id !== routeInformation.getChannel()) {
|
||||||
|
const channel = message.client.channels.get(id);
|
||||||
|
if (channel) {
|
||||||
|
if (channel.channel_type === "TextChannel") {
|
||||||
|
history.push(
|
||||||
|
`/server/${channel.server_id}/channel/${id}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
history.push(`/channel/${id}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.activeNotifications[message.channel_id] = notif;
|
||||||
|
|
||||||
|
notif.addEventListener(
|
||||||
|
"close",
|
||||||
|
() => delete this.activeNotifications[message.channel_id],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle user relationship changes.
|
||||||
|
* @param user User relationship changed with
|
||||||
|
*/
|
||||||
|
async onRelationship(user: User) {
|
||||||
|
// Ignore if disabled.
|
||||||
|
if (!this.state.settings.get("notifications:desktop")) return;
|
||||||
|
|
||||||
|
// Check whether we are busy.
|
||||||
|
// This is checked by `shouldNotify` in the case of messages.
|
||||||
|
if (user.status?.presence === "Busy") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let event;
|
||||||
|
switch (user.relationship) {
|
||||||
|
case "Incoming":
|
||||||
|
/*event = translate("notifications.sent_request", {
|
||||||
|
person: user.username,
|
||||||
|
});*/
|
||||||
|
event = `${user.username} sent you a friend request`;
|
||||||
|
break;
|
||||||
|
case "Friend":
|
||||||
|
/*event = translate("notifications.now_friends", {
|
||||||
|
person: user.username,
|
||||||
|
});*/
|
||||||
|
event = `Now friends with ${user.username}`;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const notif = await createNotification(event, {
|
||||||
|
icon: user.generateAvatarURL({ max_side: 256 }),
|
||||||
|
badge: "/assets/icons/android-chrome-512x512.png",
|
||||||
|
timestamp: +new Date(),
|
||||||
|
});
|
||||||
|
|
||||||
|
notif?.addEventListener("click", () => {
|
||||||
|
history.push(`/friends`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when document visibility changes.
|
||||||
|
*/
|
||||||
|
onVisibilityChange() {
|
||||||
|
if (document.visibilityState === "visible") {
|
||||||
|
const channel_id = routeInformation.getChannel()!;
|
||||||
|
if (this.activeNotifications[channel_id]) {
|
||||||
|
this.activeNotifications[channel_id].close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@action apply(_key: "notifications", data: unknown, _revision: number) {
|
@action apply(_key: "notifications", data: unknown, _revision: number) {
|
||||||
this.hydrate(data as Data);
|
this.hydrate(data as Data);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@ import ContextMenus from "../lib/ContextMenus";
|
||||||
import { isTouchscreenDevice } from "../lib/isTouchscreenDevice";
|
import { isTouchscreenDevice } from "../lib/isTouchscreenDevice";
|
||||||
|
|
||||||
import Popovers from "../context/intermediate/Popovers";
|
import Popovers from "../context/intermediate/Popovers";
|
||||||
import Notifications from "../context/revoltjs/Notifications";
|
|
||||||
|
|
||||||
import { Titlebar } from "../components/native/Titlebar";
|
import { Titlebar } from "../components/native/Titlebar";
|
||||||
import BottomNavigation from "../components/navigation/BottomNavigation";
|
import BottomNavigation from "../components/navigation/BottomNavigation";
|
||||||
|
|
@ -227,7 +226,6 @@ export default function App() {
|
||||||
</Routes>
|
</Routes>
|
||||||
<ContextMenus />
|
<ContextMenus />
|
||||||
<Popovers />
|
<Popovers />
|
||||||
<Notifications />
|
|
||||||
</OverlappingPanels>
|
</OverlappingPanels>
|
||||||
</AppContainer>
|
</AppContainer>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue