Compare commits

..

19 Commits

Author SHA1 Message Date
syuilo
a0f10d7ca1 10.38.4 2018-11-04 18:38:04 +09:00
syuilo
299b91edc4 [API] Improve admin/emoji/add 2018-11-04 18:37:12 +09:00
syuilo
95c89ca6db RE: [Client] Fix bug 2018-11-04 18:36:19 +09:00
syuilo
7fe0d71e7f [Client] Fix bug 2018-11-04 18:35:55 +09:00
syuilo
fbbb506e86 🎨 2018-11-04 18:31:27 +09:00
syuilo
ec80b06a45 Merge branch 'develop' of https://github.com/syuilo/misskey into develop 2018-11-04 18:24:20 +09:00
syuilo
41e1619f1f [Client] Fix bug 2018-11-04 18:24:08 +09:00
syuilo
ba6a9c6a93 Merge pull request #3092 from syuilo/l10n_develop
New Crowdin translations
2018-11-04 18:22:19 +09:00
MeiMei
18571c52fb Fix: emoji regex (#3093) 2018-11-04 17:36:37 +09:00
syuilo
5d5dfeaa83 New translations ja-JP.yml (Japanese, Kansai) 2018-11-04 17:11:19 +09:00
syuilo
3669d8c0f3 New translations ja-JP.yml (Japanese, Kansai) 2018-11-04 17:01:11 +09:00
syuilo
69d72819c6 10.38.3 2018-11-04 15:18:37 +09:00
syuilo
54dcc10250 Fix bug for Mastodon(?) 2018-11-04 15:17:52 +09:00
syuilo
1edfce8f73 [Client] スマホ/タブレットからでも管理者ページを使えるように 2018-11-04 15:16:05 +09:00
syuilo
675e573a8c 🎨 2018-11-04 14:23:28 +09:00
syuilo
1080fa63a9 10.38.2 2018-11-04 11:09:31 +09:00
syuilo
8047086988 Good bye package-lock 2018-11-04 11:08:46 +09:00
syuilo
449b9f7fa0 [Client] Improve admin panel 2018-11-04 11:08:03 +09:00
syuilo
b7a15bf6ca 絵文字を作成した/更新した時にupdateAtを更新するように 2018-11-04 10:42:16 +09:00
27 changed files with 335 additions and 17485 deletions

1
.npmrc
View File

@@ -1 +1,2 @@
save-exact = true save-exact = true
package-lock = false

View File

@@ -186,7 +186,7 @@ common:
stack-left: "左に重ねんで!" stack-left: "左に重ねんで!"
pop-right: "右に出すで!" pop-right: "右に出すで!"
dev: "アプリの作成あかんかったわ。もっぺんやってみて。" dev: "アプリの作成あかんかったわ。もっぺんやってみて。"
ai-chan-kawaii: "藍ちゃかわいい" ai-chan-kawaii: "藍ちゃめっさべっぴんさんや"
auth/views/form.vue: auth/views/form.vue:
share-access: "<i>{{ app.name }}</i>があんさんのアカウントにアクセスすんのを<b>許可</b>してもええか?" share-access: "<i>{{ app.name }}</i>があんさんのアカウントにアクセスすんのを<b>許可</b>してもええか?"
permission-ask: "このアプリは次の権限を要求してんで:" permission-ask: "このアプリは次の権限を要求してんで:"
@@ -744,7 +744,7 @@ desktop/views/components/settings.vue:
apps: "アプリ" apps: "アプリ"
mute-and-block: "ミュート/ブロック" mute-and-block: "ミュート/ブロック"
blocking: "ブロック" blocking: "ブロック"
security: "守護神セキュリティ" security: "セキュリティ"
signin: "こんな感じでサインインしたらしいで" signin: "こんな感じでサインインしたらしいで"
password: "パスワード" password: "パスワード"
2fa: "二段階認証" 2fa: "二段階認証"
@@ -873,15 +873,15 @@ common/views/components/mute-and-block.vue:
mute-and-block: "ミュートとブロック" mute-and-block: "ミュートとブロック"
mute: "ミュート" mute: "ミュート"
block: "ブロック" block: "ブロック"
no-muted-users: "ミュートしているユーザーはいません" no-muted-users: "ミュートしるユーザーはおらんで"
no-blocked-users: "ブロックしているユーザーはいません" no-blocked-users: "ブロックしるユーザーはおらんで"
common/views/components/password-settings.vue: common/views/components/password-settings.vue:
reset: "パスワードを変更する" reset: "パスワード変える"
enter-current-password: "現在のパスワードを入力してください" enter-current-password: "のパスワードを入れてや"
enter-new-password: "新しいパスワードを入力してください" enter-new-password: "こんどのパスワード入れてや"
enter-new-password-again: "もう一度新しいパスワードを入力してください" enter-new-password-again: "もっぺん入れてや"
not-match: "新しいパスワードが一致しません" not-match: "パスワードがおうとらん"
changed: "パスワードを変更しました" changed: "パスワード変えたわ"
desktop/views/components/sub-note-content.vue: desktop/views/components/sub-note-content.vue:
private: "この投稿は見せられへんわ" private: "この投稿は見せられへんわ"
deleted: "この投稿なんか無くなってもうたわ" deleted: "この投稿なんか無くなってもうたわ"
@@ -953,7 +953,7 @@ admin/views/index.vue:
emoji: "カスタム絵文字" emoji: "カスタム絵文字"
users: "ユーザー" users: "ユーザー"
update: "更新" update: "更新"
announcements: "お知らせ" announcements: "知っといてや"
hashtags: "ハッシュタグ" hashtags: "ハッシュタグ"
back-to-misskey: "Misskeyに戻る" back-to-misskey: "Misskeyに戻る"
admin/views/dashboard.vue: admin/views/dashboard.vue:
@@ -962,9 +962,9 @@ admin/views/dashboard.vue:
notes: "投稿" notes: "投稿"
drive: "ドライブ" drive: "ドライブ"
instances: "インスタンス" instances: "インスタンス"
this-instance: "のインスタンス" this-instance: "ワイのインスタンス"
federated: "連合" federated: "連合"
invite: "招待" invite: "来てや"
banner-url: "Banner URL" banner-url: "Banner URL"
disableRegistration: "Disable new user registration" disableRegistration: "Disable new user registration"
disableLocalTimeline: "Disable the local timeline" disableLocalTimeline: "Disable the local timeline"
@@ -980,7 +980,7 @@ admin/views/charts.vue:
charts: charts:
federation-instances: "インスタンスの増減" federation-instances: "インスタンスの増減"
federation-instances-total: "インスタンスの積算" federation-instances-total: "インスタンスの積算"
notes: "投稿の増減 (統合)" notes: "投稿の増減(統合)"
local-notes: "投稿の増減 (ローカル)" local-notes: "投稿の増減 (ローカル)"
remote-notes: "投稿の増減 (リモート)" remote-notes: "投稿の増減 (リモート)"
notes-total: "投稿の積算" notes-total: "投稿の積算"
@@ -1387,7 +1387,7 @@ mobile/views/pages/user.vue:
mute: "ミュート" mute: "ミュート"
unmute: "ミュート解除" unmute: "ミュート解除"
block: "ブロック" block: "ブロック"
unblock: "ブロック解除" unblock: "ブロックやめたる"
mobile/views/pages/user/home.vue: mobile/views/pages/user/home.vue:
recent-notes: "最近儲かりまっか?" recent-notes: "最近儲かりまっか?"
images: "画像" images: "画像"

17362
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,8 +1,8 @@
{ {
"name": "misskey", "name": "misskey",
"author": "syuilo <i@syuilo.com>", "author": "syuilo <i@syuilo.com>",
"version": "10.38.1", "version": "10.38.4",
"clientVersion": "1.0.11482", "clientVersion": "1.0.11501",
"codename": "nighthike", "codename": "nighthike",
"main": "./built/index.js", "main": "./built/index.js",
"private": true, "private": true,

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="cdeuzmsthagexbkpofbmatmugjuvogfb">
<ui-card> <ui-card>
<div slot="title">%fa:broadcast-tower% %i18n:@announcements%</div> <div slot="title">%fa:broadcast-tower% %i18n:@announcements%</div>
<section v-for="(announcement, i) in announcements" class="fit-top"> <section v-for="(announcement, i) in announcements" class="fit-top">
@@ -9,10 +9,10 @@
<ui-textarea v-model="announcement.text"> <ui-textarea v-model="announcement.text">
<span>%i18n:@text%</span> <span>%i18n:@text%</span>
</ui-textarea> </ui-textarea>
<ui-button-group> <ui-horizon-group>
<ui-button inline @click="save">%fa:save R% %i18n:@save%</ui-button> <ui-button @click="save">%fa:save R% %i18n:@save%</ui-button>
<ui-button inline @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button> <ui-button @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
</ui-button-group> </ui-horizon-group>
</section> </section>
<section> <section>
<ui-button @click="add">%fa:plus% %i18n:@add%</ui-button> <ui-button @click="add">%fa:plus% %i18n:@add%</ui-button>
@@ -54,11 +54,18 @@ export default Vue.extend({
(this as any).api('admin/update-meta', { (this as any).api('admin/update-meta', {
broadcasts: this.announcements broadcasts: this.announcements
}).then(() => { }).then(() => {
(this as any).os.apis.dialog({ text: `Saved` }); //(this as any).os.apis.dialog({ text: `Saved` });
}).catch(e => { }).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` }); //(this as any).os.apis.dialog({ text: `Failed ${e}` });
}); });
} }
} }
}); });
</script> </script>
<style lang="stylus" scoped>
.cdeuzmsthagexbkpofbmatmugjuvogfb
@media (min-width 500px)
padding 16px
</style>

View File

@@ -63,11 +63,11 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.hyhctythnmwihguaaapnbrbszsjqxpio .hyhctythnmwihguaaapnbrbszsjqxpio
display block display block
padding 16px padding 12px 16px 16px 16px
height 250px height 250px
overflow auto overflow hidden
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1) box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
background var(--face) background var(--adminDashboardCardBg)
border-radius 8px border-radius 8px
> table > table
@@ -76,10 +76,11 @@ export default Vue.extend({
overflow auto overflow auto
border-spacing 0 border-spacing 0
border-collapse collapse border-collapse collapse
color #555 color var(--adminDashboardCardFg)
font-size 14px
thead thead
border-bottom solid 2px #eee border-bottom solid 1px var(--adminDashboardCardDivider)
tr tr
th th
@@ -89,7 +90,7 @@ export default Vue.extend({
tbody tbody
tr tr
&:nth-child(odd) &:nth-child(odd)
background #fbfbfb background rgba(0, 0, 0, 0.025)
th, td th, td
padding 8px 16px padding 8px 16px

View File

@@ -39,6 +39,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import * as tinycolor from 'tinycolor2';
import * as ApexCharts from 'apexcharts'; import * as ApexCharts from 'apexcharts';
const limit = 90; const limit = 90;
@@ -147,7 +148,7 @@ export default Vue.extend({
this.chartInstance.destroy(); this.chartInstance.destroy();
} }
this.chartInstance = new ApexCharts(this.$refs.chart, Object.assign({ this.chartInstance = new ApexCharts(this.$refs.chart, {
chart: { chart: {
type: 'area', type: 'area',
height: 300, height: 300,
@@ -168,17 +169,41 @@ export default Vue.extend({
}, },
grid: { grid: {
clipMarkers: false, clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)'
}, },
stroke: { stroke: {
curve: 'straight', curve: 'straight',
width: 2 width: 2
}, },
legend: {
labels: {
color: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
},
},
xaxis: { xaxis: {
type: 'datetime' type: 'datetime',
labels: {
style: {
colors: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
}
},
axisBorder: {
color: 'rgba(0, 0, 0, 0.1)'
},
axisTicks: {
color: 'rgba(0, 0, 0, 0.1)'
},
}, },
yaxis: { yaxis: {
} labels: {
}, this.data)); formatter: this.data.bytes ? v => Vue.filter('bytes')(v, 0) : v => Vue.filter('number')(v),
style: {
color: tinycolor(getComputedStyle(document.documentElement).getPropertyValue('--text')).toRgbString()
}
}
},
series: this.data.series
});
this.chartInstance.render(); this.chartInstance.render();
}, },
@@ -286,6 +311,7 @@ export default Vue.extend({
driveChart(): any { driveChart(): any {
return { return {
bytes: true,
series: [{ series: [{
name: 'All', name: 'All',
data: this.format( data: this.format(
@@ -314,6 +340,7 @@ export default Vue.extend({
driveTotalChart(): any { driveTotalChart(): any {
return { return {
bytes: true,
series: [{ series: [{
name: 'Combined', name: 'Combined',
data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize)) data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize))
@@ -396,6 +423,7 @@ export default Vue.extend({
networkUsageChart(): any { networkUsageChart(): any {
return { return {
bytes: true,
series: [{ series: [{
name: 'Incoming', name: 'Incoming',
data: this.format(this.stats.network.incomingBytes) data: this.format(this.stats.network.incomingBytes)
@@ -424,8 +452,8 @@ export default Vue.extend({
margin 0 8px margin 0 8px
padding 0 0 8px 0 padding 0 0 8px 0
font-size 1em font-size 1em
color #555 color var(--adminDashboardCardFg)
border-bottom solid 1px #eee border-bottom solid 1px var(--adminDashboardCardDivider)
> b > b
margin-right 8px margin-right 8px

View File

@@ -79,6 +79,7 @@ export default Vue.extend({
}, },
grid: { grid: {
clipMarkers: false, clipMarkers: false,
borderColor: 'rgba(0, 0, 0, 0.1)'
}, },
stroke: { stroke: {
curve: 'straight', curve: 'straight',
@@ -153,7 +154,7 @@ export default Vue.extend({
display flex display flex
padding 0 8px padding 0 8px
margin-bottom -16px margin-bottom -16px
color #555 color var(--adminDashboardCardFg)
font-size 14px font-size 14px
> span > span
@@ -167,4 +168,13 @@ export default Vue.extend({
> div > div
margin-bottom -10px margin-bottom -10px
@media (max-width 1000px)
display block
margin-bottom 26px
> div
&:first-child
margin-right 0
margin-bottom 26px
</style> </style>

View File

@@ -124,17 +124,28 @@ export default Vue.extend({
<style lang="stylus" scoped> <style lang="stylus" scoped>
.obdskegsannmntldydackcpzezagxqfy .obdskegsannmntldydackcpzezagxqfy
padding 16px
@media (min-width 500px)
padding 32px
> header > header
display flex display flex
margin-bottom 16px margin-bottom 16px
padding-bottom 16px padding-bottom 16px
border-bottom solid 1px #ccc border-bottom solid 1px var(--adminDashboardHeaderBorder)
color #777 color var(--adminDashboardHeaderFg)
font-size 14px font-size 14px
white-space nowrap
@media (max-width 1000px)
display none
> p > p
display inline display block
margin 0 32px 0 0 margin 0 32px 0 0
overflow hidden
text-overflow ellipsis
> b > b
&:after &:after
@@ -152,11 +163,10 @@ export default Vue.extend({
> div > div
flex 1 flex 1
max-width 300px
margin-right 16px margin-right 16px
color var(--text) color var(--adminDashboardCardFg)
box-shadow 0 2px 4px rgba(0, 0, 0, 0.1) box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
background var(--face) background var(--adminDashboardCardBg)
border-radius 8px border-radius 8px
&:last-child &:last-child
@@ -192,7 +202,7 @@ export default Vue.extend({
> div:last-child > div:last-child
display flex display flex
padding 6px 16px padding 6px 16px
border-top solid 1px #eee border-top solid 1px var(--adminDashboardCardDivider)
> span > span
font-size 70% font-size 70%
@@ -202,6 +212,21 @@ export default Vue.extend({
margin-left auto margin-left auto
cursor pointer cursor pointer
@media (max-width 900px)
display grid
grid-template-columns 1fr 1fr
grid-template-rows 1fr 1fr
gap 16px
> div
margin-right 0
@media (max-width 500px)
display block
> div:not(:last-child)
margin-bottom 16px
> .charts > .charts
margin-bottom 16px margin-bottom 16px

View File

@@ -1,16 +1,18 @@
<template> <template>
<div> <div class="tumhkfkmgtvzljezfvmgkeurkfncshbe">
<ui-card> <ui-card>
<div slot="title">%fa:plus% %i18n:@add-emoji.title%</div> <div slot="title">%fa:plus% %i18n:@add-emoji.title%</div>
<section class="fit-top"> <section class="fit-top">
<ui-input v-model="name"> <ui-horizon-group inputs>
<span>%i18n:@add-emoji.name%</span> <ui-input v-model="name">
<span slot="text">%i18n:@add-emoji.name-desc%</span> <span>%i18n:@add-emoji.name%</span>
</ui-input> <span slot="text">%i18n:@add-emoji.name-desc%</span>
<ui-input v-model="aliases"> </ui-input>
<span>%i18n:@add-emoji.aliases%</span> <ui-input v-model="aliases">
<span slot="text">%i18n:@add-emoji.aliases-desc%</span> <span>%i18n:@add-emoji.aliases%</span>
</ui-input> <span slot="text">%i18n:@add-emoji.aliases-desc%</span>
</ui-input>
</ui-horizon-group>
<ui-input v-model="url"> <ui-input v-model="url">
<span>%i18n:@add-emoji.url%</span> <span>%i18n:@add-emoji.url%</span>
</ui-input> </ui-input>
@@ -22,21 +24,23 @@
<div slot="title">%fa:grin R% %i18n:@emojis.title%</div> <div slot="title">%fa:grin R% %i18n:@emojis.title%</div>
<section v-for="emoji in emojis"> <section v-for="emoji in emojis">
<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/> <img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
<ui-input v-model="emoji.name"> <ui-horizon-group inputs>
<span>%i18n:@add-emoji.name%</span> <ui-input v-model="emoji.name">
<span slot="text">%i18n:@add-emoji.name-desc%</span> <span>%i18n:@add-emoji.name%</span>
</ui-input> <span slot="text">%i18n:@add-emoji.name-desc%</span>
<ui-input v-model="emoji.aliases"> </ui-input>
<span>%i18n:@add-emoji.aliases%</span> <ui-input v-model="emoji.aliases">
<span slot="text">%i18n:@add-emoji.aliases-desc%</span> <span>%i18n:@add-emoji.aliases%</span>
</ui-input> <span slot="text">%i18n:@add-emoji.aliases-desc%</span>
</ui-input>
</ui-horizon-group>
<ui-input v-model="emoji.url"> <ui-input v-model="emoji.url">
<span>%i18n:@add-emoji.url%</span> <span>%i18n:@add-emoji.url%</span>
</ui-input> </ui-input>
<ui-button-group> <ui-horizon-group>
<ui-button inline @click="updateEmoji(emoji)">%fa:save R% %i18n:@emojis.update%</ui-button> <ui-button @click="updateEmoji(emoji)">%fa:save R% %i18n:@emojis.update%</ui-button>
<ui-button inline @click="removeEmoji(emoji)">%fa:trash-alt R% %i18n:@emojis.remove%</ui-button> <ui-button @click="removeEmoji(emoji)">%fa:trash-alt R% %i18n:@emojis.remove%</ui-button>
</ui-button-group> </ui-horizon-group>
</section> </section>
</ui-card> </ui-card>
</div> </div>
@@ -66,10 +70,10 @@ export default Vue.extend({
url: this.url, url: this.url,
aliases: this.aliases.split(' ') aliases: this.aliases.split(' ')
}).then(() => { }).then(() => {
(this as any).os.apis.dialog({ text: `Added` }); //(this as any).os.apis.dialog({ text: `Added` });
this.fetchEmojis(); this.fetchEmojis();
}).catch(e => { }).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` }); //(this as any).os.apis.dialog({ text: `Failed ${e}` });
}); });
}, },
@@ -87,9 +91,9 @@ export default Vue.extend({
url: emoji.url, url: emoji.url,
aliases: emoji.aliases.split(' ') aliases: emoji.aliases.split(' ')
}).then(() => { }).then(() => {
(this as any).os.apis.dialog({ text: `Updated` }); //(this as any).os.apis.dialog({ text: `Updated` });
}).catch(e => { }).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` }); //(this as any).os.apis.dialog({ text: `Failed ${e}` });
}); });
}, },
@@ -97,12 +101,19 @@ export default Vue.extend({
(this as any).api('admin/emoji/remove', { (this as any).api('admin/emoji/remove', {
id: emoji.id id: emoji.id
}).then(() => { }).then(() => {
(this as any).os.apis.dialog({ text: `Removed` }); //(this as any).os.apis.dialog({ text: `Removed` });
this.fetchEmojis(); this.fetchEmojis();
}).catch(e => { }).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` }); //(this as any).os.apis.dialog({ text: `Failed ${e}` });
}); });
} }
} }
}); });
</script> </script>
<style lang="stylus" scoped>
.tumhkfkmgtvzljezfvmgkeurkfncshbe
@media (min-width 500px)
padding 16px
</style>

View File

@@ -29,9 +29,9 @@ export default Vue.extend({
(this as any).api('admin/update-meta', { (this as any).api('admin/update-meta', {
hidedTags: this.hidedTags.split('\n') hidedTags: this.hidedTags.split('\n')
}).then(() => { }).then(() => {
(this as any).os.apis.dialog({ text: `Saved` }); //(this as any).os.apis.dialog({ text: `Saved` });
}).catch(e => { }).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` }); //(this as any).os.apis.dialog({ text: `Failed ${e}` });
}); });
} }
} }

View File

@@ -1,6 +1,15 @@
<template> <template>
<div class="mk-admin"> <div class="mk-admin" :class="{ isMobile }">
<nav> <header v-show="isMobile">
<button class="nav" @click="navOpend = true">%fa:bars%</button>
<span>MisskeyMyAdmin</span>
</header>
<div class="nav-backdrop"
v-if="navOpend && isMobile"
@click="navOpend = false"
@touchstart="navOpend = false"
></div>
<nav v-show="navOpend">
<div class="mi"> <div class="mi">
<img svg-inline src="../assets/header-icon.svg"/> <img svg-inline src="../assets/header-icon.svg"/>
</div> </div>
@@ -49,6 +58,10 @@ import XAnnouncements from "./announcements.vue";
import XHashtags from "./hashtags.vue"; import XHashtags from "./hashtags.vue";
import XUsers from "./users.vue"; import XUsers from "./users.vue";
// Detect the user agent
const ua = navigator.userAgent.toLowerCase();
const isMobile = /mobile|iphone|ipad|android/.test(ua);
export default Vue.extend({ export default Vue.extend({
components: { components: {
XDashboard, XDashboard,
@@ -58,10 +71,15 @@ export default Vue.extend({
XHashtags, XHashtags,
XUsers XUsers
}, },
provide: {
isMobile
},
data() { data() {
return { return {
page: 'dashboard', page: 'dashboard',
version version,
isMobile,
navOpend: !isMobile
}; };
}, },
methods: { methods: {
@@ -74,12 +92,46 @@ export default Vue.extend({
<style lang="stylus"> <style lang="stylus">
.mk-admin .mk-admin
$headerHeight = 48px
display flex display flex
height 100% height 100%
> header
position fixed
top 0
z-index 10000
width 100%
color var(--mobileHeaderFg)
background-color var(--mobileHeaderBg)
box-shadow 0 1px 0 rgba(#000, 0.075)
&, *
user-select none
> span
display block
line-height $headerHeight
text-align center
> .nav
display block
position absolute
top 0
left 0
z-index 10001
padding 0
width $headerHeight
font-size 1.4em
line-height $headerHeight
border-right solid 1px rgba(#000, 0.1)
> [data-fa]
transition all 0.2s ease
> nav > nav
position fixed position fixed
z-index 10000 z-index 20001
top 0 top 0
left 0 left 0
width 250px width 250px
@@ -187,9 +239,22 @@ export default Vue.extend({
border-bottom solid 16px transparent border-bottom solid 16px transparent
border-left solid 16px transparent border-left solid 16px transparent
> .nav-backdrop
position fixed
top 0
left 0
z-index 20000
width 100%
height 100%
background var(--mobileNavBackdrop)
> main > main
width 100% width 100%
padding 32px 32px 32px calc(32px + 250px) padding 0 0 0 250px
max-width 1300px max-width 1300px
&.isMobile
> main
padding $headerHeight 0 0 0
</style> </style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="axbwjelsbymowqjyywpirzhdlszoncqs">
<ui-card> <ui-card>
<div slot="title">%i18n:@banner-url%</div> <div slot="title">%i18n:@banner-url%</div>
<section class="fit-top"> <section class="fit-top">
@@ -43,7 +43,7 @@ export default Vue.extend({
(this as any).api('admin/invite').then(x => { (this as any).api('admin/invite').then(x => {
this.inviteCode = x.code; this.inviteCode = x.code;
}).catch(e => { }).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` }); //(this as any).os.apis.dialog({ text: `Failed ${e}` });
}); });
}, },
updateMeta() { updateMeta() {
@@ -52,11 +52,18 @@ export default Vue.extend({
disableLocalTimeline: this.disableLocalTimeline, disableLocalTimeline: this.disableLocalTimeline,
bannerUrl: this.bannerUrl bannerUrl: this.bannerUrl
}).then(() => { }).then(() => {
(this as any).os.apis.dialog({ text: `Saved` }); //(this as any).os.apis.dialog({ text: `Saved` });
}).catch(e => { }).catch(e => {
(this as any).os.apis.dialog({ text: `Failed ${e}` }); //(this as any).os.apis.dialog({ text: `Failed ${e}` });
}); });
} }
} }
}); });
</script> </script>
<style lang="stylus" scoped>
.axbwjelsbymowqjyywpirzhdlszoncqs
@media (min-width 500px)
padding 16px
</style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="ucnffhbtogqgscfmqcymwmmupoknpfsw">
<ui-card> <ui-card>
<div slot="title">%i18n:@verify-user%</div> <div slot="title">%i18n:@verify-user%</div>
<section class="fit-top"> <section class="fit-top">
@@ -67,11 +67,11 @@ export default Vue.extend({
const process = async () => { const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.verifyUsername)); const user = await (this as any).os.api('users/show', parseAcct(this.verifyUsername));
await (this as any).os.api('admin/verify-user', { userId: user.id }); await (this as any).os.api('admin/verify-user', { userId: user.id });
(this as any).os.apis.dialog({ text: '%i18n:@verified%' }); //(this as any).os.apis.dialog({ text: '%i18n:@verified%' });
}; };
await process().catch(e => { await process().catch(e => {
(this as any).os.apis.dialog({ text: `Failed: ${e}` }); //(this as any).os.apis.dialog({ text: `Failed: ${e}` });
}); });
this.verifying = false; this.verifying = false;
@@ -83,11 +83,11 @@ export default Vue.extend({
const process = async () => { const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.unverifyUsername)); const user = await (this as any).os.api('users/show', parseAcct(this.unverifyUsername));
await (this as any).os.api('admin/unverify-user', { userId: user.id }); await (this as any).os.api('admin/unverify-user', { userId: user.id });
(this as any).os.apis.dialog({ text: '%i18n:@unverified%' }); //(this as any).os.apis.dialog({ text: '%i18n:@unverified%' });
}; };
await process().catch(e => { await process().catch(e => {
(this as any).os.apis.dialog({ text: `Failed: ${e}` }); //(this as any).os.apis.dialog({ text: `Failed: ${e}` });
}); });
this.unverifying = false; this.unverifying = false;
@@ -99,11 +99,11 @@ export default Vue.extend({
const process = async () => { const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.suspendUsername)); const user = await (this as any).os.api('users/show', parseAcct(this.suspendUsername));
await (this as any).os.api('admin/suspend-user', { userId: user.id }); await (this as any).os.api('admin/suspend-user', { userId: user.id });
(this as any).os.apis.dialog({ text: '%i18n:@suspended%' }); //(this as any).os.apis.dialog({ text: '%i18n:@suspended%' });
}; };
await process().catch(e => { await process().catch(e => {
(this as any).os.apis.dialog({ text: `Failed: ${e}` }); //(this as any).os.apis.dialog({ text: `Failed: ${e}` });
}); });
this.suspending = false; this.suspending = false;
@@ -115,11 +115,11 @@ export default Vue.extend({
const process = async () => { const process = async () => {
const user = await (this as any).os.api('users/show', parseAcct(this.unsuspendUsername)); const user = await (this as any).os.api('users/show', parseAcct(this.unsuspendUsername));
await (this as any).os.api('admin/unsuspend-user', { userId: user.id }); await (this as any).os.api('admin/unsuspend-user', { userId: user.id });
(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' }); //(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' });
}; };
await process().catch(e => { await process().catch(e => {
(this as any).os.apis.dialog({ text: `Failed: ${e}` }); //(this as any).os.apis.dialog({ text: `Failed: ${e}` });
}); });
this.unsuspending = false; this.unsuspending = false;
@@ -127,3 +127,10 @@ export default Vue.extend({
} }
}); });
</script> </script>
<style lang="stylus" scoped>
.ucnffhbtogqgscfmqcymwmmupoknpfsw
@media (min-width 500px)
padding 16px
</style>

View File

@@ -42,7 +42,7 @@ import Reversi from './games/reversi/reversi.vue';
import welcomeTimeline from './welcome-timeline.vue'; import welcomeTimeline from './welcome-timeline.vue';
import uiInput from './ui/input.vue'; import uiInput from './ui/input.vue';
import uiButton from './ui/button.vue'; import uiButton from './ui/button.vue';
import uiButtonGroup from './ui/button-group.vue'; import uiHorizonGroup from './ui/horizon-group.vue';
import uiCard from './ui/card.vue'; import uiCard from './ui/card.vue';
import uiForm from './ui/form.vue'; import uiForm from './ui/form.vue';
import uiTextarea from './ui/textarea.vue'; import uiTextarea from './ui/textarea.vue';
@@ -95,7 +95,7 @@ Vue.component('mk-reversi', Reversi);
Vue.component('mk-welcome-timeline', welcomeTimeline); Vue.component('mk-welcome-timeline', welcomeTimeline);
Vue.component('ui-input', uiInput); Vue.component('ui-input', uiInput);
Vue.component('ui-button', uiButton); Vue.component('ui-button', uiButton);
Vue.component('ui-button-group', uiButtonGroup); Vue.component('ui-horizon-group', uiHorizonGroup);
Vue.component('ui-card', uiCard); Vue.component('ui-card', uiCard);
Vue.component('ui-form', uiForm); Vue.component('ui-form', uiForm);
Vue.component('ui-textarea', uiTextarea); Vue.component('ui-textarea', uiTextarea);

View File

@@ -1,21 +0,0 @@
<template>
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze">
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({});
</script>
<style lang="stylus" scoped>
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
display flex
> *
flex 1
&:not(:last-child)
margin-right 16px
</style>

View File

@@ -1,5 +1,10 @@
<template> <template>
<component class="dmtdnykelhudezerjlfpbhgovrgnqqgr" :is="link ? 'a' : 'button'" :class="[styl, { inline, primary }]" :type="type" @click="$emit('click')"> <component class="dmtdnykelhudezerjlfpbhgovrgnqqgr"
:is="link ? 'a' : 'button'"
:class="[styl, { inline, primary }]"
:type="type"
@click="$emit('click')"
>
<slot></slot> <slot></slot>
</component> </component>
</template> </template>
@@ -7,6 +12,7 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
export default Vue.extend({ export default Vue.extend({
inject: ['horizonGrouped'],
props: { props: {
type: { type: {
type: String, type: String,
@@ -20,7 +26,9 @@ export default Vue.extend({
inline: { inline: {
type: Boolean, type: Boolean,
required: false, required: false,
default: false default(): boolean {
return this.horizonGrouped;
}
}, },
link: { link: {
type: Boolean, type: Boolean,

View File

@@ -0,0 +1,35 @@
<template>
<div class="pfzekjfwkwvadvlujpdnnxfggqgqjoze" :class="{ inputs }">
<slot></slot>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
provide: {
horizonGrouped: true
},
props: {
inputs: {
type: Boolean,
required: false,
default: false
}
}
});
</script>
<style lang="stylus" scoped>
.pfzekjfwkwvadvlujpdnnxfggqgqjoze
display flex
&.inputs
margin 32px 0
> *
flex 1
&:not(:last-child)
margin-right 16px
</style>

View File

@@ -1,5 +1,5 @@
<template> <template>
<div class="ui-input" :class="[{ focused, filled }, styl]"> <div class="ui-input" :class="[{ focused, filled, inline }, styl]">
<div class="icon" ref="icon"><slot name="icon"></slot></div> <div class="icon" ref="icon"><slot name="icon"></slot></div>
<div class="input"> <div class="input">
<div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength"> <div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
@@ -41,6 +41,7 @@ import Vue from 'vue';
const getPasswordStrength = require('syuilo-password-strength'); const getPasswordStrength = require('syuilo-password-strength');
export default Vue.extend({ export default Vue.extend({
inject: ['horizonGrouped'],
props: { props: {
value: { value: {
required: false required: false
@@ -72,6 +73,13 @@ export default Vue.extend({
required: false, required: false,
default: false default: false
}, },
inline: {
type: Boolean,
required: false,
default(): boolean {
return this.horizonGrouped;
}
},
styl: { styl: {
type: String, type: String,
required: false, required: false,
@@ -337,4 +345,8 @@ root(fill)
&:not(.fill) &:not(.fill)
root(false) root(false)
&.inline
display inline-block
margin 0
</style> </style>

View File

@@ -31,7 +31,7 @@
<p>%fa:cog%<span>%i18n:@settings%</span>%fa:angle-right%</p> <p>%fa:cog%<span>%i18n:@settings%</span>%fa:angle-right%</p>
</li> </li>
<li v-if="$store.state.i.isAdmin"> <li v-if="$store.state.i.isAdmin">
<router-link to="/admin">%fa:terminal%<span>%i18n:@admin%</span>%fa:angle-right%</router-link> <a href="/admin">%fa:terminal%<span>%i18n:@admin%</span>%fa:angle-right%</a>
</li> </li>
</ul> </ul>
<ul> <ul>

View File

@@ -30,7 +30,7 @@
<ul> <ul>
<li><a @click="search">%fa:search%%i18n:@search%%fa:angle-right%</a></li> <li><a @click="search">%fa:search%%i18n:@search%%fa:angle-right%</a></li>
<li><router-link to="/i/settings" :data-active="$route.name == 'settings'">%fa:cog%%i18n:@settings%%fa:angle-right%</router-link></li> <li><router-link to="/i/settings" :data-active="$route.name == 'settings'">%fa:cog%%i18n:@settings%%fa:angle-right%</router-link></li>
<li v-if="$store.getters.isSignedIn && $store.state.i.isAdmin"><router-link to="/admin">%fa:terminal%<span>%i18n:@admin%</span>%fa:angle-right%</router-link></li> <li v-if="$store.getters.isSignedIn && $store.state.i.isAdmin"><a href="/admin">%fa:terminal%<span>%i18n:@admin%</span>%fa:angle-right%</a></li>
<li @click="dark"><p><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template><span>%i18n:@darkmode%</span></p></li> <li @click="dark"><p><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template><span>%i18n:@darkmode%</span></p></li>
</ul> </ul>
</div> </div>

View File

@@ -215,5 +215,11 @@
reversiGameEmptyCell: ':lighten<2<$secondary', reversiGameEmptyCell: ':lighten<2<$secondary',
reversiGameEmptyCellMyTurn: ':lighten<5<$secondary', reversiGameEmptyCellMyTurn: ':lighten<5<$secondary',
reversiGameEmptyCellCanPut: ':lighten<4<$secondary', reversiGameEmptyCellCanPut: ':lighten<4<$secondary',
adminDashboardHeaderFg: ':alpha<0.9<$text',
adminDashboardHeaderBorder: 'rgba(0, 0, 0, 0.3)',
adminDashboardCardBg: '$secondary',
adminDashboardCardFg: '$text',
adminDashboardCardDivider: 'rgba(0, 0, 0, 0.3)',
}, },
} }

View File

@@ -215,5 +215,11 @@
reversiGameEmptyCell: 'rgba(0, 0, 0, 0.06)', reversiGameEmptyCell: 'rgba(0, 0, 0, 0.06)',
reversiGameEmptyCellMyTurn: 'rgba(0, 0, 0, 0.12)', reversiGameEmptyCellMyTurn: 'rgba(0, 0, 0, 0.12)',
reversiGameEmptyCellCanPut: 'rgba(0, 0, 0, 0.9)', reversiGameEmptyCellCanPut: 'rgba(0, 0, 0, 0.9)',
adminDashboardHeaderFg: ':alpha<0.9<$text',
adminDashboardHeaderBorder: 'rgba(0, 0, 0, 0.1)',
adminDashboardCardBg: '$secondary',
adminDashboardCardFg: '$text',
adminDashboardCardDivider: 'rgba(0, 0, 0, 0.082)',
}, },
} }

View File

@@ -9,7 +9,7 @@ export type TextElementEmoji = {
}; };
export default function(text: string) { export default function(text: string) {
const match = text.match(/^:([a-zA-Z0-9+-_]+?):/); const match = text.match(/^:([a-zA-Z0-9+_-]+):/);
if (!match) return null; if (!match) return null;
const emoji = match[0]; const emoji = match[0];
return { return {

View File

@@ -4,7 +4,7 @@ import parse from '../../../mfm/parse';
export default function(note: INote) { export default function(note: INote) {
let html = toHtml(parse(note.text), note.mentionedRemoteUsers); let html = toHtml(parse(note.text), note.mentionedRemoteUsers);
if (html == null) html = ''; if (html == null) html = '<p>.</p>';
return html; return html;
} }

View File

@@ -27,12 +27,15 @@ export const meta = {
}; };
export default define(meta, (ps) => new Promise(async (res, rej) => { export default define(meta, (ps) => new Promise(async (res, rej) => {
await Emoji.insert({ const emoji = await Emoji.insert({
updatedAt: new Date(),
name: ps.name, name: ps.name,
host: null, host: null,
aliases: ps.aliases, aliases: ps.aliases,
url: ps.url url: ps.url
}); });
res(); res({
id: emoji._id
});
})); }));

View File

@@ -39,6 +39,7 @@ export default define(meta, (ps) => new Promise(async (res, rej) => {
await Emoji.update({ _id: emoji._id }, { await Emoji.update({ _id: emoji._id }, {
$set: { $set: {
updatedAt: new Date(),
name: ps.name, name: ps.name,
aliases: ps.aliases, aliases: ps.aliases,
url: ps.url url: ps.url