2021-06-19 17:37:12 -04:00
|
|
|
import dayjs from "dayjs";
|
|
|
|
import { decodeTime } from "ulid";
|
|
|
|
import { Text } from "preact-i18n";
|
|
|
|
import styles from "./Panes.module.scss";
|
|
|
|
import Tip from "../../../components/ui/Tip";
|
|
|
|
import { useHistory } from "react-router-dom";
|
|
|
|
import Button from "../../../components/ui/Button";
|
|
|
|
import Preloader from "../../../components/ui/Preloader";
|
|
|
|
import { useContext, useEffect, useState } from "preact/hooks";
|
|
|
|
import { AppContext } from "../../../context/revoltjs/RevoltClient";
|
|
|
|
|
|
|
|
import { HelpCircle } from "@styled-icons/feather";
|
|
|
|
import {
|
|
|
|
Android,
|
|
|
|
Firefoxbrowser,
|
|
|
|
Googlechrome,
|
|
|
|
Ios,
|
|
|
|
Linux,
|
|
|
|
Macos,
|
|
|
|
Microsoftedge,
|
|
|
|
Safari,
|
|
|
|
Windows
|
|
|
|
} from "@styled-icons/simple-icons";
|
|
|
|
|
|
|
|
import relativeTime from "dayjs/plugin/relativeTime";
|
|
|
|
dayjs.extend(relativeTime);
|
|
|
|
|
|
|
|
interface Session {
|
|
|
|
id: string;
|
|
|
|
friendly_name: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function Sessions() {
|
|
|
|
const client = useContext(AppContext);
|
|
|
|
const deviceId = client.session?.id;
|
|
|
|
|
|
|
|
const [sessions, setSessions] = useState<Session[] | undefined>(undefined);
|
|
|
|
const [attemptingDelete, setDelete] = useState<string[]>([]);
|
|
|
|
const history = useHistory();
|
|
|
|
|
|
|
|
function switchPage(to: string) {
|
|
|
|
history.replace(`/settings/${to}`);
|
|
|
|
}
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
client.req("GET", "/auth/sessions").then(data => {
|
|
|
|
data.sort(
|
|
|
|
(a, b) =>
|
|
|
|
(b.id === deviceId ? 1 : 0) - (a.id === deviceId ? 1 : 0)
|
|
|
|
);
|
|
|
|
setSessions(data);
|
|
|
|
});
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
if (typeof sessions === "undefined") {
|
|
|
|
return (
|
|
|
|
<div className={styles.loader}>
|
2021-06-22 11:29:47 -04:00
|
|
|
<Preloader type="ring" />
|
2021-06-19 17:37:12 -04:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
function getIcon(session: Session) {
|
|
|
|
const name = session.friendly_name;
|
|
|
|
switch (true) {
|
|
|
|
case /firefox/i.test(name):
|
|
|
|
return <Firefoxbrowser />;
|
|
|
|
case /chrome/i.test(name):
|
|
|
|
return <Googlechrome />;
|
|
|
|
case /safari/i.test(name):
|
|
|
|
return <Safari />;
|
|
|
|
case /edge/i.test(name):
|
|
|
|
return <Microsoftedge />;
|
|
|
|
default:
|
|
|
|
return <HelpCircle />;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSystemIcon(session: Session) {
|
|
|
|
const name = session.friendly_name;
|
|
|
|
switch (true) {
|
|
|
|
case /linux/i.test(name):
|
|
|
|
return <Linux />;
|
|
|
|
case /android/i.test(name):
|
|
|
|
return <Android />;
|
|
|
|
case /mac.*os/i.test(name):
|
|
|
|
return <Macos />;
|
|
|
|
case /ios/i.test(name):
|
|
|
|
return <Ios />;
|
|
|
|
case /windows/i.test(name):
|
|
|
|
return <Windows />;
|
|
|
|
default:
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const mapped = sessions.map(session => {
|
|
|
|
return {
|
|
|
|
...session,
|
|
|
|
timestamp: decodeTime(session.id)
|
|
|
|
};
|
|
|
|
});
|
|
|
|
|
|
|
|
mapped.sort((a, b) => b.timestamp - a.timestamp);
|
|
|
|
let id = mapped.findIndex(x => x.id === deviceId);
|
|
|
|
|
|
|
|
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>
|
|
|
|
{render.map(session => (
|
|
|
|
<div
|
|
|
|
className={styles.entry}
|
|
|
|
data-active={session.id === deviceId}
|
|
|
|
data-deleting={attemptingDelete.indexOf(session.id) > -1}
|
|
|
|
>
|
|
|
|
{deviceId === session.id && (
|
|
|
|
<span className={styles.label}>
|
|
|
|
<Text id="app.settings.pages.sessions.this_device" />{" "}
|
|
|
|
</span>
|
|
|
|
)}
|
|
|
|
<div className={styles.session}>
|
|
|
|
<div className={styles.icon}>
|
|
|
|
{getIcon(session)}
|
|
|
|
<div>{getSystemIcon(session)}</div>
|
|
|
|
</div>
|
|
|
|
<div className={styles.info}>
|
|
|
|
<span className={styles.name}>
|
|
|
|
{session.friendly_name}
|
|
|
|
</span>
|
|
|
|
<span className={styles.time}>
|
|
|
|
<Text
|
|
|
|
id="app.settings.pages.sessions.created"
|
|
|
|
fields={{
|
|
|
|
time_ago: dayjs(
|
|
|
|
session.timestamp
|
|
|
|
).fromNow()
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
</span>
|
|
|
|
</div>
|
|
|
|
{deviceId !== session.id && (
|
|
|
|
<Button
|
|
|
|
onClick={async () => {
|
|
|
|
setDelete([
|
|
|
|
...attemptingDelete,
|
|
|
|
session.id
|
|
|
|
]);
|
|
|
|
await client.req(
|
|
|
|
"DELETE",
|
|
|
|
`/auth/sessions/${session.id}` as any
|
|
|
|
);
|
|
|
|
setSessions(
|
|
|
|
sessions?.filter(
|
|
|
|
x => x.id !== session.id
|
|
|
|
)
|
|
|
|
);
|
|
|
|
}}
|
|
|
|
disabled={
|
|
|
|
attemptingDelete.indexOf(session.id) > -1
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<Text id="app.settings.pages.logOut" />
|
|
|
|
</Button>
|
|
|
|
)}
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
))}
|
|
|
|
<Tip>
|
|
|
|
<span>
|
|
|
|
<Text id="app.settings.tips.sessions.a" />
|
|
|
|
</span>{" "}
|
|
|
|
<a onClick={() => switchPage("account")}>
|
|
|
|
<Text id="app.settings.tips.sessions.b" />
|
|
|
|
</a>
|
|
|
|
</Tip>
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|