Files
Modrinth-plus/apps/frontend/src/pages/settings/sessions.vue
Cal H. b81e727204 feat: introduce dependency injection framework (#4091)
* feat: migrate frontend notifications to dependency injection based notificaton manager

* fix: lint

* fix: issues

* fix: compile error + notif binding issue

* refactor: move org context to new DI setup

* feat: migrate app notifications to DI + frontend styling

* fix: sidebar issues

* fix: dont use delete in computed

* fix: import and prop issue

* refactor: move handleError to main notification manager class

* fix: lint & build

* fix: merge issues

* fix: lint issues

* fix: lint issues

---------

Signed-off-by: IMB11 <hendersoncal117@gmail.com>
Signed-off-by: Cal H. <hendersoncal117@gmail.com>
2025-08-13 20:48:52 +00:00

149 lines
4.2 KiB
Vue

<template>
<div class="universal-card">
<h2 class="text-2xl">{{ formatMessage(commonSettingsMessages.sessions) }}</h2>
<p class="preserve-lines">
{{ formatMessage(messages.sessionsDescription) }}
</p>
<div v-for="session in sessions" :key="session.id" class="universal-card recessed session mt-4">
<div>
<div>
<strong>
{{ session.os ?? formatMessage(messages.unknownOsLabel) }}
{{ session.platform ?? formatMessage(messages.unknownPlatformLabel) }}
{{ session.ip }}
</strong>
</div>
<div>
<template v-if="session.city">{{ session.city }}, {{ session.country }} </template>
<span
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(session.last_login),
time: new Date(session.last_login),
})
"
>
{{
formatMessage(messages.lastAccessedAgoLabel, {
ago: formatRelativeTime(session.last_login),
})
}}
</span>
<span
v-tooltip="
formatMessage(commonMessages.dateAtTimeTooltip, {
date: new Date(session.created),
time: new Date(session.created),
})
"
>
{{
formatMessage(messages.createdAgoLabel, {
ago: formatRelativeTime(session.created),
})
}}
</span>
</div>
</div>
<div class="input-group">
<i v-if="session.current">{{ formatMessage(messages.currentSessionLabel) }}</i>
<button v-else class="iconified-button raised-button" @click="revokeSession(session.id)">
<XIcon /> {{ formatMessage(messages.revokeSessionButton) }}
</button>
</div>
</div>
</div>
</template>
<script setup>
import { XIcon } from "@modrinth/assets";
import {
commonMessages,
commonSettingsMessages,
injectNotificationManager,
useRelativeTime,
} from "@modrinth/ui";
definePageMeta({
middleware: "auth",
});
const { addNotification } = injectNotificationManager();
const { formatMessage } = useVIntl();
const formatRelativeTime = useRelativeTime();
const messages = defineMessages({
currentSessionLabel: {
id: "settings.sessions.current-session",
defaultMessage: "Current session",
},
revokeSessionButton: {
id: "settings.sessions.action.revoke-session",
defaultMessage: "Revoke session",
},
createdAgoLabel: {
id: "settings.sessions.created-ago",
defaultMessage: "Created {ago}",
},
sessionsDescription: {
id: "settings.sessions.description",
defaultMessage:
"Here are all the devices that are currently logged in with your Modrinth account. You can log out of each one individually.\n\nIf you see an entry you don't recognize, log out of that device and change your Modrinth account password immediately.",
},
lastAccessedAgoLabel: {
id: "settings.sessions.last-accessed-ago",
defaultMessage: "Last accessed {ago}",
},
unknownOsLabel: {
id: "settings.sessions.unknown-os",
defaultMessage: "Unknown OS",
},
unknownPlatformLabel: {
id: "settings.sessions.unknown-platform",
defaultMessage: "Unknown platform",
},
});
useHead({
title: () => `${formatMessage(commonSettingsMessages.sessions)} - Modrinth`,
});
const { data: sessions, refresh } = await useAsyncData("session/list", () =>
useBaseFetch("session/list"),
);
async function revokeSession(id) {
startLoading();
try {
sessions.value = sessions.value.filter((x) => x.id !== id);
await useBaseFetch(`session/${id}`, {
method: "DELETE",
});
await refresh();
} catch (err) {
addNotification({
title: formatMessage(commonMessages.errorNotificationTitle),
text: err.data ? err.data.description : err,
type: "error",
});
}
stopLoading();
}
</script>
<style lang="scss" scoped>
.session {
display: flex;
flex-direction: column;
gap: 0.5rem;
@media screen and (min-width: 800px) {
flex-direction: row;
align-items: center;
.input-group {
margin-left: auto;
}
}
}
</style>