+
{!!getCurrentChannel()?.guild_id && (
+ }
+ lowerBadgeWidth={20}
+ lowerBadgeHeight={20}
+ >
+
+
+
+
+ );
+ }, { noop: true }),
+
+ async checkNewSessions() {
+ const data = await RestAPI.get({
+ url: "/auth/sessions"
+ });
+
+ for (const session of data.body.user_sessions) {
+ if (savedSessionsCache.has(session.id_hash)) continue;
+
+ savedSessionsCache.set(session.id_hash, { name: "", isNew: true });
+ showNotification({
+ title: "BetterSessions",
+ body: `New session:\n${session.client_info.os} · ${session.client_info.platform} · ${session.client_info.location}`,
+ permanent: true,
+ onClick: () => UserSettingsModal.open("Sessions")
+ });
+ }
+
+ saveSessionsToDataStore();
+ },
+
+ flux: {
+ USER_SETTINGS_ACCOUNT_RESET_AND_CLOSE_FORM() {
+ const lastFetchedHashes: string[] = AuthSessionsStore.getSessions().map((session: SessionInfo["session"]) => session.id_hash);
+
+ // Add new sessions to cache
+ lastFetchedHashes.forEach(idHash => {
+ if (!savedSessionsCache.has(idHash)) savedSessionsCache.set(idHash, { name: "", isNew: false });
+ });
+
+ // Delete removed sessions from cache
+ if (lastFetchedHashes.length > 0) {
+ savedSessionsCache.forEach((_, idHash) => {
+ if (!lastFetchedHashes.includes(idHash)) savedSessionsCache.delete(idHash);
+ });
+ }
+
+ // Dismiss the "NEW" badge of all sessions.
+ // Since the only way for a session to be marked as "NEW" is going to the Devices tab,
+ // closing the settings means they've been viewed and are no longer considered new.
+ savedSessionsCache.forEach(data => {
+ data.isNew = false;
+ });
+ saveSessionsToDataStore();
+ }
+ },
+
+ async start() {
+ await fetchNamesFromDataStore();
+
+ this.checkNewSessions();
+ if (settings.store.backgroundCheck) {
+ this.checkInterval = setInterval(this.checkNewSessions, settings.store.checkInterval * 60 * 1000);
+ }
+ },
+
+ stop() {
+ clearInterval(this.checkInterval);
+ }
+});
diff --git a/src/plugins/betterSessions/types.ts b/src/plugins/betterSessions/types.ts
new file mode 100644
index 000000000..9026d5313
--- /dev/null
+++ b/src/plugins/betterSessions/types.ts
@@ -0,0 +1,32 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
.
+*/
+
+export interface SessionInfo {
+ session: {
+ id_hash: string;
+ approx_last_used_time: Date;
+ client_info: {
+ os: string;
+ platform: string;
+ location: string;
+ };
+ },
+ current?: boolean;
+}
+
+export type Session = SessionInfo["session"];
diff --git a/src/plugins/betterSessions/utils.ts b/src/plugins/betterSessions/utils.ts
new file mode 100644
index 000000000..3015dc47c
--- /dev/null
+++ b/src/plugins/betterSessions/utils.ts
@@ -0,0 +1,90 @@
+/*
+ * Vencord, a modification for Discord's desktop app
+ * Copyright (c) 2023 Vendicated and contributors
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see
.
+*/
+
+import { DataStore } from "@api/index";
+import { UserStore } from "@webpack/common";
+
+import { ChromeIcon, DiscordIcon, EdgeIcon, FirefoxIcon, IEIcon, MobileIcon, OperaIcon, SafariIcon, UnknownIcon } from "./components/icons";
+import { SessionInfo } from "./types";
+
+const getDataKey = () => `BetterSessions_savedSessions_${UserStore.getCurrentUser().id}`;
+
+export const savedSessionsCache: Map
= new Map();
+
+export function getDefaultName(clientInfo: SessionInfo["session"]["client_info"]) {
+ return `${clientInfo.os} · ${clientInfo.platform}`;
+}
+
+export function saveSessionsToDataStore() {
+ return DataStore.set(getDataKey(), savedSessionsCache);
+}
+
+export async function fetchNamesFromDataStore() {
+ const savedSessions = await DataStore.get