インストールしたアプリ見れるようにしたり削除できるようにしたり
This commit is contained in:
parent
b8088dc01a
commit
dcd43a17ba
9 changed files with 153 additions and 40 deletions
|
@ -468,6 +468,10 @@ unableToProcess: "操作を完了できません"
|
|||
recentUsed: "最近使用"
|
||||
install: "インストール"
|
||||
uninstall: "アンインストール"
|
||||
installedApps: "インストールされたアプリ"
|
||||
nothing: "ありません"
|
||||
installedDate: "インストール日時"
|
||||
lastUsedDate: "最終使用日時"
|
||||
|
||||
_theme:
|
||||
explore: "テーマを探す"
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="mk-notes" v-size="[{ max: 500 }]">
|
||||
<div class="empty" v-if="empty">
|
||||
<div class="_fullinfo" v-if="empty">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
<div>{{ $t('noNotes') }}</div>
|
||||
</div>
|
||||
|
@ -90,18 +90,6 @@ export default Vue.extend({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.mk-notes {
|
||||
> .empty {
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
|
||||
> img {
|
||||
vertical-align: bottom;
|
||||
height: 128px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
> .notes {
|
||||
> ::v-deep *:not(:last-child) {
|
||||
margin-bottom: var(--marginFull);
|
||||
|
|
98
src/client/pages/apps.vue
Normal file
98
src/client/pages/apps.vue
Normal file
|
@ -0,0 +1,98 @@
|
|||
<template>
|
||||
<div>
|
||||
<portal to="icon"><fa :icon="faPlug"/></portal>
|
||||
<portal to="title">{{ $t('installedApps') }}</portal>
|
||||
|
||||
<mk-pagination :pagination="pagination" class="bfomjevm" ref="list">
|
||||
<template #empty>
|
||||
<div class="_fullinfo">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
<div>{{ $t('nothing') }}</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default="{items}">
|
||||
<div class="token _panel" v-for="token in items" :key="token.id">
|
||||
<img class="icon" :src="token.iconUrl" alt=""/>
|
||||
<div class="body">
|
||||
<div class="name">{{ token.name }}</div>
|
||||
<div class="description">{{ token.description }}</div>
|
||||
<div class="_keyValue">
|
||||
<div>{{ $t('installedDate') }}:</div>
|
||||
<div><mk-time :time="token.createdAt"/></div>
|
||||
</div>
|
||||
<div class="_keyValue">
|
||||
<div>{{ $t('lastUsedDate') }}:</div>
|
||||
<div><mk-time :time="token.lastUsedAt"/></div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="_button" @click="revoke(token)"><fa :icon="faTrashAlt"/></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</mk-pagination>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faTrashAlt, faPlug } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkPagination from '../components/ui/pagination.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.$t('installedApps') as string
|
||||
};
|
||||
},
|
||||
|
||||
components: {
|
||||
MkPagination
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
pagination: {
|
||||
endpoint: 'i/apps',
|
||||
limit: 100,
|
||||
},
|
||||
faTrashAlt, faPlug
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
revoke(token) {
|
||||
this.$root.api('i/revoke-token', { tokenId: token.id }).then(() => {
|
||||
this.$refs.list.reload();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bfomjevm {
|
||||
> .token {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
|
||||
> .icon {
|
||||
display: block;
|
||||
flex-shrink: 0;
|
||||
margin: 0 12px 0 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
> .body {
|
||||
width: calc(100% - 62px);
|
||||
position: relative;
|
||||
|
||||
> .name {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<mk-pagination :pagination="pagination" class="mk-follow-requests" ref="list">
|
||||
<template #empty>
|
||||
<div class="tkdrhpxr">
|
||||
<div class="_fullinfo">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
<div>{{ $t('noFollowRequests') }}</div>
|
||||
</div>
|
||||
|
@ -75,18 +75,6 @@ export default Vue.extend({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.mk-follow-requests {
|
||||
.tkdrhpxr {
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
|
||||
> img {
|
||||
vertical-align: bottom;
|
||||
height: 128px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
> .user {
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
</div>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="no-history" v-if="!fetching && messages.length == 0">
|
||||
<div class="_fullinfo" v-if="!fetching && messages.length == 0">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
<div>{{ $t('noHistory') }}</div>
|
||||
</div>
|
||||
|
@ -287,18 +287,6 @@ export default Vue.extend({
|
|||
}
|
||||
}
|
||||
|
||||
> .no-history {
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
|
||||
> img {
|
||||
vertical-align: bottom;
|
||||
height: 128px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
> .history {
|
||||
> .message {
|
||||
|
|
|
@ -32,7 +32,9 @@
|
|||
<x-integration/>
|
||||
<x-api/>
|
||||
|
||||
<mk-button @click="$root.signout()" primary style="margin: var(--margin) auto;">{{ $t('logout') }}</mk-button>
|
||||
<router-link class="_panel _buttonPrimary" to="/my/apps" style="margin: var(--margin) auto;">{{ $t('installedApps') }}</router-link>
|
||||
|
||||
<button class="_panel _buttonPrimary" @click="$root.signout()" style="margin: var(--margin) auto;">{{ $t('logout') }}</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ export const router = new VueRouter({
|
|||
{ path: '/my/groups', component: page('my-groups/index') },
|
||||
{ path: '/my/groups/:group', component: page('my-groups/group') },
|
||||
{ path: '/my/antennas', component: page('my-antennas/index') },
|
||||
{ path: '/my/apps', component: page('apps') },
|
||||
{ path: '/preferences', component: page('preferences/index') },
|
||||
{ path: '/instance', component: page('instance/index') },
|
||||
{ path: '/instance/emojis', component: page('instance/emojis') },
|
||||
|
|
|
@ -412,6 +412,26 @@ main ._panel {
|
|||
}
|
||||
}
|
||||
|
||||
._fullinfo {
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
|
||||
> img {
|
||||
vertical-align: bottom;
|
||||
height: 128px;
|
||||
margin-bottom: 16px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
._keyValue {
|
||||
display: flex;
|
||||
|
||||
> div {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
._link {
|
||||
color: var(--link);
|
||||
}
|
||||
|
|
24
src/server/api/endpoints/i/revoke-token.ts
Normal file
24
src/server/api/endpoints/i/revoke-token.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import $ from 'cafy';
|
||||
import define from '../../define';
|
||||
import { AccessTokens } from '../../../../models';
|
||||
import { ID } from '../../../../misc/cafy-id';
|
||||
|
||||
export const meta = {
|
||||
requireCredential: true as const,
|
||||
|
||||
secure: true,
|
||||
|
||||
params: {
|
||||
tokenId: {
|
||||
validator: $.type(ID)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default define(meta, async (ps, user) => {
|
||||
const token = await AccessTokens.findOne(ps.tokenId);
|
||||
|
||||
if (token) {
|
||||
AccessTokens.delete(token.id);
|
||||
}
|
||||
});
|
Loading…
Reference in a new issue