summaryrefslogtreecommitdiff
path: root/src/client/app
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2018-08-30 03:56:51 +0900
committerGitHub <noreply@github.com>2018-08-30 03:56:51 +0900
commit4e11da98d90c1c44fce1abaf63c248896feff03a (patch)
treecbe91363f87a3cc29b142433c16e4b16ccf2aa7d /src/client/app
parentNew translations ja-JP.yml (French) (diff)
parent:art: (diff)
downloadmisskey-4e11da98d90c1c44fce1abaf63c248896feff03a.tar.gz
misskey-4e11da98d90c1c44fce1abaf63c248896feff03a.tar.bz2
misskey-4e11da98d90c1c44fce1abaf63c248896feff03a.zip
Merge branch 'develop' into l10n_develop
Diffstat (limited to 'src/client/app')
-rw-r--r--src/client/app/boot.js19
-rw-r--r--src/client/app/common/views/components/nav.vue4
-rw-r--r--src/client/app/common/views/components/url-preview.vue187
-rw-r--r--src/client/app/common/views/filters/bytes.ts8
-rw-r--r--src/client/app/common/views/widgets/donation.vue15
-rw-r--r--src/client/app/desktop/api/update-avatar.ts67
-rw-r--r--src/client/app/desktop/api/update-banner.ts13
-rw-r--r--src/client/app/desktop/script.ts4
-rw-r--r--src/client/app/desktop/views/components/charts.chart.ts42
-rw-r--r--src/client/app/desktop/views/components/charts.vue587
-rw-r--r--src/client/app/desktop/views/components/note-detail.vue2
-rw-r--r--src/client/app/desktop/views/components/notes.note.vue2
-rw-r--r--src/client/app/desktop/views/components/settings.vue7
-rw-r--r--src/client/app/desktop/views/components/ui.header.account.vue11
-rw-r--r--src/client/app/desktop/views/components/ui.header.nav.vue2
-rw-r--r--src/client/app/desktop/views/components/ui.header.vue2
-rw-r--r--src/client/app/desktop/views/components/user-preview.vue2
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.dashboard.vue20
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.drive-chart.chart.vue51
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.drive-chart.vue34
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue76
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.notes-chart.vue34
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.suspend-user.vue2
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue2
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.unverify-user.vue2
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue51
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.users-chart.vue34
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.verify-user.vue2
-rw-r--r--src/client/app/desktop/views/pages/admin/admin.vue22
-rw-r--r--src/client/app/desktop/views/pages/deck/deck.note.vue2
-rw-r--r--src/client/app/desktop/views/pages/share.vue7
-rw-r--r--src/client/app/desktop/views/pages/stats/stats.vue64
-rw-r--r--src/client/app/desktop/views/pages/user/user.friends.vue19
-rw-r--r--src/client/app/desktop/views/pages/user/user.photos.vue13
-rw-r--r--src/client/app/desktop/views/pages/user/user.vue2
-rw-r--r--src/client/app/init.ts4
-rw-r--r--src/client/app/mobile/views/components/note-detail.vue2
-rw-r--r--src/client/app/mobile/views/components/note.vue2
-rw-r--r--src/client/app/mobile/views/components/ui.nav.vue1
-rw-r--r--src/client/app/mobile/views/pages/settings.vue11
-rw-r--r--src/client/app/mobile/views/pages/share.vue7
-rw-r--r--src/client/app/mobile/views/pages/user.vue2
-rw-r--r--src/client/app/store.ts1
43 files changed, 984 insertions, 457 deletions
diff --git a/src/client/app/boot.js b/src/client/app/boot.js
index 952881f6cb..54397c98c6 100644
--- a/src/client/app/boot.js
+++ b/src/client/app/boot.js
@@ -38,15 +38,22 @@
//#endregion
//#region Detect the user language
- let lang = navigator.language;
+ let lang = null;
- if (!LANGS.includes(lang)) lang = lang.split('-')[0];
+ if (LANGS.includes(navigator.language)) {
+ lang = navigator.language;
+ } else {
+ lang = LANGS.find(x => x.split('-')[0] == navigator.language);
- // The default language is English
- if (!LANGS.includes(lang)) lang = 'en';
+ if (lang == null) {
+ // Fallback
+ lang = 'en-US';
+ }
+ }
- if (settings) {
- if (settings.device.lang) lang = settings.device.lang;
+ if (settings && settings.device.lang &&
+ LANGS.includes(settings.device.lang)) {
+ lang = settings.device.lang;
}
//#endregion
diff --git a/src/client/app/common/views/components/nav.vue b/src/client/app/common/views/components/nav.vue
index 1f745bf69d..27e66358e4 100644
--- a/src/client/app/common/views/components/nav.vue
+++ b/src/client/app/common/views/components/nav.vue
@@ -26,8 +26,8 @@ export default Vue.extend({
},
created() {
(this as any).os.getMeta().then(meta => {
- if (meta.repositoryUrl) this.repositoryUrl = meta.repositoryUrl;
- if (meta.feedbackUrl) this.feedbackUrl = meta.feedbackUrl;
+ if (meta.maintainer.repository_url) this.repositoryUrl = meta.maintainer.repository_url;
+ if (meta.maintainer.feedback_url) this.feedbackUrl = meta.maintainer.feedback_url;
});
}
});
diff --git a/src/client/app/common/views/components/url-preview.vue b/src/client/app/common/views/components/url-preview.vue
index be69012737..242d9ba5c6 100644
--- a/src/client/app/common/views/components/url-preview.vue
+++ b/src/client/app/common/views/components/url-preview.vue
@@ -28,18 +28,99 @@
import Vue from 'vue';
import { url as misskeyUrl } from '../../../config';
+// THIS IS THE WHITELIST FOR THE EMBED PLAYER
+const whiteList = [
+ 'afreecatv.com',
+ 'aparat.com',
+ 'applemusic.com',
+ 'amazon.com',
+ 'awa.fm',
+ 'bandcamp.com',
+ 'bbc.co.uk',
+ 'beatport.com',
+ 'bilibili.com',
+ 'boomstream.com',
+ 'breakers.tv',
+ 'cam4.com',
+ 'cavelis.net',
+ 'chaturbate.com',
+ 'cnn.com',
+ 'cybergame.tv',
+ 'dailymotion.com',
+ 'deezer.com',
+ 'djlive.pl',
+ 'e-onkyo.com',
+ 'eventials.com',
+ 'facebook.com',
+ 'fc2.com',
+ 'gameplank.tv',
+ 'goodgame.ru',
+ 'google.com',
+ 'hardtunes.com',
+ 'instagram.com',
+ 'johnnylooch.com',
+ 'kexp.org',
+ 'lahzenegar.com',
+ 'liveedu.tv',
+ 'livetube.cc',
+ 'livestream.com',
+ 'meridix.com',
+ 'mixcloud.com',
+ 'mixer.com',
+ 'mobcrush.com',
+ 'mylive.in.th',
+ 'myspace.com',
+ 'netflix.com',
+ 'newretrowave.com',
+ 'nhk.or.jp',
+ 'nicovideo.jp',
+ 'nico.ms',
+ 'noisetrade.com',
+ 'nood.tv',
+ 'npr.org',
+ 'openrec.tv',
+ 'pandora.com',
+ 'pandora.tv',
+ 'picarto.tv',
+ 'pscp.tv',
+ 'restream.io',
+ 'reverbnation.com',
+ 'sermonaudio.com',
+ 'smashcast.tv',
+ 'songkick.com',
+ 'soundcloud.com',
+ 'spinninrecords.com',
+ 'spotify.com',
+ 'stitcher.com',
+ 'stream.me',
+ 'switchboard.live',
+ 'tunein.com',
+ 'twitcasting.tv',
+ 'twitch.tv',
+ 'twitter.com',
+ 'vaughnlive.tv',
+ 'veoh.com',
+ 'vimeo.com',
+ 'watchpeoplecode.com',
+ 'web.tv',
+ 'youtube.com',
+ 'youtu.be'
+];
+
export default Vue.extend({
props: {
url: {
type: String,
require: true
},
+
detail: {
type: Boolean,
required: false,
default: false
}
},
+
data() {
return {
fetching: true,
@@ -57,6 +138,7 @@ export default Vue.extend({
misskeyUrl
};
},
+
created() {
const url = new URL(this.url);
@@ -81,102 +163,27 @@ export default Vue.extend({
}
return;
}
+
fetch('/url?url=' + encodeURIComponent(this.url)).then(res => {
res.json().then(info => {
- if (info.url != null) {
- this.title = info.title;
- this.description = info.description;
- this.thumbnail = info.thumbnail;
- this.icon = info.icon;
- this.sitename = info.sitename;
- this.fetching = false;
- if ([ // THIS IS THE WHITELIST FOR THE EMBED PLAYER
- 'afreecatv.com',
- 'aparat.com',
- 'applemusic.com',
- 'amazon.com',
- 'awa.fm',
- 'bandcamp.com',
- 'bbc.co.uk',
- 'beatport.com',
- 'bilibili.com',
- 'boomstream.com',
- 'breakers.tv',
- 'cam4.com',
- 'cavelis.net',
- 'chaturbate.com',
- 'cnn.com',
- 'cybergame.tv',
- 'dailymotion.com',
- 'deezer.com',
- 'djlive.pl',
- 'e-onkyo.com',
- 'eventials.com',
- 'facebook.com',
- 'fc2.com',
- 'gameplank.tv',
- 'goodgame.ru',
- 'google.com',
- 'hardtunes.com',
- 'instagram.com',
- 'johnnylooch.com',
- 'kexp.org',
- 'lahzenegar.com',
- 'liveedu.tv',
- 'livetube.cc',
- 'livestream.com',
- 'meridix.com',
- 'mixcloud.com',
- 'mixer.com',
- 'mobcrush.com',
- 'mylive.in.th',
- 'myspace.com',
- 'netflix.com',
- 'newretrowave.com',
- 'nhk.or.jp',
- 'nicovideo.jp',
- 'nico.ms',
- 'noisetrade.com',
- 'nood.tv',
- 'npr.org',
- 'openrec.tv',
- 'pandora.com',
- 'pandora.tv',
- 'picarto.tv',
- 'pscp.tv',
- 'restream.io',
- 'reverbnation.com',
- 'sermonaudio.com',
- 'smashcast.tv',
- 'songkick.com',
- 'soundcloud.com',
- 'spinninrecords.com',
- 'spotify.com',
- 'stitcher.com',
- 'stream.me',
- 'switchboard.live',
- 'tunein.com',
- 'twitcasting.tv',
- 'twitch.tv',
- 'twitter.com',
- 'vaughnlive.tv',
- 'veoh.com',
- 'vimeo.com',
- 'watchpeoplecode.com',
- 'web.tv',
- 'youtube.com',
- 'youtu.be'
- ].some(x => x == url.hostname || url.hostname.endsWith(`.${x}`)))
- this.player = info.player;
- } // info.url
- }) // json
- }); // fetch
- } // created
+ if (info.url == null) return;
+ this.title = info.title;
+ this.description = info.description;
+ this.thumbnail = info.thumbnail;
+ this.icon = info.icon;
+ this.sitename = info.sitename;
+ this.fetching = false;
+ if (whiteList.some(x => x == url.hostname || url.hostname.endsWith(`.${x}`))) {
+ this.player = info.player;
+ }
+ })
+ });
+ }
});
</script>
<style lang="stylus" scoped>
-.twitter
+.player
position relative
width 100%
diff --git a/src/client/app/common/views/filters/bytes.ts b/src/client/app/common/views/filters/bytes.ts
index 3afb11e9ae..f7a1b2690f 100644
--- a/src/client/app/common/views/filters/bytes.ts
+++ b/src/client/app/common/views/filters/bytes.ts
@@ -1,8 +1,10 @@
import Vue from 'vue';
Vue.filter('bytes', (v, digits = 0) => {
- const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
- if (v == 0) return '0Byte';
+ const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
+ if (v == 0) return '0';
+ const isMinus = v < 0;
+ if (isMinus) v = -v;
const i = Math.floor(Math.log(v) / Math.log(1024));
- return (v / Math.pow(1024, i)).toFixed(digits).replace(/\.0+$/, '') + sizes[i];
+ return (isMinus ? '-' : '') + (v / Math.pow(1024, i)).toFixed(digits).replace(/\.0+$/, '') + sizes[i];
});
diff --git a/src/client/app/common/views/widgets/donation.vue b/src/client/app/common/views/widgets/donation.vue
index 470576d5e6..544ca1bd9d 100644
--- a/src/client/app/common/views/widgets/donation.vue
+++ b/src/client/app/common/views/widgets/donation.vue
@@ -2,9 +2,9 @@
<div class="mkw-donation" :data-mobile="platform == 'mobile'">
<article>
<h1>%fa:heart%%i18n:@title%</h1>
- <p>
+ <p v-if="meta">
{{ '%i18n:@text%'.substr(0, '%i18n:@text%'.indexOf('{')) }}
- <a href="https://syuilo.com">@syuilo</a>
+ <a :href="meta.maintainer.url">{{ meta.maintainer.name }}</a>
{{ '%i18n:@text%'.substr('%i18n:@text%'.indexOf('}') + 1) }}
</p>
</article>
@@ -15,6 +15,17 @@
import define from '../../../common/define-widget';
export default define({
name: 'donation'
+}).extend({
+ data() {
+ return {
+ meta: null
+ };
+ },
+ created() {
+ (this as any).os.getMeta().then(meta => {
+ this.meta = meta;
+ });
+ }
});
</script>
diff --git a/src/client/app/desktop/api/update-avatar.ts b/src/client/app/desktop/api/update-avatar.ts
index 83820f92bd..e9d92d1eb1 100644
--- a/src/client/app/desktop/api/update-avatar.ts
+++ b/src/client/app/desktop/api/update-avatar.ts
@@ -3,8 +3,21 @@ import { apiUrl } from '../../config';
import CropWindow from '../views/components/crop-window.vue';
import ProgressDialog from '../views/components/progress-dialog.vue';
-export default (os: OS) => (cb, file = null) => {
- const fileSelected = file => {
+export default (os: OS) => {
+
+ const cropImage = file => new Promise((resolve, reject) => {
+
+ const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
+ if (!regex.test(file.name) ) {
+ os.apis.dialog({
+ title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
+ text: null,
+ actions: [{
+ text: '%i18n:common.got-it%'
+ }]
+ });
+ reject();
+ }
const w = os.new(CropWindow, {
image: file,
@@ -19,27 +32,29 @@ export default (os: OS) => (cb, file = null) => {
os.api('drive/folders/find', {
name: '%i18n:desktop.avatar%'
- }).then(iconFolder => {
- if (iconFolder.length === 0) {
+ }).then(avatarFolder => {
+ if (avatarFolder.length === 0) {
os.api('drive/folders/create', {
name: '%i18n:desktop.avatar%'
}).then(iconFolder => {
- upload(data, iconFolder);
+ resolve(upload(data, iconFolder));
});
} else {
- upload(data, iconFolder[0]);
+ resolve(upload(data, avatarFolder[0]));
}
});
});
w.$once('skipped', () => {
- set(file);
+ resolve(file);
});
+ w.$once('cancelled', reject);
+
document.body.appendChild(w.$el);
- };
+ });
- const upload = (data, folder) => {
+ const upload = (data, folder) => new Promise((resolve, reject) => {
const dialog = os.new(ProgressDialog, {
title: '%i18n:desktop.uploading-avatar%'
});
@@ -52,18 +67,19 @@ export default (os: OS) => (cb, file = null) => {
xhr.onload = e => {
const file = JSON.parse((e.target as any).response);
(dialog as any).close();
- set(file);
+ resolve(file);
};
+ xhr.onerror = reject;
xhr.upload.onprogress = e => {
if (e.lengthComputable) (dialog as any).update(e.loaded, e.total);
};
xhr.send(data);
- };
+ });
- const set = file => {
- os.api('i/update', {
+ const setAvatar = file => {
+ return os.api('i/update', {
avatarId: file.id
}).then(i => {
os.store.commit('updateIKeyValue', {
@@ -83,18 +99,21 @@ export default (os: OS) => (cb, file = null) => {
}]
});
- if (cb) cb(i);
+ return i;
});
};
- if (file) {
- fileSelected(file);
- } else {
- os.apis.chooseDriveFile({
- multiple: false,
- title: '%fa:image% %i18n:desktop.choose-avatar%'
- }).then(file => {
- fileSelected(file);
- });
- }
+ return (file = null) => {
+ const selectedFile = file
+ ? Promise.resolve(file)
+ : os.apis.chooseDriveFile({
+ multiple: false,
+ title: '%fa:image% %i18n:desktop.choose-avatar%'
+ });
+
+ return selectedFile
+ .then(cropImage)
+ .then(setAvatar)
+ .catch(err => err && console.warn(err));
+ };
};
diff --git a/src/client/app/desktop/api/update-banner.ts b/src/client/app/desktop/api/update-banner.ts
index 33c4e306a2..e8fa35149b 100644
--- a/src/client/app/desktop/api/update-banner.ts
+++ b/src/client/app/desktop/api/update-banner.ts
@@ -6,6 +6,19 @@ import ProgressDialog from '../views/components/progress-dialog.vue';
export default (os: OS) => {
const cropImage = file => new Promise((resolve, reject) => {
+
+ const regex = RegExp('\.(jpg|jpeg|png|gif|webp|bmp|tiff)$');
+ if (!regex.test(file.name) ) {
+ os.apis.dialog({
+ title: '%fa:info-circle% %i18n:desktop.invalid-filetype%',
+ text: null,
+ actions: [{
+ text: '%i18n:common.got-it%'
+ }]
+ });
+ reject();
+ }
+
const w = os.new(CropWindow, {
image: file,
title: '%i18n:desktop.banner-crop-title%',
diff --git a/src/client/app/desktop/script.ts b/src/client/app/desktop/script.ts
index 8dc0482191..f0e8a42662 100644
--- a/src/client/app/desktop/script.ts
+++ b/src/client/app/desktop/script.ts
@@ -25,6 +25,7 @@ import updateBanner from './api/update-banner';
import MkIndex from './views/pages/index.vue';
import MkDeck from './views/pages/deck/deck.vue';
import MkAdmin from './views/pages/admin/admin.vue';
+import MkStats from './views/pages/stats/stats.vue';
import MkUser from './views/pages/user/user.vue';
import MkFavorites from './views/pages/favorites.vue';
import MkSelectDrive from './views/pages/selectdrive.vue';
@@ -57,6 +58,7 @@ init(async (launch) => {
{ path: '/', name: 'index', component: MkIndex },
{ path: '/deck', name: 'deck', component: MkDeck },
{ path: '/admin', name: 'admin', component: MkAdmin },
+ { path: '/stats', name: 'stats', component: MkStats },
{ path: '/i/customize-home', component: MkHomeCustomize },
{ path: '/i/favorites', component: MkFavorites },
{ path: '/i/messaging/:user', component: MkMessagingRoom },
@@ -94,7 +96,7 @@ init(async (launch) => {
/**
* Init Notification
*/
- if ('Notification' in window) {
+ if ('Notification' in window && os.store.getters.isSignedIn) {
// 許可を得ていなかったらリクエスト
if ((Notification as any).permission == 'default') {
await Notification.requestPermission();
diff --git a/src/client/app/desktop/views/components/charts.chart.ts b/src/client/app/desktop/views/components/charts.chart.ts
new file mode 100644
index 0000000000..6a241631e9
--- /dev/null
+++ b/src/client/app/desktop/views/components/charts.chart.ts
@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import { Line } from 'vue-chartjs';
+import * as mergeOptions from 'merge-options';
+
+export default Vue.extend({
+ extends: Line,
+ props: {
+ data: {
+ required: true
+ },
+ opts: {
+ required: false
+ }
+ },
+ watch: {
+ data() {
+ this.render();
+ }
+ },
+ mounted() {
+ this.render();
+ },
+ methods: {
+ render() {
+ this.renderChart(this.data, mergeOptions({
+ responsive: true,
+ maintainAspectRatio: false,
+ scales: {
+ xAxes: [{
+ type: 'time',
+ distribution: 'series'
+ }]
+ },
+ tooltips: {
+ intersect: false,
+ mode: 'x',
+ position: 'nearest'
+ }
+ }, this.opts || {}));
+ }
+ }
+});
diff --git a/src/client/app/desktop/views/components/charts.vue b/src/client/app/desktop/views/components/charts.vue
new file mode 100644
index 0000000000..c4e92e429f
--- /dev/null
+++ b/src/client/app/desktop/views/components/charts.vue
@@ -0,0 +1,587 @@
+<template>
+<div class="gkgckalzgidaygcxnugepioremxvxvpt">
+ <header>
+ <b>%i18n:@title%:</b>
+ <select v-model="chartType">
+ <optgroup label="%i18n:@users%">
+ <option value="users">%i18n:@charts.users%</option>
+ <option value="users-total">%i18n:@charts.users-total%</option>
+ </optgroup>
+ <optgroup label="%i18n:@notes%">
+ <option value="notes">%i18n:@charts.notes%</option>
+ <option value="local-notes">%i18n:@charts.local-notes%</option>
+ <option value="remote-notes">%i18n:@charts.remote-notes%</option>
+ <option value="notes-total">%i18n:@charts.notes-total%</option>
+ </optgroup>
+ <optgroup label="%i18n:@drive%">
+ <option value="drive-files">%i18n:@charts.drive-files%</option>
+ <option value="drive-files-total">%i18n:@charts.drive-files-total%</option>
+ <option value="drive">%i18n:@charts.drive%</option>
+ <option value="drive-total">%i18n:@charts.drive-total%</option>
+ </optgroup>
+ </select>
+ <div>
+ <span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
+ </div>
+ </header>
+ <div>
+ <x-chart v-if="chart" :data="data[0]" :opts="data[1]"/>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import XChart from './charts.chart.ts';
+
+const colors = {
+ local: 'rgb(246, 88, 79)',
+ remote: 'rgb(65, 221, 222)',
+
+ localPlus: 'rgb(52, 178, 118)',
+ remotePlus: 'rgb(158, 255, 209)',
+ localMinus: 'rgb(255, 97, 74)',
+ remoteMinus: 'rgb(255, 149, 134)'
+};
+
+const rgba = (color: string): string => {
+ return color.replace('rgb', 'rgba').replace(')', ', 0.1)');
+};
+
+export default Vue.extend({
+ components: {
+ XChart
+ },
+
+ data() {
+ return {
+ chart: null,
+ chartType: 'notes',
+ span: 'hour'
+ };
+ },
+
+ computed: {
+ data(): any {
+ if (this.chart == null) return null;
+ switch (this.chartType) {
+ case 'users': return this.usersChart(false);
+ case 'users-total': return this.usersChart(true);
+ case 'notes': return this.notesChart('combined');
+ case 'local-notes': return this.notesChart('local');
+ case 'remote-notes': return this.notesChart('remote');
+ case 'notes-total': return this.notesTotalChart();
+ case 'drive': return this.driveChart();
+ case 'drive-total': return this.driveTotalChart();
+ case 'drive-files': return this.driveFilesChart();
+ case 'drive-files-total': return this.driveFilesTotalChart();
+ }
+ },
+
+ stats(): any[] {
+ return (
+ this.span == 'day' ? this.chart.perDay :
+ this.span == 'hour' ? this.chart.perHour :
+ null
+ );
+ }
+ },
+
+ created() {
+ (this as any).api('chart', {
+ limit: 32
+ }).then(chart => {
+ this.chart = chart;
+ });
+ },
+
+ methods: {
+ notesChart(type: string): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ normal: type == 'local' ? x.notes.local.diffs.normal : type == 'remote' ? x.notes.remote.diffs.normal : x.notes.local.diffs.normal + x.notes.remote.diffs.normal,
+ reply: type == 'local' ? x.notes.local.diffs.reply : type == 'remote' ? x.notes.remote.diffs.reply : x.notes.local.diffs.reply + x.notes.remote.diffs.reply,
+ renote: type == 'local' ? x.notes.local.diffs.renote : type == 'remote' ? x.notes.remote.diffs.renote : x.notes.local.diffs.renote + x.notes.remote.diffs.renote,
+ all: type == 'local' ? (x.notes.local.inc + -x.notes.local.dec) : type == 'remote' ? (x.notes.remote.inc + -x.notes.remote.dec) : (x.notes.local.inc + -x.notes.local.dec) + (x.notes.remote.inc + -x.notes.remote.dec)
+ }));
+
+ return [{
+ datasets: [{
+ label: 'All',
+ fill: false,
+ borderColor: '#555',
+ borderWidth: 2,
+ borderDash: [4, 4],
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.all }))
+ }, {
+ label: 'Renotes',
+ fill: true,
+ backgroundColor: 'rgba(161, 222, 65, 0.1)',
+ borderColor: '#a1de41',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.renote }))
+ }, {
+ label: 'Replies',
+ fill: true,
+ backgroundColor: 'rgba(247, 121, 108, 0.1)',
+ borderColor: '#f7796c',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.reply }))
+ }, {
+ label: 'Normal',
+ fill: true,
+ backgroundColor: 'rgba(65, 221, 222, 0.1)',
+ borderColor: '#41ddde',
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.normal }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: value => {
+ return Vue.filter('number')(value);
+ }
+ }
+ }]
+ },
+ tooltips: {
+ callbacks: {
+ label: (tooltipItem, data) => {
+ const label = data.datasets[tooltipItem.datasetIndex].label || '';
+ return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
+ }
+ }
+ }
+ }];
+ },
+
+ notesTotalChart(): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ localCount: x.notes.local.total,
+ remoteCount: x.notes.remote.total
+ }));
+
+ return [{
+ datasets: [{
+ label: 'Combined',
+ fill: false,
+ borderColor: '#555',
+ borderWidth: 2,
+ borderDash: [4, 4],
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteCount + x.localCount }))
+ }, {
+ label: 'Local',
+ fill: true,
+ backgroundColor: rgba(colors.local),
+ borderColor: colors.local,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localCount }))
+ }, {
+ label: 'Remote',
+ fill: true,
+ backgroundColor: rgba(colors.remote),
+ borderColor: colors.remote,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteCount }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: value => {
+ return Vue.filter('number')(value);
+ }
+ }
+ }]
+ },
+ tooltips: {
+ callbacks: {
+ label: (tooltipItem, data) => {
+ const label = data.datasets[tooltipItem.datasetIndex].label || '';
+ return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
+ }
+ }
+ }
+ }];
+ },
+
+ usersChart(total: boolean): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ localCount: total ? x.users.local.total : (x.users.local.inc + -x.users.local.dec),
+ remoteCount: total ? x.users.remote.total : (x.users.remote.inc + -x.users.remote.dec)
+ }));
+
+ return [{
+ datasets: [{
+ label: 'Combined',
+ fill: false,
+ borderColor: '#555',
+ borderWidth: 2,
+ borderDash: [4, 4],
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteCount + x.localCount }))
+ }, {
+ label: 'Local',
+ fill: true,
+ backgroundColor: rgba(colors.local),
+ borderColor: colors.local,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localCount }))
+ }, {
+ label: 'Remote',
+ fill: true,
+ backgroundColor: rgba(colors.remote),
+ borderColor: colors.remote,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteCount }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: value => {
+ return Vue.filter('number')(value);
+ }
+ }
+ }]
+ },
+ tooltips: {
+ callbacks: {
+ label: (tooltipItem, data) => {
+ const label = data.datasets[tooltipItem.datasetIndex].label || '';
+ return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
+ }
+ }
+ }
+ }];
+ },
+
+ driveChart(): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ localInc: x.drive.local.incSize,
+ localDec: -x.drive.local.decSize,
+ remoteInc: x.drive.remote.incSize,
+ remoteDec: -x.drive.remote.decSize,
+ }));
+
+ return [{
+ datasets: [{
+ label: 'All',
+ fill: false,
+ borderColor: '#555',
+ borderWidth: 2,
+ borderDash: [4, 4],
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localInc + x.localDec + x.remoteInc + x.remoteDec }))
+ }, {
+ label: 'Local +',
+ fill: true,
+ backgroundColor: rgba(colors.localPlus),
+ borderColor: colors.localPlus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localInc }))
+ }, {
+ label: 'Local -',
+ fill: true,
+ backgroundColor: rgba(colors.localMinus),
+ borderColor: colors.localMinus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localDec }))
+ }, {
+ label: 'Remote +',
+ fill: true,
+ backgroundColor: rgba(colors.remotePlus),
+ borderColor: colors.remotePlus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteInc }))
+ }, {
+ label: 'Remote -',
+ fill: true,
+ backgroundColor: rgba(colors.remoteMinus),
+ borderColor: colors.remoteMinus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteDec }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: value => {
+ return Vue.filter('bytes')(value, 1);
+ }
+ }
+ }]
+ },
+ tooltips: {
+ callbacks: {
+ label: (tooltipItem, data) => {
+ const label = data.datasets[tooltipItem.datasetIndex].label || '';
+ return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
+ }
+ }
+ }
+ }];
+ },
+
+ driveTotalChart(): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ localSize: x.drive.local.totalSize,
+ remoteSize: x.drive.remote.totalSize
+ }));
+
+ return [{
+ datasets: [{
+ label: 'Combined',
+ fill: false,
+ borderColor: '#555',
+ borderWidth: 2,
+ borderDash: [4, 4],
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteSize + x.localSize }))
+ }, {
+ label: 'Local',
+ fill: true,
+ backgroundColor: rgba(colors.local),
+ borderColor: colors.local,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localSize }))
+ }, {
+ label: 'Remote',
+ fill: true,
+ backgroundColor: rgba(colors.remote),
+ borderColor: colors.remote,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteSize }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: value => {
+ return Vue.filter('bytes')(value, 1);
+ }
+ }
+ }]
+ },
+ tooltips: {
+ callbacks: {
+ label: (tooltipItem, data) => {
+ const label = data.datasets[tooltipItem.datasetIndex].label || '';
+ return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
+ }
+ }
+ }
+ }];
+ },
+
+ driveFilesChart(): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ localInc: x.drive.local.incCount,
+ localDec: -x.drive.local.decCount,
+ remoteInc: x.drive.remote.incCount,
+ remoteDec: -x.drive.remote.decCount
+ }));
+
+ return [{
+ datasets: [{
+ label: 'All',
+ fill: false,
+ borderColor: '#555',
+ borderWidth: 2,
+ borderDash: [4, 4],
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localInc + x.localDec + x.remoteInc + x.remoteDec }))
+ }, {
+ label: 'Local +',
+ fill: true,
+ backgroundColor: rgba(colors.localPlus),
+ borderColor: colors.localPlus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localInc }))
+ }, {
+ label: 'Local -',
+ fill: true,
+ backgroundColor: rgba(colors.localMinus),
+ borderColor: colors.localMinus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localDec }))
+ }, {
+ label: 'Remote +',
+ fill: true,
+ backgroundColor: rgba(colors.remotePlus),
+ borderColor: colors.remotePlus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteInc }))
+ }, {
+ label: 'Remote -',
+ fill: true,
+ backgroundColor: rgba(colors.remoteMinus),
+ borderColor: colors.remoteMinus,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteDec }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: value => {
+ return Vue.filter('number')(value);
+ }
+ }
+ }]
+ },
+ tooltips: {
+ callbacks: {
+ label: (tooltipItem, data) => {
+ const label = data.datasets[tooltipItem.datasetIndex].label || '';
+ return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
+ }
+ }
+ }
+ }];
+ },
+
+ driveFilesTotalChart(): any {
+ const data = this.stats.slice().reverse().map(x => ({
+ date: new Date(x.date),
+ localCount: x.drive.local.totalCount,
+ remoteCount: x.drive.remote.totalCount,
+ }));
+
+ return [{
+ datasets: [{
+ label: 'Combined',
+ fill: false,
+ borderColor: '#555',
+ borderWidth: 2,
+ borderDash: [4, 4],
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localCount + x.remoteCount }))
+ }, {
+ label: 'Local',
+ fill: true,
+ backgroundColor: rgba(colors.local),
+ borderColor: colors.local,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.localCount }))
+ }, {
+ label: 'Remote',
+ fill: true,
+ backgroundColor: rgba(colors.remote),
+ borderColor: colors.remote,
+ borderWidth: 2,
+ pointBackgroundColor: '#fff',
+ lineTension: 0,
+ data: data.map(x => ({ t: x.date, y: x.remoteCount }))
+ }]
+ }, {
+ scales: {
+ yAxes: [{
+ ticks: {
+ callback: value => {
+ return Vue.filter('number')(value);
+ }
+ }
+ }]
+ },
+ tooltips: {
+ callbacks: {
+ label: (tooltipItem, data) => {
+ const label = data.datasets[tooltipItem.datasetIndex].label || '';
+ return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
+ }
+ }
+ }
+ }];
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+@import '~const.styl'
+
+.gkgckalzgidaygcxnugepioremxvxvpt
+ padding 32px
+ background #fff
+ box-shadow 0 2px 8px rgba(#000, 0.1)
+
+ *
+ user-select none
+
+ > header
+ display flex
+ margin 0 0 1em 0
+ padding 0 0 8px 0
+ font-size 1em
+ color #555
+ border-bottom solid 1px #eee
+
+ > b
+ margin-right 8px
+
+ > *:last-child
+ margin-left auto
+
+ *
+ &:not(.active)
+ color $theme-color
+ cursor pointer
+
+ > div
+ > *
+ display block
+ height 320px
+
+</style>
diff --git a/src/client/app/desktop/views/components/note-detail.vue b/src/client/app/desktop/views/components/note-detail.vue
index 227bcc349d..1ba4a9a447 100644
--- a/src/client/app/desktop/views/components/note-detail.vue
+++ b/src/client/app/desktop/views/components/note-detail.vue
@@ -47,7 +47,7 @@
</div>
<mk-poll v-if="p.poll" :note="p"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url" :detail="true"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
+ <a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="map" v-if="p.geo" ref="map"></div>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote"/>
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index 87acf7974d..7592ae3905 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -32,7 +32,7 @@
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
+ <a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
<div class="map" v-if="p.geo" ref="map"></div>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote"/>
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index df131a1a65..7d6f1d55fb 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -49,6 +49,7 @@
</div>
<mk-switch v-model="$store.state.settings.showPostFormOnTopOfTl" @change="onChangeShowPostFormOnTopOfTl" text="%i18n:@post-form-on-timeline%"/>
<mk-switch v-model="$store.state.settings.suggestRecentHashtags" @change="onChangeSuggestRecentHashtags" text="%i18n:@suggest-recent-hashtags%"/>
+ <mk-switch v-model="$store.state.settings.showClockOnHeader" @change="onChangeShowClockOnHeader" text="%i18n:@show-clock-on-header%"/>
<mk-switch v-model="$store.state.settings.showReplyTarget" @change="onChangeShowReplyTarget" text="%i18n:@show-reply-target%"/>
<mk-switch v-model="$store.state.settings.showMyRenotes" @change="onChangeShowMyRenotes" text="%i18n:@show-my-renotes%"/>
<mk-switch v-model="$store.state.settings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes" text="%i18n:@show-renoted-my-notes%"/>
@@ -333,6 +334,12 @@ export default Vue.extend({
value: v
});
},
+ onChangeShowClockOnHeader(v) {
+ this.$store.dispatch('settings/set', {
+ key: 'showClockOnHeader',
+ value: v
+ });
+ },
onChangeShowReplyTarget(v) {
this.$store.dispatch('settings/set', {
key: 'showReplyTarget',
diff --git a/src/client/app/desktop/views/components/ui.header.account.vue b/src/client/app/desktop/views/components/ui.header.account.vue
index 4e0fc1cf1a..5e26389d89 100644
--- a/src/client/app/desktop/views/components/ui.header.account.vue
+++ b/src/client/app/desktop/views/components/ui.header.account.vue
@@ -30,10 +30,8 @@
<li @click="settings">
<p>%fa:cog%<span>%i18n:@settings%</span>%fa:angle-right%</p>
</li>
- </ul>
- <ul>
- <li @click="signout">
- <p class="signout">%fa:power-off%<span>%i18n:@signout%</span></p>
+ <li v-if="$store.state.i.isAdmin">
+ <router-link to="/admin">%fa:terminal%<span>%i18n:@admin%</span>%fa:angle-right%</router-link>
</li>
</ul>
<ul>
@@ -41,6 +39,11 @@
<p><span>%i18n:@dark%</span><template v-if="$store.state.device.darkmode">%fa:moon%</template><template v-else>%fa:R moon%</template></p>
</li>
</ul>
+ <ul>
+ <li @click="signout">
+ <p class="signout">%fa:power-off%<span>%i18n:@signout%</span></p>
+ </li>
+ </ul>
</div>
</transition>
</div>
diff --git a/src/client/app/desktop/views/components/ui.header.nav.vue b/src/client/app/desktop/views/components/ui.header.nav.vue
index f01aade306..6292b764c6 100644
--- a/src/client/app/desktop/views/components/ui.header.nav.vue
+++ b/src/client/app/desktop/views/components/ui.header.nav.vue
@@ -11,7 +11,7 @@
<li class="deck" :class="{ active: $route.name == 'deck' }" @click="goToTop">
<router-link to="/deck">
%fa:columns%
- <p>%i18n:@deck% <small>(beta)</small></p>
+ <p>%i18n:@deck%</p>
</router-link>
</li>
<li class="messaging">
diff --git a/src/client/app/desktop/views/components/ui.header.vue b/src/client/app/desktop/views/components/ui.header.vue
index edd9829c1c..6de4eaf744 100644
--- a/src/client/app/desktop/views/components/ui.header.vue
+++ b/src/client/app/desktop/views/components/ui.header.vue
@@ -17,7 +17,7 @@
<x-account v-if="$store.getters.isSignedIn"/>
<x-notifications v-if="$store.getters.isSignedIn"/>
<x-post v-if="$store.getters.isSignedIn"/>
- <x-clock/>
+ <x-clock v-if="$store.state.settings.showClockOnHeader"/>
</div>
</div>
</div>
diff --git a/src/client/app/desktop/views/components/user-preview.vue b/src/client/app/desktop/views/components/user-preview.vue
index 7ef8dff5be..1e1755ec3c 100644
--- a/src/client/app/desktop/views/components/user-preview.vue
+++ b/src/client/app/desktop/views/components/user-preview.vue
@@ -48,7 +48,7 @@ export default Vue.extend({
this.open();
});
} else {
- const query = this.user[0] == '@' ?
+ const query = this.user.startsWith('@') ?
parseAcct(this.user.substr(1)) :
{ userId: this.user };
diff --git a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
index 3567585cb8..ebb54d782e 100644
--- a/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.dashboard.vue
@@ -1,16 +1,20 @@
<template>
-<div class="obdskegsannmntldydackcpzezagxqfy card">
+<div class="obdskegsannmntldydackcpzezagxqfy mk-admin-card">
<header>%i18n:@dashboard%</header>
<div v-if="stats" class="stats">
<div><b>%fa:user% {{ stats.originalUsersCount | number }}</b><span>%i18n:@original-users%</span></div>
<div><span>%fa:user% {{ stats.usersCount | number }}</span><span>%i18n:@all-users%</span></div>
- <div><b>%fa:pen% {{ stats.originalNotesCount | number }}</b><span>%i18n:@original-notes%</span></div>
- <div><span>%fa:pen% {{ stats.notesCount | number }}</span><span>%i18n:@all-notes%</span></div>
+ <div><b>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</b><span>%i18n:@original-notes%</span></div>
+ <div><span>%fa:pencil-alt% {{ stats.notesCount | number }}</span><span>%i18n:@all-notes%</span></div>
</div>
<div class="cpu-memory">
<x-cpu-memory :connection="connection"/>
</div>
<div>
+ <label>
+ <input type="checkbox" v-model="disableRegistration" @change="updateMeta">
+ <span>disableRegistration</span>
+ </label>
<button class="ui" @click="invite">%i18n:@invite%</button>
<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
</div>
@@ -28,6 +32,7 @@ export default Vue.extend({
data() {
return {
stats: null,
+ disableRegistration: false,
inviteCode: null,
connection: null,
connectionId: null
@@ -37,6 +42,10 @@ export default Vue.extend({
this.connection = (this as any).os.streams.serverStatsStream.getConnection();
this.connectionId = (this as any).os.streams.serverStatsStream.use();
+ (this as any).os.getMeta().then(meta => {
+ this.disableRegistration = meta.disableRegistration;
+ });
+
(this as any).api('stats').then(stats => {
this.stats = stats;
});
@@ -49,6 +58,11 @@ export default Vue.extend({
(this as any).api('admin/invite').then(x => {
this.inviteCode = x.code;
});
+ },
+ updateMeta() {
+ (this as any).api('admin/update-meta', {
+ disableRegistration: this.disableRegistration
+ });
}
}
});
diff --git a/src/client/app/desktop/views/pages/admin/admin.drive-chart.chart.vue b/src/client/app/desktop/views/pages/admin/admin.drive-chart.chart.vue
deleted file mode 100644
index 3c537d8d6d..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.drive-chart.chart.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-<template>
-<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
- <polyline
- :points="points"
- fill="none"
- stroke-width="1"
- stroke="#555"/>
-</svg>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: {
- chart: {
- required: true
- },
- type: {
- type: String,
- required: true
- }
- },
- data() {
- return {
- viewBoxX: 365,
- viewBoxY: 70,
- points: null
- };
- },
- created() {
- const peak = Math.max.apply(null, this.chart.map(d => this.type == 'local' ? d.drive.local.totalSize : d.drive.remote.totalSize));
-
- if (peak != 0) {
- const data = this.chart.slice().reverse().map(x => ({
- size: this.type == 'local' ? x.drive.local.totalSize : x.drive.remote.totalSize
- }));
-
- this.points = data.map((d, i) => `${i},${(1 - (d.size / peak)) * this.viewBoxY}`).join(' ');
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-svg
- display block
- padding 10px
- width 100%
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.drive-chart.vue b/src/client/app/desktop/views/pages/admin/admin.drive-chart.vue
deleted file mode 100644
index 4f94fd2372..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.drive-chart.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<template>
-<div class="card">
- <header>%i18n:@title%</header>
- <div class="card">
- <header>%i18n:@local%</header>
- <x-chart v-if="chart" :chart="chart" type="local"/>
- </div>
- <div class="card">
- <header>%i18n:@remote%</header>
- <x-chart v-if="chart" :chart="chart" type="remote"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from "vue";
-import XChart from "./admin.drive-chart.chart.vue";
-
-export default Vue.extend({
- components: {
- XChart
- },
- props: {
- chart: {
- required: true
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue b/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue
deleted file mode 100644
index 83c61c1313..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.notes-chart.chart.vue
+++ /dev/null
@@ -1,76 +0,0 @@
-<template>
-<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
- <polyline
- :points="pointsNote"
- fill="none"
- stroke-width="1"
- stroke="#41ddde"/>
- <polyline
- :points="pointsReply"
- fill="none"
- stroke-width="1"
- stroke="#f7796c"/>
- <polyline
- :points="pointsRenote"
- fill="none"
- stroke-width="1"
- stroke="#a1de41"/>
- <polyline
- :points="pointsTotal"
- fill="none"
- stroke-width="1"
- stroke="#555"
- stroke-dasharray="2 2"/>
-</svg>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: {
- chart: {
- required: true
- },
- type: {
- type: String,
- required: true
- }
- },
- data() {
- return {
- viewBoxX: 365,
- viewBoxY: 70,
- pointsNote: null,
- pointsReply: null,
- pointsRenote: null,
- pointsTotal: null
- };
- },
- created() {
- const peak = Math.max.apply(null, this.chart.map(d => this.type == 'local' ? d.notes.local.diff : d.notes.remote.diff));
-
- if (peak != 0) {
- const data = this.chart.slice().reverse().map(x => ({
- normal: this.type == 'local' ? x.notes.local.diffs.normal : x.notes.remote.diffs.normal,
- reply: this.type == 'local' ? x.notes.local.diffs.reply : x.notes.remote.diffs.reply,
- renote: this.type == 'local' ? x.notes.local.diffs.renote : x.notes.remote.diffs.renote,
- total: this.type == 'local' ? x.notes.local.diff : x.notes.remote.diff
- }));
-
- this.pointsNote = data.map((d, i) => `${i},${(1 - (d.normal / peak)) * this.viewBoxY}`).join(' ');
- this.pointsReply = data.map((d, i) => `${i},${(1 - (d.reply / peak)) * this.viewBoxY}`).join(' ');
- this.pointsRenote = data.map((d, i) => `${i},${(1 - (d.renote / peak)) * this.viewBoxY}`).join(' ');
- this.pointsTotal = data.map((d, i) => `${i},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-svg
- display block
- padding 10px
- width 100%
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue b/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue
deleted file mode 100644
index e4d396d9c6..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.notes-chart.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<template>
-<div class="card">
- <header>%i18n:@title%</header>
- <div class="card">
- <header>%i18n:@local%</header>
- <x-chart v-if="chart" :chart="chart" type="local"/>
- </div>
- <div class="card">
- <header>%i18n:@remote%</header>
- <x-chart v-if="chart" :chart="chart" type="remote"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from "vue";
-import XChart from "./admin.notes-chart.chart.vue";
-
-export default Vue.extend({
- components: {
- XChart
- },
- props: {
- chart: {
- required: true
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue b/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
index 59932f4be7..8d8e37e181 100644
--- a/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.suspend-user.vue
@@ -1,5 +1,5 @@
<template>
-<div class="card">
+<div class="mk-admin-card">
<header>%i18n:@suspend-user%</header>
<input v-model="username" type="text" class="ui"/>
<button class="ui" @click="suspendUser" :disabled="suspending">%i18n:@suspend%</button>
diff --git a/src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue b/src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue
index a75c0bd64e..ec423969be 100644
--- a/src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.unsuspend-user.vue
@@ -1,5 +1,5 @@
<template>
-<div class="card">
+<div class="mk-admin-card">
<header>%i18n:@unsuspend-user%</header>
<input v-model="username" type="text" class="ui"/>
<button class="ui" @click="unsuspendUser" :disabled="unsuspending">%i18n:@unsuspend%</button>
diff --git a/src/client/app/desktop/views/pages/admin/admin.unverify-user.vue b/src/client/app/desktop/views/pages/admin/admin.unverify-user.vue
index 72962870d9..e8204e69f4 100644
--- a/src/client/app/desktop/views/pages/admin/admin.unverify-user.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.unverify-user.vue
@@ -1,5 +1,5 @@
<template>
-<div class="card">
+<div class="mk-admin-card">
<header>%i18n:@unverify-user%</header>
<input v-model="username" type="text" class="ui"/>
<button class="ui" @click="unverifyUser" :disabled="unverifying">%i18n:@unverify%</button>
diff --git a/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue b/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue
deleted file mode 100644
index c2ab4a78e3..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.users-chart.chart.vue
+++ /dev/null
@@ -1,51 +0,0 @@
-<template>
-<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`">
- <polyline
- :points="points"
- fill="none"
- stroke-width="1"
- stroke="#555"/>
-</svg>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: {
- chart: {
- required: true
- },
- type: {
- type: String,
- required: true
- }
- },
- data() {
- return {
- viewBoxX: 365,
- viewBoxY: 70,
- points: null
- };
- },
- created() {
- const peak = Math.max.apply(null, this.chart.map(d => this.type == 'local' ? d.users.local.diff : d.users.remote.diff));
-
- if (peak != 0) {
- const data = this.chart.slice().reverse().map(x => ({
- count: this.type == 'local' ? x.users.local.diff : x.users.remote.diff
- }));
-
- this.points = data.map((d, i) => `${i},${(1 - (d.count / peak)) * this.viewBoxY}`).join(' ');
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-svg
- display block
- padding 10px
- width 100%
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.users-chart.vue b/src/client/app/desktop/views/pages/admin/admin.users-chart.vue
deleted file mode 100644
index e620012702..0000000000
--- a/src/client/app/desktop/views/pages/admin/admin.users-chart.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-<template>
-<div class="card">
- <header>%i18n:@title%</header>
- <div class="card">
- <header>%i18n:@local%</header>
- <x-chart v-if="chart" :chart="chart" type="local"/>
- </div>
- <div class="card">
- <header>%i18n:@remote%</header>
- <x-chart v-if="chart" :chart="chart" type="remote"/>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from "vue";
-import XChart from "./admin.users-chart.chart.vue";
-
-export default Vue.extend({
- components: {
- XChart
- },
- props: {
- chart: {
- required: true
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-</style>
diff --git a/src/client/app/desktop/views/pages/admin/admin.verify-user.vue b/src/client/app/desktop/views/pages/admin/admin.verify-user.vue
index 3902d4bddd..91fb04af80 100644
--- a/src/client/app/desktop/views/pages/admin/admin.verify-user.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.verify-user.vue
@@ -1,5 +1,5 @@
<template>
-<div class="card">
+<div class="mk-admin-card">
<header>%i18n:@verify-user%</header>
<input v-model="username" type="text" class="ui"/>
<button class="ui" @click="verifyUser" :disabled="verifying">%i18n:@verify%</button>
diff --git a/src/client/app/desktop/views/pages/admin/admin.vue b/src/client/app/desktop/views/pages/admin/admin.vue
index cbb1890cc3..3438462cd6 100644
--- a/src/client/app/desktop/views/pages/admin/admin.vue
+++ b/src/client/app/desktop/views/pages/admin/admin.vue
@@ -11,9 +11,7 @@
<main>
<div v-show="page == 'dashboard'">
<x-dashboard/>
- <x-users-chart :chart="chart"/>
- <x-notes-chart :chart="chart"/>
- <x-drive-chart :chart="chart"/>
+ <x-charts/>
</div>
<div v-if="page == 'users'">
<x-suspend-user/>
@@ -34,9 +32,7 @@ import XSuspendUser from "./admin.suspend-user.vue";
import XUnsuspendUser from "./admin.unsuspend-user.vue";
import XVerifyUser from "./admin.verify-user.vue";
import XUnverifyUser from "./admin.unverify-user.vue";
-import XUsersChart from "./admin.users-chart.vue";
-import XNotesChart from "./admin.notes-chart.vue";
-import XDriveChart from "./admin.drive-chart.vue";
+import XCharts from "../../components/charts.vue";
export default Vue.extend({
components: {
@@ -45,21 +41,13 @@ export default Vue.extend({
XUnsuspendUser,
XVerifyUser,
XUnverifyUser,
- XUsersChart,
- XNotesChart,
- XDriveChart
+ XCharts
},
data() {
return {
- page: 'dashboard',
- chart: null
+ page: 'dashboard'
};
},
- created() {
- (this as any).api('admin/chart').then(chart => {
- this.chart = chart;
- });
- },
methods: {
nav(page: string) {
this.page = page;
@@ -115,7 +103,7 @@ export default Vue.extend({
> div
max-width 800px
-.card
+.mk-admin-card
padding 32px
background #fff
box-shadow 0 2px 8px rgba(#000, 0.1)
diff --git a/src/client/app/desktop/views/pages/deck/deck.note.vue b/src/client/app/desktop/views/pages/deck/deck.note.vue
index c7df715a05..e6d062eac9 100644
--- a/src/client/app/desktop/views/pages/deck/deck.note.vue
+++ b/src/client/app/desktop/views/pages/deck/deck.note.vue
@@ -32,7 +32,7 @@
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
+ <a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote" :mini="true"/>
</div>
diff --git a/src/client/app/desktop/views/pages/share.vue b/src/client/app/desktop/views/pages/share.vue
index 4dd6080690..69ecbf115f 100644
--- a/src/client/app/desktop/views/pages/share.vue
+++ b/src/client/app/desktop/views/pages/share.vue
@@ -16,7 +16,7 @@ import Vue from 'vue';
export default Vue.extend({
data() {
return {
- name: (this as any).os.instanceName,
+ name: null,
posted: false,
text: new URLSearchParams(location.search).get('text')
};
@@ -25,6 +25,11 @@ export default Vue.extend({
close() {
window.close();
}
+ },
+ mounted() {
+ (this as any).os.getMeta().then(meta => {
+ this.name = meta.name;
+ });
}
});
</script>
diff --git a/src/client/app/desktop/views/pages/stats/stats.vue b/src/client/app/desktop/views/pages/stats/stats.vue
new file mode 100644
index 0000000000..41005b6398
--- /dev/null
+++ b/src/client/app/desktop/views/pages/stats/stats.vue
@@ -0,0 +1,64 @@
+<template>
+<div class="tcrwdhwpuxrwmcttxjcsehgpagpstqey">
+ <div v-if="stats" class="stats">
+ <div><b>%fa:user% {{ stats.originalUsersCount | number }}</b><span>%i18n:@original-users%</span></div>
+ <div><span>%fa:user% {{ stats.usersCount | number }}</span><span>%i18n:@all-users%</span></div>
+ <div><b>%fa:pencil-alt% {{ stats.originalNotesCount | number }}</b><span>%i18n:@original-notes%</span></div>
+ <div><span>%fa:pencil-alt% {{ stats.notesCount | number }}</span><span>%i18n:@all-notes%</span></div>
+ </div>
+ <div>
+ <x-charts/>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from "vue";
+import XCharts from "../../components/charts.vue";
+
+export default Vue.extend({
+ components: {
+ XCharts
+ },
+ data() {
+ return {
+ stats: null
+ };
+ },
+ created() {
+ (this as any).api('stats').then(stats => {
+ this.stats = stats;
+ });
+ },
+});
+</script>
+
+<style lang="stylus">
+@import '~const.styl'
+
+.tcrwdhwpuxrwmcttxjcsehgpagpstqey
+ width 100%
+ padding 16px
+
+ > .stats
+ display flex
+ justify-content center
+ margin-bottom 16px
+ padding 32px
+ background #fff
+ box-shadow 0 2px 8px rgba(#000, 0.1)
+
+ > div
+ flex 1
+ text-align center
+
+ > *:first-child
+ display block
+ color $theme-color
+
+ > *:last-child
+ font-size 70%
+
+ > div
+ max-width 850px
+</style>
diff --git a/src/client/app/desktop/views/pages/user/user.friends.vue b/src/client/app/desktop/views/pages/user/user.friends.vue
index 4af0f0bca6..516eea0288 100644
--- a/src/client/app/desktop/views/pages/user/user.friends.vue
+++ b/src/client/app/desktop/views/pages/user/user.friends.vue
@@ -40,10 +40,12 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
+root(isDark)
.friends
- background #fff
+ background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
+ overflow hidden
> .title
z-index 1
@@ -52,7 +54,8 @@ export default Vue.extend({
line-height 42px
font-size 0.9em
font-weight bold
- color #888
+ background isDark ? #313543 : inherit
+ color isDark ? #e3e5e8 : #888
box-shadow 0 1px rgba(#000, 0.07)
> i
@@ -70,7 +73,7 @@ export default Vue.extend({
> .user
padding 16px
- border-bottom solid 1px #eee
+ border-bottom solid 1px isDark ? #21242f : #eee
&:last-child
border-bottom none
@@ -96,18 +99,24 @@ export default Vue.extend({
margin 0
font-size 16px
line-height 24px
- color #555
+ color isDark ? #ccc : #555
> .username
display block
margin 0
font-size 15px
line-height 16px
- color #ccc
+ color isDark ? #555 : #ccc
> .mk-follow-button
position absolute
top 16px
right 16px
+.friends[data-darkmode]
+ root(true)
+
+.friends:not([data-darkmode])
+ root(false)
+
</style>
diff --git a/src/client/app/desktop/views/pages/user/user.photos.vue b/src/client/app/desktop/views/pages/user/user.photos.vue
index ce7791a96b..8397e56484 100644
--- a/src/client/app/desktop/views/pages/user/user.photos.vue
+++ b/src/client/app/desktop/views/pages/user/user.photos.vue
@@ -39,10 +39,12 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
+root(isDark)
.photos
- background #fff
+ background isDark ? #282C37 : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
+ overflow hidden
> .title
z-index 1
@@ -51,7 +53,8 @@ export default Vue.extend({
line-height 42px
font-size 0.9em
font-weight bold
- color #888
+ background: isDark ? #313543 : inherit
+ color isDark ? #e3e5e8 : #888
box-shadow 0 1px rgba(#000, 0.07)
> i
@@ -85,4 +88,10 @@ export default Vue.extend({
> i
margin-right 4px
+.photos[data-darkmode]
+ root(true)
+
+.photos:not([data-darkmode])
+ root(false)
+
</style>
diff --git a/src/client/app/desktop/views/pages/user/user.vue b/src/client/app/desktop/views/pages/user/user.vue
index 300fd68f06..afb5e674d9 100644
--- a/src/client/app/desktop/views/pages/user/user.vue
+++ b/src/client/app/desktop/views/pages/user/user.vue
@@ -138,7 +138,7 @@ root(isDark)
padding 16px
font-size 12px
color #aaa
- background #fff
+ background isDark ? #21242f : #fff
border solid 1px rgba(#000, 0.075)
border-radius 6px
diff --git a/src/client/app/init.ts b/src/client/app/init.ts
index 18f510ea24..cf97957400 100644
--- a/src/client/app/init.ts
+++ b/src/client/app/init.ts
@@ -19,8 +19,8 @@ import { version, codename, lang } from './config';
let elementLocale;
switch (lang) {
- case 'ja': elementLocale = ElementLocaleJa; break;
- case 'en': elementLocale = ElementLocaleEn; break;
+ case 'ja-JP': elementLocale = ElementLocaleJa; break;
+ case 'en-US': elementLocale = ElementLocaleEn; break;
default: elementLocale = ElementLocaleEn; break;
}
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index 317f08dcfa..f9996f9da6 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -45,7 +45,7 @@
</div>
<mk-poll v-if="p.poll" :note="p"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url" :detail="true"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
+ <a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="map" v-if="p.geo" ref="map"></div>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote"/>
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index 8fc8af7f8d..d0cea135f9 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -33,7 +33,7 @@
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
- <a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
+ <a class="location" v-if="p.geo" :href="`https://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% %i18n:@location%</a>
<div class="map" v-if="p.geo" ref="map"></div>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote"/>
diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue
index 74564a48bb..39ea513b76 100644
--- a/src/client/app/mobile/views/components/ui.nav.vue
+++ b/src/client/app/mobile/views/components/ui.nav.vue
@@ -30,6 +30,7 @@
<ul>
<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 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 @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>
</div>
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 6b82be099d..7437eb8b47 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -42,6 +42,12 @@
</ui-card>
<ui-card>
+ <div slot="title">%fa:volume-up% %i18n:@sound%</div>
+
+ <ui-switch v-model="enableSounds">%i18n:@enable-sounds%</ui-switch>
+ </ui-card>
+
+ <ui-card>
<div slot="title">%fa:language% %i18n:@lang%</div>
<ui-select v-model="lang" placeholder="%i18n:@auto%">
@@ -142,6 +148,11 @@ export default Vue.extend({
get() { return this.$store.state.device.lang; },
set(value) { this.$store.commit('device/set', { key: 'lang', value }); }
},
+
+ enableSounds: {
+ get() { return this.$store.state.device.enableSounds; },
+ set(value) { this.$store.commit('device/set', { key: 'enableSounds', value }); }
+ },
},
mounted() {
diff --git a/src/client/app/mobile/views/pages/share.vue b/src/client/app/mobile/views/pages/share.vue
index 588b0941e6..d75763c525 100644
--- a/src/client/app/mobile/views/pages/share.vue
+++ b/src/client/app/mobile/views/pages/share.vue
@@ -16,7 +16,7 @@ import Vue from 'vue';
export default Vue.extend({
data() {
return {
- name: (this as any).os.instanceName,
+ name: null,
posted: false,
text: new URLSearchParams(location.search).get('text')
};
@@ -25,6 +25,11 @@ export default Vue.extend({
close() {
window.close();
}
+ },
+ mounted() {
+ (this as any).os.getMeta().then(meta => {
+ this.name = meta.name;
+ });
}
});
</script>
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index e72867352f..8918847a8f 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -11,7 +11,7 @@
<a class="avatar">
<img :src="user.avatarUrl" alt="avatar"/>
</a>
- <mk-mute-button v-if="$store.state.i.id != user.id" :user="user"/>
+ <mk-mute-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
<mk-follow-button v-if="$store.getters.isSignedIn && $store.state.i.id != user.id" :user="user"/>
</div>
<div class="title">
diff --git a/src/client/app/store.ts b/src/client/app/store.ts
index ba91a11f25..469563495f 100644
--- a/src/client/app/store.ts
+++ b/src/client/app/store.ts
@@ -13,6 +13,7 @@ const defaultSettings = {
showMaps: true,
showPostFormOnTopOfTl: false,
suggestRecentHashtags: true,
+ showClockOnHeader: true,
circleIcons: true,
gradientWindowHeader: false,
showReplyTarget: true,