revite/src/pages/settings/panes/Sessions.tsx

245 lines
9.2 KiB
TypeScript
Raw Normal View History

import { Chrome, Android, Apple, Windows } from "@styled-icons/boxicons-logos";
import { HelpCircle, Desktop } from "@styled-icons/boxicons-regular";
import {
Safari,
Firefoxbrowser,
Microsoftedge,
Linux,
Macos,
Opera,
2021-09-04 14:59:56 +00:00
Samsung,
} from "@styled-icons/simple-icons";
2021-07-05 11:23:23 +01:00
import relativeTime from "dayjs/plugin/relativeTime";
import { useHistory } from "react-router-dom";
2021-09-11 17:36:23 +01:00
import { SessionInfo } from "revolt-api/types/Auth";
2021-06-19 22:37:12 +01:00
import { decodeTime } from "ulid";
2021-07-05 11:23:23 +01:00
2021-06-19 22:37:12 +01:00
import styles from "./Panes.module.scss";
2021-07-05 11:23:23 +01:00
import { Text } from "preact-i18n";
2021-06-19 22:37:12 +01:00
import { useContext, useEffect, useState } from "preact/hooks";
2021-07-05 11:23:23 +01:00
2021-07-06 19:29:27 +01:00
import { dayjs } from "../../../context/Locale";
2021-06-19 22:37:12 +01:00
import { AppContext } from "../../../context/revoltjs/RevoltClient";
2021-07-05 11:23:23 +01:00
import Button from "../../../components/ui/Button";
import Preloader from "../../../components/ui/Preloader";
import Tip from "../../../components/ui/Tip";
2021-06-19 22:37:12 +01:00
dayjs.extend(relativeTime);
export function Sessions() {
2021-07-05 11:25:20 +01:00
const client = useContext(AppContext);
const deviceId =
2021-09-11 17:36:23 +01:00
typeof client.session === "object" ? client.session._id : undefined;
2021-07-05 11:25:20 +01:00
2021-09-11 17:36:23 +01:00
const [sessions, setSessions] = useState<SessionInfo[] | undefined>(
undefined,
);
2021-07-05 11:25:20 +01:00
const [attemptingDelete, setDelete] = useState<string[]>([]);
const history = useHistory();
function switchPage(to: string) {
history.replace(`/settings/${to}`);
}
useEffect(() => {
2021-09-11 17:36:23 +01:00
client.req("GET", "/auth/session/all").then((data) => {
2021-07-05 11:25:20 +01:00
data.sort(
(a, b) =>
2021-09-11 17:36:23 +01:00
(b._id === deviceId ? 1 : 0) - (a._id === deviceId ? 1 : 0),
2021-07-05 11:25:20 +01:00
);
setSessions(data);
});
2021-08-05 14:47:00 +01:00
}, [client, setSessions, deviceId]);
2021-07-05 11:25:20 +01:00
if (typeof sessions === "undefined") {
return (
<div className={styles.loader}>
<Preloader type="ring" />
</div>
);
}
2021-09-11 17:36:23 +01:00
function getIcon(session: SessionInfo) {
const name = session.name;
2021-07-05 11:25:20 +01:00
switch (true) {
case /firefox/i.test(name):
2021-07-08 14:07:25 +01:00
return <Firefoxbrowser size={32} />;
2021-07-05 11:25:20 +01:00
case /chrome/i.test(name):
2021-07-08 14:19:54 +01:00
return <Chrome size={32} />;
2021-07-05 11:25:20 +01:00
case /safari/i.test(name):
2021-07-08 14:07:25 +01:00
return <Safari size={32} />;
2021-07-05 11:25:20 +01:00
case /edge/i.test(name):
2021-07-08 14:07:25 +01:00
return <Microsoftedge size={32} />;
case /opera/i.test(name):
return <Opera size={32} />;
2021-09-04 14:59:56 +00:00
case /samsung/i.test(name):
return <Samsung size={32} />;
case /desktop/i.test(name):
return <Desktop size={32} />;
2021-07-05 11:25:20 +01:00
default:
2021-07-08 14:07:25 +01:00
return <HelpCircle size={32} />;
2021-07-05 11:25:20 +01:00
}
}
2021-09-11 17:36:23 +01:00
function getSystemIcon(session: SessionInfo) {
const name = session.name;
2021-07-05 11:25:20 +01:00
switch (true) {
case /linux/i.test(name):
2021-07-08 14:07:25 +01:00
return <Linux size={14} />;
2021-07-05 11:25:20 +01:00
case /android/i.test(name):
2021-07-08 14:07:25 +01:00
return <Android size={14} />;
2021-07-05 11:25:20 +01:00
case /mac.*os/i.test(name):
2021-07-08 14:07:25 +01:00
return <Macos size={14} />;
2021-07-05 11:25:20 +01:00
case /ios/i.test(name):
2021-07-08 14:19:54 +01:00
return <Apple size={14} />;
2021-07-05 11:25:20 +01:00
case /windows/i.test(name):
2021-07-08 14:07:25 +01:00
return <Windows size={14} />;
2021-07-05 11:25:20 +01:00
default:
return null;
}
}
const mapped = sessions.map((session) => {
return {
...session,
2021-09-11 17:36:23 +01:00
timestamp: decodeTime(session._id),
2021-07-05 11:25:20 +01:00
};
});
mapped.sort((a, b) => b.timestamp - a.timestamp);
2021-09-11 17:36:23 +01:00
const id = mapped.findIndex((x) => x._id === deviceId);
2021-07-05 11:25:20 +01:00
const render = [
mapped[id],
...mapped.slice(0, id),
...mapped.slice(id + 1, mapped.length),
];
return (
<div className={styles.sessions}>
<h3>
<Text id="app.settings.pages.sessions.active_sessions" />
</h3>
2021-07-08 14:07:25 +01:00
{render.map((session) => {
const systemIcon = getSystemIcon(session);
return (
<div
2021-09-11 17:36:23 +01:00
key={session._id}
2021-07-08 14:07:25 +01:00
className={styles.entry}
2021-09-11 17:36:23 +01:00
data-active={session._id === deviceId}
data-deleting={
2021-09-11 17:36:23 +01:00
attemptingDelete.indexOf(session._id) > -1
}>
2021-09-11 17:36:23 +01:00
{deviceId === session._id && (
2021-07-08 14:07:25 +01:00
<span className={styles.label}>
<Text id="app.settings.pages.sessions.this_device" />{" "}
</span>
)}
<div className={styles.session}>
<div className={styles.detail}>
<svg width={42} height={42} viewBox="0 0 32 32">
2021-07-08 14:07:25 +01:00
<foreignObject
x="0"
y="0"
width="32"
height="32"
mask={
systemIcon
? "url(#session)"
: undefined
}>
2021-07-08 14:07:25 +01:00
{getIcon(session)}
</foreignObject>
<foreignObject
x="18"
y="18"
width="14"
height="14">
{systemIcon}
2021-07-08 14:07:25 +01:00
</foreignObject>
</svg>
<div className={styles.info}>
<input
type="text"
className={styles.name}
2021-09-11 17:36:23 +01:00
value={session.name}
2021-07-08 14:07:25 +01:00
autocomplete="off"
style={{ pointerEvents: "none" }}
/>
2021-07-08 14:07:25 +01:00
<span className={styles.time}>
<Text
id="app.settings.pages.sessions.created"
fields={{
time_ago: dayjs(
session.timestamp,
).fromNow(),
}}
/>
</span>
</div>
2021-07-07 22:15:52 +02:00
</div>
2021-09-11 17:36:23 +01:00
{deviceId !== session._id && (
2021-07-08 14:07:25 +01:00
<Button
onClick={async () => {
setDelete([
...attemptingDelete,
2021-09-11 17:36:23 +01:00
session._id,
2021-07-08 14:07:25 +01:00
]);
await client.req(
"DELETE",
2021-09-11 17:36:23 +01:00
`/auth/session/${session._id}` as "/auth/session/id",
2021-07-08 14:07:25 +01:00
);
setSessions(
sessions?.filter(
2021-09-11 17:36:23 +01:00
(x) => x._id !== session._id,
2021-07-08 14:07:25 +01:00
),
);
}}
disabled={
2021-09-11 17:36:23 +01:00
attemptingDelete.indexOf(session._id) >
-1
2021-07-08 14:07:25 +01:00
}>
<Text id="app.settings.pages.logOut" />
</Button>
)}
2021-07-05 11:25:20 +01:00
</div>
</div>
);
2021-07-08 14:07:25 +01:00
})}
<Button
error
onClick={async () => {
// ! FIXME: add to rAuth
const del: string[] = [];
render.forEach((session) => {
2021-09-11 17:36:23 +01:00
if (deviceId !== session._id) {
del.push(session._id);
}
});
setDelete(del);
for (const id of del) {
await client.req(
"DELETE",
2021-09-11 17:36:23 +01:00
`/auth/session/${id}` as "/auth/session/id",
);
}
2021-09-11 17:36:23 +01:00
setSessions(sessions.filter((x) => x._id === deviceId));
}}>
2021-07-08 01:14:23 +02:00
<Text id="app.settings.pages.sessions.logout" />
</Button>
2021-07-05 11:25:20 +01:00
<Tip>
<span>
<Text id="app.settings.tips.sessions.a" />
</span>{" "}
<a onClick={() => switchPage("account")}>
<Text id="app.settings.tips.sessions.b" />
</a>
</Tip>
</div>
);
2021-06-19 22:37:12 +01:00
}