Compare commits
23 Commits
Author | SHA1 | Date | |
---|---|---|---|
36cd88e6b7 | |||
517b0908da | |||
b23b3e4d21 | |||
883fc5dde0 | |||
9d044329f6 | |||
d1e9e74cb8 | |||
98a87ee75f | |||
331491077d | |||
913c3a6636 | |||
fbaf5fe355 | |||
804c932f60 | |||
cef6d1d1b6 | |||
e4e7ab1135 | |||
6ca30df8c4 | |||
a340d4ed8e | |||
ca7cb94358 | |||
54779b25f5 | |||
44d7652171 | |||
c9ed15b682 | |||
8faad646ae | |||
1d50bc3382 | |||
da4af041af | |||
e2ff408f2f |
25
CHANGELOG.md
25
CHANGELOG.md
@ -1,6 +1,31 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
12.16.0 (2020/02/19)
|
||||
-------------------
|
||||
### ✨Improvements
|
||||
* 通知一覧をポップアップではなくページで表示できるように
|
||||
* 返信、引用、メンションの通知を直接ノートとして表示するように
|
||||
|
||||
### 🐛Fixes
|
||||
* v12以前のリアクションが表示されない問題を修正
|
||||
|
||||
12.15.0 (2020/02/19)
|
||||
-------------------
|
||||
### ✨Improvements
|
||||
* 固定投稿フォームを実装
|
||||
* ページ遷移のトランジションを無しに
|
||||
* スクロールしてるときに新しいノートが来たときにわかるように表示するように
|
||||
|
||||
### 🐛Fixes
|
||||
* ページのいいねボタンを修正
|
||||
|
||||
12.14.0 (2020/02/18)
|
||||
-------------------
|
||||
### ✨Improvements
|
||||
* オブジェクトストレージの設定を実装
|
||||
* サーバーログビューア実装
|
||||
|
||||
12.13.0 (2020/02/18)
|
||||
-------------------
|
||||
### ✨Improvements
|
||||
|
@ -412,6 +412,13 @@ dayOverDayChanges: "Daily"
|
||||
accessibility: "Accessibility"
|
||||
clinetSettings: "Client Settings"
|
||||
accountSettings: "Account Settings"
|
||||
promotion: "Promoted"
|
||||
promote: "Promote"
|
||||
numberOfDays: "Amount of days"
|
||||
hideThisNote: "Hide this note"
|
||||
showFeaturedNotesInTimeline: "Show Featured notes in Timeline"
|
||||
objectStorage: "Object Storage"
|
||||
useObjectStorage: "Use object storage"
|
||||
_ago:
|
||||
unknown: "Unknown"
|
||||
future: "Future"
|
||||
@ -513,6 +520,7 @@ _widgets:
|
||||
clock: "Clock"
|
||||
rss: "RSS reader"
|
||||
activity: "Activity"
|
||||
photos: "Photos"
|
||||
_cw:
|
||||
hide: "Hide"
|
||||
show: "Load more"
|
||||
|
@ -412,6 +412,13 @@ dayOverDayChanges: "Dif diaria"
|
||||
accessibility: "Accesibilidad"
|
||||
clinetSettings: "Ajustes del cliente"
|
||||
accountSettings: "Ajustes de cuenta"
|
||||
promotion: "Promovido"
|
||||
promote: "Promover"
|
||||
numberOfDays: "Cantidad de dias"
|
||||
hideThisNote: "Ocultar esta nota"
|
||||
showFeaturedNotesInTimeline: "Mostrar notas destacadas en la línea de tiempo"
|
||||
objectStorage: "Almacenamiento de objetos"
|
||||
useObjectStorage: "Usar almacenamiento de objetos"
|
||||
_ago:
|
||||
unknown: "Desconocido"
|
||||
future: "Futuro"
|
||||
@ -513,6 +520,7 @@ _widgets:
|
||||
clock: "Reloj"
|
||||
rss: "Lector RSS"
|
||||
activity: "Actividad"
|
||||
photos: "Fotos"
|
||||
_cw:
|
||||
hide: "Ocultar"
|
||||
show: "Ver más"
|
||||
|
@ -412,6 +412,13 @@ dayOverDayChanges: "Diff quotidien"
|
||||
accessibility: "Accessibilité"
|
||||
clinetSettings: "Paramètres du client"
|
||||
accountSettings: "Paramètres du compte"
|
||||
promotion: "Promu"
|
||||
promote: "Promouvoir"
|
||||
numberOfDays: "Nombre de jours"
|
||||
hideThisNote: "Masquer cette note"
|
||||
showFeaturedNotesInTimeline: "Afficher les notes en vedette dans Fil d'actualité"
|
||||
objectStorage: "Stockage d'objets"
|
||||
useObjectStorage: "Utiliser le stockage d'objets"
|
||||
_ago:
|
||||
unknown: "Inconnu"
|
||||
future: "Futur"
|
||||
@ -493,6 +500,7 @@ _widgets:
|
||||
clock: "Horloge"
|
||||
rss: "Lecteur de flux RSS"
|
||||
activity: "Activités"
|
||||
photos: "Photos"
|
||||
_cw:
|
||||
hide: "Masquer"
|
||||
show: "Voir plus"
|
||||
|
@ -417,6 +417,13 @@ promote: "プロモート"
|
||||
numberOfDays: "日数"
|
||||
hideThisNote: "このノートを非表示"
|
||||
showFeaturedNotesInTimeline: "タイムラインにおすすめのノートを表示する"
|
||||
objectStorage: "オブジェクトストレージ"
|
||||
useObjectStorage: "オブジェクトストレージを使用"
|
||||
serverLogs: "サーバーログ"
|
||||
deleteAll: "全て削除"
|
||||
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
|
||||
newNoteRecived: "新しいノートがあります"
|
||||
useNotificationsPopup: "通知一覧をポップアップで表示"
|
||||
|
||||
_ago:
|
||||
unknown: "謎"
|
||||
|
@ -23,6 +23,9 @@ login: "ಪ್ರವೇಶ"
|
||||
loggingIn: "ಪ್ರವೇಶಿಸುತ್ತಾ..."
|
||||
logout: "ಆಚೆಗೆ"
|
||||
signup: "ನೋಂದಣಿ"
|
||||
uploading: "ಅಪ್ಲೋಡಾಗುತ್ತಿದೆ"
|
||||
save: "ಉಳಿಸಿ"
|
||||
users: "ಬಳಕೆದಾರ"
|
||||
instances: "ನಿದರ್ಶನ"
|
||||
_widgets:
|
||||
notifications: "ಅಧಿಸೂಚನೆಗಳು"
|
||||
|
@ -412,6 +412,13 @@ dayOverDayChanges: "어제보다"
|
||||
accessibility: "접근성"
|
||||
clinetSettings: "클라이언트 설정"
|
||||
accountSettings: "계정 설정"
|
||||
promotion: "프로모션"
|
||||
promote: "프로모션하기"
|
||||
numberOfDays: "며칠동안"
|
||||
hideThisNote: "이 노트를 숨기기"
|
||||
showFeaturedNotesInTimeline: "타임라인에 추천 노트를 표시"
|
||||
objectStorage: "오브젝트 스토리지"
|
||||
useObjectStorage: "오브젝트 스토리지를 사용"
|
||||
_ago:
|
||||
unknown: "알 수 없음"
|
||||
future: "미래"
|
||||
@ -513,6 +520,7 @@ _widgets:
|
||||
clock: "시계"
|
||||
rss: "RSS 리더"
|
||||
activity: "활동"
|
||||
photos: "사진"
|
||||
_cw:
|
||||
hide: "숨기기"
|
||||
show: "더 보기"
|
||||
|
@ -414,6 +414,16 @@ _time:
|
||||
_tutorial:
|
||||
title: "Misskey的使用方法"
|
||||
step1_1: "欢迎!"
|
||||
step1_2: "这个页面叫做「时间线」,它会按照时间顺序显示所有你「关注」的人所发的「帖子」。"
|
||||
step1_3: "如果你并没有发布任何帖子,也没有关注其他的人,你的时间线页面应当什么都没有显示。"
|
||||
step2_1: "在你想发布一些帖子之前,让我们先进行一下个人资料设置。"
|
||||
step2_2: "如果别人能够更加的了解你,关注你的概率也会得到提升。"
|
||||
step3_1: "已经设置完个人资料了吗?"
|
||||
step3_2: "那么接下来,试着写一些什么东西来发布吧。你可以通过点击屏幕上的铅笔图标来打开投稿页面。"
|
||||
step3_3: "写完内容后,点击窗口右上方的按钮就可以投稿。"
|
||||
step3_4: "不知道说些什么好吗?那就写下「Misskey我来啦!」这样的话吧。"
|
||||
step4_1: "将你的话语发布出去了吗?"
|
||||
step4_2: "太棒了!现在你可以在你的时间线中看到你刚刚发布的帖子了。"
|
||||
step7_3: "接下来,享受Misskey带来的乐趣吧🚀"
|
||||
_2fa:
|
||||
alreadyRegistered: "此设备已被注册"
|
||||
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||
"version": "12.13.0",
|
||||
"version": "12.16.0",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -50,21 +50,27 @@
|
||||
<router-link class="item index" active-class="active" to="/" exact v-else>
|
||||
<fa :icon="faHome" fixed-width/><span class="text">{{ $store.getters.isSignedIn ? $t('timeline') : $t('home') }}</span>
|
||||
</router-link>
|
||||
<button class="item _button notifications" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.getters.isSignedIn">
|
||||
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
|
||||
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
|
||||
</button>
|
||||
<router-link class="item" active-class="active" to="/my/messaging" v-if="$store.getters.isSignedIn">
|
||||
<fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span>
|
||||
<i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i>
|
||||
</router-link>
|
||||
<router-link class="item" active-class="active" to="/my/drive" v-if="$store.getters.isSignedIn">
|
||||
<fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span>
|
||||
</router-link>
|
||||
<router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.getters.isSignedIn && $store.state.i.isLocked">
|
||||
<fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span>
|
||||
<i v-if="$store.state.i.hasPendingReceivedFollowRequest"><fa :icon="faCircle"/></i>
|
||||
</router-link>
|
||||
<template v-if="$store.getters.isSignedIn">
|
||||
<button class="item _button notifications" @click="notificationsOpen = !notificationsOpen" ref="notificationButton" v-if="$store.state.device.useNotificationsPopup">
|
||||
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
|
||||
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
|
||||
</button>
|
||||
<router-link class="item notifications" active-class="active" to="/my/notifications" ref="notificationButton" v-else>
|
||||
<fa :icon="faBell" fixed-width/><span class="text">{{ $t('notifications') }}</span>
|
||||
<i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i>
|
||||
</router-link>
|
||||
<router-link class="item" active-class="active" to="/my/messaging">
|
||||
<fa :icon="faComments" fixed-width/><span class="text">{{ $t('messaging') }}</span>
|
||||
<i v-if="$store.state.i.hasUnreadMessagingMessage"><fa :icon="faCircle"/></i>
|
||||
</router-link>
|
||||
<router-link class="item" active-class="active" to="/my/drive">
|
||||
<fa :icon="faCloud" fixed-width/><span class="text">{{ $t('drive') }}</span>
|
||||
</router-link>
|
||||
<router-link class="item" active-class="active" to="/my/follow-requests" v-if="$store.state.i.isLocked">
|
||||
<fa :icon="faUserClock" fixed-width/><span class="text">{{ $t('followRequests') }}</span>
|
||||
<i v-if="$store.state.i.hasPendingReceivedFollowRequest"><fa :icon="faCircle"/></i>
|
||||
</router-link>
|
||||
</template>
|
||||
<div class="divider"></div>
|
||||
<router-link class="item" active-class="active" to="/featured">
|
||||
<fa :icon="faFireAlt" fixed-width/><span class="text">{{ $t('featured') }}</span>
|
||||
@ -143,7 +149,8 @@
|
||||
<button class="button nav _button" @click="showNav = true" ref="navButton"><fa :icon="faBars"/><i v-if="$store.getters.isSignedIn && ($store.state.i.hasUnreadSpecifiedNotes || $store.state.i.hasPendingReceivedFollowRequest || $store.state.i.hasUnreadMessagingMessage || $store.state.i.hasUnreadAnnouncement)"><fa :icon="faCircle"/></i></button>
|
||||
<button v-if="$route.name === 'index'" class="button home _button" @click="top()"><fa :icon="faHome"/></button>
|
||||
<button v-else class="button home _button" @click="$router.push('/')"><fa :icon="faHome"/></button>
|
||||
<button v-if="$store.getters.isSignedIn" class="button notifications _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton2"><fa :icon="notificationsOpen ? faTimes : faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button>
|
||||
<button v-if="$store.getters.isSignedIn && $store.state.device.useNotificationsPopup" class="button notifications _button" @click="notificationsOpen = !notificationsOpen" ref="notificationButton2"><fa :icon="notificationsOpen ? faTimes : faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button>
|
||||
<button v-if="$store.getters.isSignedIn && !$store.state.device.useNotificationsPopup" class="button notifications _button" @click="$router.push('/my/notifications')" ref="notificationButton2"><fa :icon="faBell"/><i v-if="$store.state.i.hasUnreadNotification"><fa :icon="faCircle"/></i></button>
|
||||
<button v-if="$store.getters.isSignedIn" class="button post _buttonPrimary" @click="post()"><fa :icon="faPencilAlt"/></button>
|
||||
</div>
|
||||
|
||||
@ -650,18 +657,6 @@ export default Vue.extend({
|
||||
transform: scale(0.9);
|
||||
}
|
||||
|
||||
.page-enter-active, .page-leave-active {
|
||||
transition: opacity 0.5s, transform 0.5s !important;
|
||||
}
|
||||
.page-enter {
|
||||
opacity: 0;
|
||||
transform: translateY(-32px);
|
||||
}
|
||||
.page-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(32px);
|
||||
}
|
||||
|
||||
.nav-enter-active,
|
||||
.nav-leave-active {
|
||||
opacity: 1;
|
||||
@ -1218,15 +1213,17 @@ export default Vue.extend({
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: 0 auto;
|
||||
padding: 8px 8px 0 8px;
|
||||
z-index: 10001;
|
||||
width: 350px;
|
||||
height: 400px;
|
||||
box-sizing: border-box;
|
||||
background: var(--vocsgcxy);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
backdrop-filter: blur(12px);
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 3px 12px rgba(27, 31, 35, 0.15);
|
||||
overflow: hidden;
|
||||
overflow: auto;
|
||||
|
||||
@media (max-width: 800px) {
|
||||
width: 320px;
|
||||
|
BIN
src/client/assets/fedi.jpg
Normal file
BIN
src/client/assets/fedi.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 76 KiB |
@ -105,16 +105,6 @@ export default Vue.extend({
|
||||
text: this.$t('delete'),
|
||||
icon: faTrashAlt,
|
||||
action: this.deleteFile
|
||||
}, null, {
|
||||
type: 'nest',
|
||||
text: this.$t('contextmenu.else-files'),
|
||||
menu: [{
|
||||
text: this.$t('contextmenu.set-as-avatar'),
|
||||
action: this.setAsAvatar
|
||||
}, {
|
||||
text: this.$t('contextmenu.set-as-banner'),
|
||||
action: this.setAsBanner
|
||||
}]
|
||||
}],
|
||||
source: ev.currentTarget || ev.target,
|
||||
});
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="mk-notification" :class="notification.type">
|
||||
<div class="mk-notification" :class="notification.type" v-size="[{ max: 500 }, { max: 600 }]">
|
||||
<div class="head">
|
||||
<mk-avatar class="avatar" :user="notification.user"/>
|
||||
<div class="icon" :class="notification.type">
|
||||
@ -113,12 +113,17 @@ export default Vue.extend({
|
||||
.mk-notification {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
padding: 16px;
|
||||
padding: 28px 32px;
|
||||
font-size: 0.9em;
|
||||
overflow-wrap: break-word;
|
||||
display: flex;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
&.max-width_600px {
|
||||
padding: 16px;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
&.max-width_500px {
|
||||
padding: 12px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
@ -1,19 +1,18 @@
|
||||
<template>
|
||||
<div class="mk-notifications">
|
||||
<div class="contents">
|
||||
<x-list class="notifications" :items="items" v-slot="{ item: notification, i }">
|
||||
<x-notification :notification="notification" :with-time="true" :full="true" class="notification" :key="notification.id"/>
|
||||
</x-list>
|
||||
<div class="mk-notifications" :class="{ page }">
|
||||
<x-list class="notifications" :items="items" v-slot="{ item: notification }">
|
||||
<x-note v-if="['reply', 'quote', 'mention'].includes(notification.type)" :note="notification.note" :key="notification.id"/>
|
||||
<x-notification v-else :notification="notification" :with-time="true" :full="true" class="notification" :class="{ _panel: page }" :key="notification.id"/>
|
||||
</x-list>
|
||||
|
||||
<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
|
||||
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
|
||||
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
|
||||
</button>
|
||||
<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
|
||||
<template v-if="!moreFetching">{{ $t('loadMore') }}</template>
|
||||
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
|
||||
</button>
|
||||
|
||||
<p class="empty" v-if="empty">{{ $t('noNotifications') }}</p>
|
||||
<p class="empty" v-if="empty">{{ $t('noNotifications') }}</p>
|
||||
|
||||
<mk-error v-if="error" @retry="init()"/>
|
||||
</div>
|
||||
<mk-error v-if="error" @retry="init()"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -24,6 +23,7 @@ import i18n from '../i18n';
|
||||
import paging from '../scripts/paging';
|
||||
import XNotification from './notification.vue';
|
||||
import XList from './date-separated-list.vue';
|
||||
import XNote from './note.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n,
|
||||
@ -31,6 +31,7 @@ export default Vue.extend({
|
||||
components: {
|
||||
XNotification,
|
||||
XList,
|
||||
XNote,
|
||||
},
|
||||
|
||||
mixins: [
|
||||
@ -42,7 +43,7 @@ export default Vue.extend({
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
wide: {
|
||||
page: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
@ -93,11 +94,15 @@ export default Vue.extend({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mk-notifications {
|
||||
> .contents {
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
padding: 8px 8px 0 8px;
|
||||
&.page {
|
||||
> .notifications {
|
||||
> ::v-deep * {
|
||||
margin-bottom: var(--margin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.page) {
|
||||
> .notifications {
|
||||
> ::v-deep * {
|
||||
margin-bottom: 8px;
|
||||
@ -109,28 +114,28 @@ export default Vue.extend({
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .more {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
> .more {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
|
||||
> [data-icon] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
> [data-icon] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
> .empty {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
color: var(--fg);
|
||||
}
|
||||
> .empty {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
> .placeholder {
|
||||
padding: 32px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
> .placeholder {
|
||||
padding: 32px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -112,8 +112,7 @@ export default Vue.extend({
|
||||
margin: 4px 0;
|
||||
padding: 4px 8px;
|
||||
width: 100%;
|
||||
color: var(--pollChoiceText);
|
||||
border: solid 1px var(--pollChoiceBorder);
|
||||
border: solid 1px var(--divider);
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
|
@ -6,7 +6,7 @@
|
||||
@drop.stop="onDrop"
|
||||
>
|
||||
<header>
|
||||
<button class="cancel _button" @click="cancel"><fa :icon="faTimes"/></button>
|
||||
<button v-if="!fixed" class="cancel _button" @click="cancel"><fa :icon="faTimes"/></button>
|
||||
<div>
|
||||
<span class="text-count" :class="{ over: trimmedLength(text) > max }">{{ max - trimmedLength(text) }}</span>
|
||||
<button class="_button visibility" @click="setVisibility" ref="visibilityButton">
|
||||
@ -18,7 +18,7 @@
|
||||
<button class="submit _buttonPrimary" :disabled="!canPost" @click="post">{{ submitText }}<fa :icon="reply ? faReply : renote ? faQuoteRight : faPaperPlane"/></button>
|
||||
</div>
|
||||
</header>
|
||||
<div class="form">
|
||||
<div class="form" :class="{ fixed }">
|
||||
<x-note-preview class="preview" v-if="reply" :note="reply"/>
|
||||
<x-note-preview class="preview" v-if="renote" :note="renote"/>
|
||||
<div class="with-quote" v-if="quoteId"><fa icon="quote-left"/> {{ $t('quoteAttached') }}<button @click="quoteId = null"><fa icon="times"/></button></div>
|
||||
@ -108,6 +108,11 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
@ -582,7 +587,6 @@ export default Vue.extend({
|
||||
.gafaadew {
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: 0 0 2px rgba(#000, 0.1);
|
||||
|
||||
> header {
|
||||
z-index: 1000;
|
||||
@ -651,6 +655,10 @@ export default Vue.extend({
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
|
||||
&.fixed {
|
||||
max-width: unset;
|
||||
}
|
||||
|
||||
> .preview {
|
||||
padding: 16px;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-notes ref="tl" :pagination="pagination" @before="$emit('before')" @after="e => $emit('after', e)"/>
|
||||
<x-notes ref="tl" :pagination="pagination" @before="$emit('before')" @after="e => $emit('after', e)" @queue="$emit('queue', $event)"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
|
@ -3,7 +3,7 @@
|
||||
<template #header><mk-user-name :user="user"/></template>
|
||||
<div class="vrcsvlkm">
|
||||
<mk-button @click="resetPassword()" primary>{{ $t('resetPassword') }}</mk-button>
|
||||
<mk-switch v-if="$store.state.i.isAdmin && !user.isAdmin" @change="toggleModerator()" v-model="moderator">{{ $t('moderator') }}</mk-switch>
|
||||
<mk-switch v-if="$store.state.i.isAdmin && (this.moderator || !user.isAdmin)" @change="toggleModerator()" v-model="moderator">{{ $t('moderator') }}</mk-switch>
|
||||
<mk-switch @change="toggleSilence()" v-model="silenced">{{ $t('silence') }}</mk-switch>
|
||||
<mk-switch @change="toggleSuspend()" v-model="suspended">{{ $t('suspend') }}</mk-switch>
|
||||
</div>
|
||||
|
@ -14,9 +14,12 @@
|
||||
</button>
|
||||
</portal>
|
||||
|
||||
<div class="new" v-if="queue > 0" :style="{ width: width + 'px' }"><button class="_buttonPrimary" @click="top()">{{ $t('newNoteRecived') }}</button></div>
|
||||
|
||||
<x-tutorial class="tutorial" v-if="$store.state.settings.tutorial != -1"/>
|
||||
|
||||
<x-timeline ref="tl" :key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src" :src="src" :list="list" :antenna="antenna" @before="before()" @after="after()"/>
|
||||
<x-post-form class="post-form _panel" fixed v-if="$store.state.device.showFixedPostForm"/>
|
||||
<x-timeline ref="tl" :key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src" :src="src" :list="list" :antenna="antenna" @before="before()" @after="after()" @queue="queueUpdated"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -27,6 +30,7 @@ import { faComments } from '@fortawesome/free-regular-svg-icons';
|
||||
import Progress from '../scripts/loading';
|
||||
import XTimeline from '../components/timeline.vue';
|
||||
import XTutorial from './index.home.tutorial.vue';
|
||||
import XPostForm from '../components/post-form.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
metaInfo() {
|
||||
@ -38,6 +42,7 @@ export default Vue.extend({
|
||||
components: {
|
||||
XTimeline,
|
||||
XTutorial,
|
||||
XPostForm,
|
||||
},
|
||||
|
||||
props: {
|
||||
@ -53,6 +58,8 @@ export default Vue.extend({
|
||||
list: null,
|
||||
antenna: null,
|
||||
menuOpened: false,
|
||||
queue: 0,
|
||||
width: 0,
|
||||
faAngleDown, faAngleUp, faHome, faShareAlt, faGlobe, faComments, faListUl, faSatellite, faCircle
|
||||
};
|
||||
},
|
||||
@ -100,6 +107,15 @@ export default Vue.extend({
|
||||
Progress.done();
|
||||
},
|
||||
|
||||
queueUpdated(q) {
|
||||
this.width = this.$el.offsetWidth;
|
||||
this.queue = q;
|
||||
},
|
||||
|
||||
top() {
|
||||
window.scroll({ top: 0, behavior: 'instant' });
|
||||
},
|
||||
|
||||
async choose(ev) {
|
||||
this.menuOpened = true;
|
||||
const [antennas, lists] = await Promise.all([
|
||||
@ -169,9 +185,26 @@ export default Vue.extend({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mk-home {
|
||||
> .new {
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
|
||||
> button {
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
padding: 8px 12px;
|
||||
border-radius: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
> .tutorial {
|
||||
margin-bottom: var(--margin);
|
||||
}
|
||||
|
||||
> .post-form {
|
||||
position: relative;
|
||||
margin-bottom: var(--margin);
|
||||
}
|
||||
}
|
||||
|
||||
._kjvfvyph_ {
|
||||
|
@ -5,6 +5,38 @@
|
||||
|
||||
<mk-instance-stats style="margin-bottom: var(--margin);"/>
|
||||
|
||||
<section class="_card logs">
|
||||
<div class="_title"><fa :icon="faStream"/> {{ $t('serverLogs') }}</div>
|
||||
<div class="_content">
|
||||
<div class="_inputs">
|
||||
<mk-input v-model="logDomain" :debounce="true">
|
||||
<span>{{ $t('domain') }}</span>
|
||||
</mk-input>
|
||||
<mk-select v-model="logLevel">
|
||||
<template #label>{{ $t('level') }}</template>
|
||||
<option value="all">{{ $t('levels.all') }}</option>
|
||||
<option value="info">{{ $t('levels.info') }}</option>
|
||||
<option value="success">{{ $t('levels.success') }}</option>
|
||||
<option value="warning">{{ $t('levels.warning') }}</option>
|
||||
<option value="error">{{ $t('levels.error') }}</option>
|
||||
<option value="debug">{{ $t('levels.debug') }}</option>
|
||||
</mk-select>
|
||||
</div>
|
||||
|
||||
<div class="logs">
|
||||
<code v-for="log in logs" :key="log.id" :class="log.level">
|
||||
<details>
|
||||
<summary><mk-time :time="log.createdAt"/> [{{ log.domain.join('.') }}] {{ log.message }}</summary>
|
||||
<vue-json-pretty v-if="log.data" :data="log.data"></vue-json-pretty>
|
||||
</details>
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_footer">
|
||||
<mk-button @click="deleteAllLogs()" primary><fa :icon="faTrashAlt"/> {{ $t('deleteAll') }}</mk-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="_card chart">
|
||||
<div class="_title"><fa :icon="faMicrochip"/> {{ $t('cpuAndMemory') }}</div>
|
||||
<div class="_content" style="margin-top: -8px; margin-bottom: -12px;">
|
||||
@ -67,9 +99,13 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faServer, faExchangeAlt, faMicrochip, faHdd } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import Chart from 'chart.js';
|
||||
import VueJsonPretty from 'vue-json-pretty';
|
||||
import MkInstanceStats from '../../components/instance-stats.vue';
|
||||
import MkButton from '../../components/ui/button.vue';
|
||||
import MkSelect from '../../components/ui/select.vue';
|
||||
import MkInput from '../../components/ui/input.vue';
|
||||
import { version, url } from '../../config';
|
||||
import i18n from '../../i18n';
|
||||
|
||||
@ -92,6 +128,10 @@ export default Vue.extend({
|
||||
|
||||
components: {
|
||||
MkInstanceStats,
|
||||
MkButton,
|
||||
MkSelect,
|
||||
MkInput,
|
||||
VueJsonPretty
|
||||
},
|
||||
|
||||
data() {
|
||||
@ -104,7 +144,10 @@ export default Vue.extend({
|
||||
memUsage: 0,
|
||||
chartCpuMem: null,
|
||||
chartNet: null,
|
||||
faServer, faExchangeAlt, faMicrochip, faHdd
|
||||
logs: [],
|
||||
logLevel: 'all',
|
||||
logDomain: '',
|
||||
faServer, faExchangeAlt, faMicrochip, faHdd, faStream, faTrashAlt
|
||||
}
|
||||
},
|
||||
|
||||
@ -114,7 +157,20 @@ export default Vue.extend({
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
logLevel() {
|
||||
this.logs = [];
|
||||
this.fetchLogs();
|
||||
},
|
||||
logDomain() {
|
||||
this.logs = [];
|
||||
this.fetchLogs();
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchLogs();
|
||||
|
||||
Chart.defaults.global.defaultFontColor = getComputedStyle(document.documentElement).getPropertyValue('--fg');
|
||||
|
||||
this.chartCpuMem = new Chart(this.$refs.cpumem, {
|
||||
@ -330,6 +386,25 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetchLogs() {
|
||||
this.$root.api('admin/logs', {
|
||||
level: this.logLevel === 'all' ? null : this.logLevel,
|
||||
domain: this.logDomain === '' ? null : this.logDomain,
|
||||
limit: 30
|
||||
}).then(logs => {
|
||||
this.logs = logs.reverse();
|
||||
});
|
||||
},
|
||||
|
||||
deleteAllLogs() {
|
||||
this.$root.api('admin/delete-logs').then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
iconOnly: true, autoClose: true
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
onStats(stats) {
|
||||
const cpu = (stats.cpu * 100).toFixed(0);
|
||||
const memActive = (stats.mem.active / this.serverInfo.mem.total * 100).toFixed(0);
|
||||
@ -389,6 +464,37 @@ export default Vue.extend({
|
||||
}
|
||||
}
|
||||
|
||||
> .logs {
|
||||
> ._content {
|
||||
> .logs {
|
||||
padding: 8px;
|
||||
background: #000;
|
||||
color: #fff;
|
||||
font-size: 0.9em;
|
||||
|
||||
> code {
|
||||
display: block;
|
||||
|
||||
&.error {
|
||||
color: #f00;
|
||||
}
|
||||
|
||||
&.warning {
|
||||
color: #ff0;
|
||||
}
|
||||
|
||||
&.success {
|
||||
color: #0f0;
|
||||
}
|
||||
|
||||
&.debug {
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .chart {
|
||||
> ._content {
|
||||
> .table {
|
||||
|
@ -61,10 +61,10 @@
|
||||
<div class="_content">
|
||||
<mk-switch v-model="enableServiceWorker">{{ $t('enableServiceworker') }}<template #desc>{{ $t('serviceworkerInfo') }}</template></mk-switch>
|
||||
<template v-if="enableServiceWorker">
|
||||
<mk-horizon-group inputs class="fit-bottom">
|
||||
<div class="_inputs">
|
||||
<mk-input v-model="swPublicKey" :disabled="!enableServiceWorker"><template #icon><fa :icon="faKey"/></template>Public key</mk-input>
|
||||
<mk-input v-model="swPrivateKey" :disabled="!enableServiceWorker"><template #icon><fa :icon="faKey"/></template>Private key</mk-input>
|
||||
</mk-horizon-group>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="_footer">
|
||||
@ -97,6 +97,33 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faCloud"/> {{ $t('objectStorage') }}</div>
|
||||
<div class="_content">
|
||||
<mk-switch v-model="useObjectStorage">{{ $t('useObjectStorage') }}</mk-switch>
|
||||
<template v-if="useObjectStorage">
|
||||
<mk-input v-model="objectStorageBaseUrl" :disabled="!useObjectStorage">URL</mk-input>
|
||||
<div class="_inputs">
|
||||
<mk-input v-model="objectStorageBucket" :disabled="!useObjectStorage">Bucket</mk-input>
|
||||
<mk-input v-model="objectStoragePrefix" :disabled="!useObjectStorage">Prefix</mk-input>
|
||||
</div>
|
||||
<mk-input v-model="objectStorageEndpoint" :disabled="!useObjectStorage">Endpoint</mk-input>
|
||||
<div class="_inputs">
|
||||
<mk-input v-model="objectStorageRegion" :disabled="!useObjectStorage">Region</mk-input>
|
||||
<mk-input v-model="objectStoragePort" type="number" :disabled="!useObjectStorage">Port</mk-input>
|
||||
</div>
|
||||
<div class="_inputs">
|
||||
<mk-input v-model="objectStorageAccessKey" :disabled="!useObjectStorage"><template #icon><fa :icon="faKey"/></template>Access key</mk-input>
|
||||
<mk-input v-model="objectStorageSecretKey" :disabled="!useObjectStorage"><template #icon><fa :icon="faKey"/></template>Secret key</mk-input>
|
||||
</div>
|
||||
<mk-switch v-model="objectStorageUseSSL" :disabled="!useObjectStorage">SSL</mk-switch>
|
||||
</template>
|
||||
</div>
|
||||
<div class="_footer">
|
||||
<mk-button primary @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</mk-button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="_card">
|
||||
<div class="_title"><fa :icon="faGhost"/> {{ $t('proxyAccount') }}</div>
|
||||
<div class="_content">
|
||||
@ -213,6 +240,16 @@ export default Vue.extend({
|
||||
enableServiceWorker: false,
|
||||
swPublicKey: null,
|
||||
swPrivateKey: null,
|
||||
useObjectStorage: false,
|
||||
objectStorageBaseUrl: null,
|
||||
objectStorageBucket: null,
|
||||
objectStoragePrefix: null,
|
||||
objectStorageEndpoint: null,
|
||||
objectStorageRegion: null,
|
||||
objectStoragePort: null,
|
||||
objectStorageAccessKey: null,
|
||||
objectStorageSecretKey: null,
|
||||
objectStorageUseSSL: false,
|
||||
enableTwitterIntegration: false,
|
||||
twitterConsumerKey: null,
|
||||
twitterConsumerSecret: null,
|
||||
@ -257,6 +294,16 @@ export default Vue.extend({
|
||||
this.enableServiceWorker = this.meta.enableServiceWorker;
|
||||
this.swPublicKey = this.meta.swPublickey;
|
||||
this.swPrivateKey = this.meta.swPrivateKey;
|
||||
this.useObjectStorage = this.meta.useObjectStorage;
|
||||
this.objectStorageBaseUrl = this.meta.objectStorageBaseUrl;
|
||||
this.objectStorageBucket = this.meta.objectStorageBucket;
|
||||
this.objectStoragePrefix = this.meta.objectStoragePrefix;
|
||||
this.objectStorageEndpoint = this.meta.objectStorageEndpoint;
|
||||
this.objectStorageRegion = this.meta.objectStorageRegion;
|
||||
this.objectStoragePort = this.meta.objectStoragePort;
|
||||
this.objectStorageAccessKey = this.meta.objectStorageAccessKey;
|
||||
this.objectStorageSecretKey = this.meta.objectStorageSecretKey;
|
||||
this.objectStorageUseSSL = this.meta.objectStorageUseSSL;
|
||||
this.enableTwitterIntegration = this.meta.enableTwitterIntegration;
|
||||
this.twitterConsumerKey = this.meta.twitterConsumerKey;
|
||||
this.twitterConsumerSecret = this.meta.twitterConsumerSecret;
|
||||
@ -341,6 +388,16 @@ export default Vue.extend({
|
||||
enableServiceWorker: this.enableServiceWorker,
|
||||
swPublicKey: this.swPublicKey,
|
||||
swPrivateKey: this.swPrivateKey,
|
||||
useObjectStorage: this.useObjectStorage,
|
||||
objectStorageBaseUrl: this.objectStorageBaseUrl ? this.objectStorageBaseUrl : null,
|
||||
objectStorageBucket: this.objectStorageBucket ? this.objectStorageBucket : null,
|
||||
objectStoragePrefix: this.objectStoragePrefix ? this.objectStoragePrefix : null,
|
||||
objectStorageEndpoint: this.objectStorageEndpoint ? this.objectStorageEndpoint : null,
|
||||
objectStorageRegion: this.objectStorageRegion ? this.objectStorageRegion : null,
|
||||
objectStoragePort: this.objectStoragePort ? this.objectStoragePort : null,
|
||||
objectStorageAccessKey: this.objectStorageAccessKey ? this.objectStorageAccessKey : null,
|
||||
objectStorageSecretKey: this.objectStorageSecretKey ? this.objectStorageSecretKey : null,
|
||||
objectStorageUseSSL: this.objectStorageUseSSL,
|
||||
enableTwitterIntegration: this.enableTwitterIntegration,
|
||||
twitterConsumerKey: this.twitterConsumerKey,
|
||||
twitterConsumerSecret: this.twitterConsumerSecret,
|
||||
|
42
src/client/pages/notifications.vue
Normal file
42
src/client/pages/notifications.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<div>
|
||||
<portal to="icon"><fa :icon="faBell"/></portal>
|
||||
<portal to="title">{{ $t('notifications') }}</portal>
|
||||
<x-notifications @before="before" @after="after" page/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faBell } from '@fortawesome/free-solid-svg-icons';
|
||||
import Progress from '../scripts/loading';
|
||||
import XNotifications from '../components/notifications.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
metaInfo() {
|
||||
return {
|
||||
title: this.$t('notifications') as string
|
||||
};
|
||||
},
|
||||
|
||||
components: {
|
||||
XNotifications
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faBell
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
before() {
|
||||
Progress.start();
|
||||
},
|
||||
|
||||
after() {
|
||||
Progress.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -17,8 +17,8 @@
|
||||
</template>
|
||||
<router-link :to="`./${page.name}/view-source`">{{ $t('_pages.viewSource') }}</router-link>
|
||||
<div class="like">
|
||||
<button @click="unlike()" v-if="page.isLiked" :title="$t('_pages.unlike')"><fa :icon="faHeartS"/></button>
|
||||
<button @click="like()" v-else :title="$t('_pages.like')"><fa :icon="faHeart"/></button>
|
||||
<button class="_button" @click="unlike()" v-if="page.isLiked" :title="$t('_pages.unlike')"><fa :icon="faHeartS"/></button>
|
||||
<button class="_button" @click="like()" v-else :title="$t('_pages.like')"><fa :icon="faHeartR"/></button>
|
||||
<span class="count" v-if="page.likedCount > 0">{{ page.likedCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -28,6 +28,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeart as faHeartR } from '@fortawesome/free-regular-svg-icons';
|
||||
import XPage from '../components/page/page.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
@ -49,6 +51,7 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
page: null,
|
||||
faHeartS, faHeartR
|
||||
};
|
||||
},
|
||||
|
||||
@ -102,7 +105,7 @@ export default Vue.extend({
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
iconOnly: true, autoClose: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -20,6 +20,8 @@
|
||||
{{ $t('useOsNativeEmojis') }}
|
||||
<template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template>
|
||||
</mk-switch>
|
||||
<mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch>
|
||||
<mk-switch v-model="useNotificationsPopup">{{ $t('useNotificationsPopup') }}</mk-switch>
|
||||
</div>
|
||||
<div class="_content">
|
||||
<mk-select v-model="lang">
|
||||
@ -105,6 +107,16 @@ export default Vue.extend({
|
||||
get() { return this.$store.state.device.imageNewTab; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'imageNewTab', value }); }
|
||||
},
|
||||
|
||||
showFixedPostForm: {
|
||||
get() { return this.$store.state.device.showFixedPostForm; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); }
|
||||
},
|
||||
|
||||
useNotificationsPopup: {
|
||||
get() { return this.$store.state.device.useNotificationsPopup; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'useNotificationsPopup', value }); }
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
|
@ -73,7 +73,6 @@ export default Vue.extend({
|
||||
applyTheme(this.themes.find(x => x.id === this.theme));
|
||||
},
|
||||
|
||||
|
||||
wallpaper() {
|
||||
if (this.wallpaper == null) {
|
||||
localStorage.removeItem('wallpaper');
|
||||
|
@ -27,6 +27,7 @@ export const router = new VueRouter({
|
||||
{ path: '/explore', component: page('explore') },
|
||||
{ path: '/explore/tags/:tag', props: true, component: page('explore') },
|
||||
{ path: '/search', component: page('search') },
|
||||
{ path: '/my/notifications', component: page('notifications') },
|
||||
{ path: '/my/favorites', component: page('favorites') },
|
||||
{ path: '/my/messages', component: page('messages') },
|
||||
{ path: '/my/mentions', component: page('mentions') },
|
||||
|
@ -31,6 +31,10 @@ export default (opts) => ({
|
||||
watch: {
|
||||
pagination() {
|
||||
this.init();
|
||||
},
|
||||
|
||||
queue() {
|
||||
this.$emit('queue', this.queue.length);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -39,6 +39,8 @@ const defaultDeviceSettings = {
|
||||
animation: true,
|
||||
animatedMfm: true,
|
||||
imageNewTab: false,
|
||||
showFixedPostForm: false,
|
||||
useNotificationsPopup: true,
|
||||
userData: {},
|
||||
};
|
||||
|
||||
|
@ -248,6 +248,32 @@ hr {
|
||||
}
|
||||
}
|
||||
|
||||
._inputs {
|
||||
display: flex;
|
||||
margin: 32px 0;
|
||||
|
||||
&:first-child {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
> * {
|
||||
flex: 1;
|
||||
margin: 0 !important;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: 8px !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
._shadow {
|
||||
box-shadow: 0 8px 32px var(--shadow);
|
||||
|
||||
|
@ -84,6 +84,7 @@ export default define({
|
||||
|
||||
.tl {
|
||||
background: var(--bg);
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -20,6 +20,28 @@ export async function getFallbackReaction(): Promise<string> {
|
||||
return meta.useStarForReactionFallback ? '⭐' : '👍';
|
||||
}
|
||||
|
||||
export function convertLegacyReactions(reactions: Record<string, number>) {
|
||||
const _reactions = {} as Record<string, number>;
|
||||
|
||||
for (const reaction of Object.keys(reactions)) {
|
||||
if (Object.keys(legacy10).includes(reaction)) {
|
||||
if (_reactions[legacy10[reaction]]) {
|
||||
_reactions[legacy10[reaction]] += reactions[reaction];
|
||||
} else {
|
||||
_reactions[legacy10[reaction]] = reactions[reaction];
|
||||
}
|
||||
} else {
|
||||
if (_reactions[reaction]) {
|
||||
_reactions[reaction] += reactions[reaction];
|
||||
} else {
|
||||
_reactions[reaction] = reactions[reaction];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _reactions;
|
||||
}
|
||||
|
||||
export async function toDbReaction(reaction?: string | null): Promise<string> {
|
||||
if (reaction == null) return await getFallbackReaction();
|
||||
|
||||
|
@ -6,7 +6,7 @@ import { Emojis, Users, PollVotes, DriveFiles, NoteReactions, Followings, Polls
|
||||
import { ensure } from '../../prelude/ensure';
|
||||
import { SchemaType } from '../../misc/schema';
|
||||
import { awaitAll } from '../../prelude/await-all';
|
||||
import { convertLegacyReaction } from '../../misc/reaction-lib';
|
||||
import { convertLegacyReaction, convertLegacyReactions } from '../../misc/reaction-lib';
|
||||
|
||||
export type PackedNote = SchemaType<typeof packedNoteSchema>;
|
||||
|
||||
@ -187,7 +187,7 @@ export class NoteRepository extends Repository<Note> {
|
||||
viaMobile: note.viaMobile || undefined,
|
||||
renoteCount: note.renoteCount,
|
||||
repliesCount: note.repliesCount,
|
||||
reactions: note.reactions, // v12 TODO: convert legacy reaction
|
||||
reactions: convertLegacyReactions(note.reactions),
|
||||
tags: note.tags.length > 0 ? note.tags : undefined,
|
||||
emojis: populateEmojis(note.emojis, host, Object.keys(note.reactions)),
|
||||
fileIds: note.fileIds,
|
||||
|
Reference in New Issue
Block a user