Skip to content

Commit

Permalink
Merge pull request #4155 from traPtitech/SSlime/imp-settings-session-tab
Browse files Browse the repository at this point in the history
マージ権限ないの忘れてたのでマージします
  • Loading branch information
mehm8128 committed Feb 5, 2024
2 parents 3294f40 + 5758e92 commit 4f0340c
Show file tree
Hide file tree
Showing 9 changed files with 170 additions and 86 deletions.
2 changes: 1 addition & 1 deletion public/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
blogPagePrefix: 'https://trap.jp/author/',
auth: {
resetLink: 'https://portal.trap.jp/reset-password',
changeLink: 'https://portal.trap.jp',
changeLink: 'https://portal.trap.jp/me/change-password',
changeName: 'traPortal'
},
isRootChannelSelectableAsParentChannel: false,
Expand Down
12 changes: 5 additions & 7 deletions src/components/Settings/SessionTab/AccountState.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<div>
<section>
<h3 :class="$style.header">アカウント</h3>
<div :class="$style.content">
<div>
<form-button
label="ログアウト"
:class="$style.logout"
Expand All @@ -15,7 +15,7 @@
@click="onSessionDelete"
/>
</div>
</div>
</section>
</template>

<script lang="ts" setup>
Expand Down Expand Up @@ -46,11 +46,9 @@ const onSessionDelete = async () => {

<style lang="scss" module>
.header {
margin-bottom: 8px;
}
.content {
margin-left: 12px;
margin-bottom: 4px;
}
.logout {
margin-right: 8px;
margin-bottom: 8px;
Expand Down
26 changes: 19 additions & 7 deletions src/components/Settings/SessionTab/PasswordChange.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
<template>
<div>
<section>
<h3 :class="$style.header">パスワード変更</h3>
<p v-if="showChangeLink">
パスワードの変更は
<a :href="changeLink" rel="noopener noreferrer" target="_blank">{{
changeName
}}</a>
から可能です
<a
:class="$style.link"
:href="changeLink"
rel="noopener noreferrer"
target="_blank"
>{{ changeName }}</a
>
から可能です。
</p>
<template v-else>
<form-input
Expand Down Expand Up @@ -34,7 +38,7 @@
/>
</div>
</template>
</div>
</section>
</template>

<script lang="ts">
Expand Down Expand Up @@ -91,11 +95,19 @@ const { isChanging, onChangeClick } = usePasswordChange(state, isValid)

<style lang="scss" module>
.header {
margin-bottom: 8px;
margin-bottom: 4px;
}
.form {
margin-left: 12px;
}
.link {
color: var(--markdown-link-text);
text-decoration: underline;
&:hover {
text-decoration: none;
}
}
.changeButton {
display: flex;
justify-content: center;
Expand Down
138 changes: 98 additions & 40 deletions src/components/Settings/SessionTab/TokenInfo.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,44 @@
<template>
<div :key="token.id" :class="$style.token">
<div :class="$style.revoke">
<a-icon :class="$style.icon" name="close" mdi @click="emit('revoke')" />
</div>
<li :key="token.id" :class="$style.wrap">
<div :class="$style.title">{{ token.clientName ?? '---' }}</div>
<div :class="$style.desc">{{ token.clientDesc }}</div>
<div :class="$style.developer">
開発者:
<template v-if="token.clientDeveloper">
<user-icon
:class="$style.developerIcon"
:user-id="token.clientDeveloper.id"
:size="24"
prevent-modal
/>
@{{ token.clientDeveloper.name }}
</template>
<template v-else>---</template>
<div :class="$style.description">{{ token.clientDesc }}</div>
<div :class="$style.details">
<div :class="$style.developer">
<template v-if="token.clientDeveloper">
<user-icon
:class="$style.developerIcon"
:user-id="token.clientDeveloper.id"
:size="24"
prevent-modal
aria-label=""
/>
@{{ token.clientDeveloper.name }}
</template>
<template v-else>unknown</template>
</div>
<time :datetime="issuedAtISO">{{ issuedAt }}</time>
<div :class="$style.scopes">
<div v-for="scope in scopes" :key="scope" :class="$style.scope">
{{ scope }}
</div>
</div>
</div>
<div :class="$style.issuedAt">許可日時: {{ issuedAt }}</div>
<div :class="$style.scopes">許可範囲: {{ scopes.join(', ') }}</div>
</div>
<div :class="$style.revoke">
<button
:class="$style.revokeButton"
title="トークンを無効化"
@click="emit('revoke')"
>
<a-icon name="close" mdi />
</button>
</div>
</li>
</template>

<script lang="ts" setup>
import { computed } from 'vue'
import type { ActiveOAuth2Token, User } from '@traptitech/traq'
import { getFullDayWithTimeString } from '/@/lib/basic/date'
import { getFullDayString, getISOFullDayString } from '/@/lib/basic/date'
import { scopeInfoMap } from '/@/lib/clientScope'
import AIcon from '/@/components/UI/AIcon.vue'
import UserIcon from '/@/components/UI/UserIcon.vue'
Expand All @@ -45,54 +57,100 @@ const emit = defineEmits<{
(e: 'revoke'): void
}>()

const issuedAt = computed(() =>
getFullDayWithTimeString(new Date(props.token.issuedAt))
)
const issuedAtISO = computed(() => {
const date = new Date(props.token.issuedAt)
if (Number.isNaN(date.getTime())) return ''

return getISOFullDayString(date)
})
const issuedAt = computed(() => {
const date = new Date(props.token.issuedAt)
if (Number.isNaN(date.getTime())) return 'Invalid Date'

return getFullDayString(new Date(props.token.issuedAt))
})

const scopes = computed(() =>
props.token.scopes.map(scope => scopeInfoMap[scope].name)
)
</script>

<style lang="scss" module>
.token {
.wrap {
display: grid;
grid-template:
'revoke title' auto
'revoke desc' auto
'revoke developer' auto
'revoke issuedAt' auto
'revoke scopes' auto / min-content 1fr;
'title revoke' auto
'description revoke' auto
'details revoke' auto / 1fr min-content;
word-break: normal;
overflow-wrap: break-word; // for Safari
overflow-wrap: anywhere;

padding: 12px 0;
gap: 4px 24px;
}
.revoke {
grid-area: revoke;

display: flex;
align-items: center;
grid-area: revoke;
margin-right: 4px;
}
.title {
@include color-ui-primary;
grid-area: title;

@include color-ui-primary;
font-weight: bold;
}
.desc {
grid-area: desc;
.description {
grid-area: description;
}

.details {
grid-area: details;

display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px 16px;
font-size: 0.875rem;
}

.developer {
grid-area: developer;
display: flex;
align-items: center;
}

.developerIcon {
display: inline-block;
vertical-align: middle;

margin-right: 4px;
}
.issuedAt {
grid-area: issuedAt;
}

.scopes {
grid-area: scopes;
display: flex;
flex-wrap: wrap;
gap: 4px 8px;
}
.icon {

.scope {
color: var(--theme-background-primary-default);
background: var(--theme-ui-secondary-default);
padding: 0.125rem 1rem;
font-size: 0.75rem;
border-radius: 9999px;
}
.revokeButton {
@include color-ui-tertiary;
cursor: pointer;
display: grid;
padding: 4px;
border-radius: 4px;

&:hover {
@include color-ui-secondary;
@include background-secondary;
}
}
</style>
27 changes: 15 additions & 12 deletions src/components/Settings/SessionTab/TokenManager.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
<template>
<div>
<section>
<h3 :class="$style.header">アクセスを許可しているアプリ</h3>
<div :class="$style.content">
<ul v-if="tokensWithClientData.length > 0" :class="$style.content">
<token-info
v-for="token in tokensWithClientData"
:key="token.id"
:token="token"
@revoke="revokeToken(token.id)"
/>
</div>
</div>
</ul>
<p v-else-if="!isLoading">アクセスを許可しているアプリはありません。</p>
<p v-else>Now loading...</p>
</section>
</template>

<script lang="ts" setup>
Expand All @@ -27,6 +29,7 @@ fetchUsers()
const tokens = ref<ActiveOAuth2Token[]>([])
const clients = ref<Map<OAuthClientId, OAuth2Client>>(new Map())
const isLoading = ref(true)
const tokensWithClientData = computed(() =>
[...tokens.value]
.sort((a, b) => Date.parse(b.issuedAt) - Date.parse(a.issuedAt))
Expand All @@ -44,10 +47,12 @@ const tokensWithClientData = computed(() =>
const fetchTokens = async () => {
const res = await apis.getMyTokens()
tokens.value = res.data
isLoading.value = false
}
const fetchClients = async () => {
const res = await apis.getClients(true)
clients.value = new Map(res.data.map(client => [client.id, client]))
isLoading.value = false
}
const revokeToken = async (tokenId: string) => {
if (!window.confirm('本当にトークンを無効化しますか?')) return
Expand All @@ -69,18 +74,16 @@ onMounted(() => {

<style lang="scss" module>
.header {
margin-bottom: 8px;
margin-bottom: 4px;
}
.content {
@include color-ui-secondary;
@include background-secondary;
@include color-ui-primary;
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
grid-auto-rows: max-content;
height: 400px;
padding: 8px;
margin-left: 12px;
gap: 16px;
padding: 0 4px;
gap: 4px;
border-radius: 8px;
overflow-y: scroll;
overflow-y: auto;
}
</style>
34 changes: 17 additions & 17 deletions src/components/Settings/SessionTab/ViewStates.vue
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
<template>
<div>
<h3 :class="$style.header">閲覧中チャンネル一覧</h3>
<div :class="$style.content">
<template v-if="monitoringChanelStrings.length > 0">
<p>他端末/ブラウザ/タブで開いているチャンネルは以下の通りです</p>
<ul>
<li v-for="channel in monitoringChanelStrings" :key="channel">
{{ channel }}
</li>
</ul>
</template>
<p v-else>他端末/ブラウザ/タブで開いているチャンネルはありません</p>
</div>
</div>
<section>
<h3 :class="$style.header">
他端末/ブラウザで最新のメッセージを開いているチャンネル
</h3>
<ul v-if="monitoringChanelStrings.length > 0" :class="$style.list">
<li v-for="channel in monitoringChanelStrings" :key="channel">
{{ channel }}
</li>
</ul>
<p v-else>他端末/ブラウザで開いているチャンネルはありません。</p>
</section>
</template>

<script lang="ts" setup>
Expand Down Expand Up @@ -42,9 +39,12 @@ const monitoringChanelStrings = computed(() =>

<style lang="scss" module>
.header {
margin-bottom: 8px;
margin-bottom: 4px;
}
.content {
margin-left: 12px;
ul.list {
list-style: disc;
margin-left: 0;
padding-left: 24px;
}
</style>
Loading

0 comments on commit 4f0340c

Please sign in to comment.