summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2018-05-19 20:50:43 +0900
committerGitHub <noreply@github.com>2018-05-19 20:50:43 +0900
commite52246a5f0516f621f59e635b6bb53ae86a93dc6 (patch)
tree47903f815bf335f43d263e25355a2274197668cc /src
parentNew translations ja.yml (Korean) (diff)
parent[wip] better mobile setting (diff)
downloadmisskey-e52246a5f0516f621f59e635b6bb53ae86a93dc6.tar.gz
misskey-e52246a5f0516f621f59e635b6bb53ae86a93dc6.tar.bz2
misskey-e52246a5f0516f621f59e635b6bb53ae86a93dc6.zip
Merge branch 'master' into l10n_master
Diffstat (limited to 'src')
-rw-r--r--src/client/app/ch/script.ts15
-rw-r--r--src/client/app/ch/style.styl10
-rw-r--r--src/client/app/ch/tags/channel.tag409
-rw-r--r--src/client/app/ch/tags/header.tag20
-rw-r--r--src/client/app/ch/tags/index.tag37
-rw-r--r--src/client/app/ch/tags/index.ts3
-rw-r--r--src/client/app/common/scripts/streaming/channel.ts13
-rw-r--r--src/client/app/common/views/widgets/access-log.vue91
-rw-r--r--src/client/app/common/views/widgets/index.ts2
-rw-r--r--src/client/app/desktop/views/components/activity.chart.vue2
-rw-r--r--src/client/app/desktop/views/components/choose-file-from-drive-window.vue11
-rw-r--r--src/client/app/desktop/views/components/choose-folder-from-drive-window.vue6
-rw-r--r--src/client/app/desktop/views/components/crop-window.vue6
-rw-r--r--src/client/app/desktop/views/components/follow-button.vue6
-rw-r--r--src/client/app/desktop/views/components/followers-window.vue2
-rw-r--r--src/client/app/desktop/views/components/followers.vue2
-rw-r--r--src/client/app/desktop/views/components/following-window.vue2
-rw-r--r--src/client/app/desktop/views/components/following.vue2
-rw-r--r--src/client/app/desktop/views/components/friends-maker.vue10
-rw-r--r--src/client/app/desktop/views/components/game-window.vue2
-rw-r--r--src/client/app/desktop/views/components/home.vue2
-rw-r--r--src/client/app/desktop/views/components/input-dialog.vue4
-rw-r--r--src/client/app/desktop/views/components/mentions.vue125
-rw-r--r--src/client/app/desktop/views/components/messaging-room-window.vue2
-rw-r--r--src/client/app/desktop/views/components/note-detail.sub.vue2
-rw-r--r--src/client/app/desktop/views/components/notes.note.vue6
-rw-r--r--src/client/app/desktop/views/components/notes.vue4
-rw-r--r--src/client/app/desktop/views/components/progress-dialog.vue2
-rw-r--r--src/client/app/desktop/views/components/settings.vue138
-rw-r--r--src/client/app/desktop/views/components/taskmanager.vue2
-rw-r--r--src/client/app/desktop/views/components/user-preview.vue2
-rw-r--r--src/client/app/desktop/views/pages/user/user.header.vue2
-rw-r--r--src/client/app/desktop/views/widgets/channel.channel.form.vue67
-rw-r--r--src/client/app/desktop/views/widgets/channel.channel.note.vue65
-rw-r--r--src/client/app/desktop/views/widgets/channel.channel.vue106
-rw-r--r--src/client/app/desktop/views/widgets/channel.vue108
-rw-r--r--src/client/app/desktop/views/widgets/index.ts2
-rw-r--r--src/client/app/mobile/script.ts13
-rw-r--r--src/client/app/mobile/style.styl7
-rw-r--r--src/client/app/mobile/views/components/note-detail.sub.vue2
-rw-r--r--src/client/app/mobile/views/components/note-detail.vue2
-rw-r--r--src/client/app/mobile/views/components/note-preview.vue2
-rw-r--r--src/client/app/mobile/views/components/note.sub.vue2
-rw-r--r--src/client/app/mobile/views/components/note.vue6
-rw-r--r--src/client/app/mobile/views/components/user-card.vue2
-rw-r--r--src/client/app/mobile/views/components/user-preview.vue2
-rw-r--r--src/client/app/mobile/views/pages/settings.vue65
-rw-r--r--src/client/app/mobile/views/pages/settings/settings.profile.vue126
-rw-r--r--src/client/app/mobile/views/pages/user.vue2
-rw-r--r--src/client/app/mobile/views/pages/widgets.vue1
-rw-r--r--src/client/md.scss13
-rw-r--r--src/models/channel-watching.ts13
-rw-r--r--src/models/channel.ts75
-rw-r--r--src/models/note.ts7
-rw-r--r--src/publishers/stream.ts5
-rw-r--r--src/renderers/get-note-summary.ts3
-rw-r--r--src/server/api/endpoints.ts28
-rw-r--r--src/server/api/endpoints/channels.ts58
-rw-r--r--src/server/api/endpoints/channels/create.ts35
-rw-r--r--src/server/api/endpoints/channels/notes.ts74
-rw-r--r--src/server/api/endpoints/channels/show.ts26
-rw-r--r--src/server/api/endpoints/channels/unwatch.ts56
-rw-r--r--src/server/api/endpoints/channels/watch.ts54
-rw-r--r--src/server/api/endpoints/notes/create.ts45
-rw-r--r--src/server/api/endpoints/notes/timeline.ts33
-rw-r--r--src/server/api/stream/channel.ts14
-rw-r--r--src/server/api/streaming.ts6
67 files changed, 326 insertions, 1741 deletions
diff --git a/src/client/app/ch/script.ts b/src/client/app/ch/script.ts
deleted file mode 100644
index 4c6b6dfd1b..0000000000
--- a/src/client/app/ch/script.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * Channels
- */
-
-// Style
-import './style.styl';
-
-require('./tags');
-import init from '../init';
-
-/**
- * init
- */
-init(() => {
-});
diff --git a/src/client/app/ch/style.styl b/src/client/app/ch/style.styl
deleted file mode 100644
index 21ca648cbe..0000000000
--- a/src/client/app/ch/style.styl
+++ /dev/null
@@ -1,10 +0,0 @@
-@import "../app"
-
-html
- padding 8px
- background #efefef
-
-#wait
- top auto
- bottom 15px
- left 15px
diff --git a/src/client/app/ch/tags/channel.tag b/src/client/app/ch/tags/channel.tag
deleted file mode 100644
index 74b1a9ba19..0000000000
--- a/src/client/app/ch/tags/channel.tag
+++ /dev/null
@@ -1,409 +0,0 @@
-<mk-channel>
- <mk-header/>
- <hr>
- <main v-if="!fetching">
- <h1>{ channel.title }</h1>
-
- <div v-if="$root.$data.os.isSignedIn">
- <p v-if="channel.isWatching">このチャンネルをウォッチしています <a @click="unwatch">ウォッチ解除</a></p>
- <p v-if="!channel.isWatching"><a @click="watch">このチャンネルをウォッチする</a></p>
- </div>
-
- <div class="share">
- <mk-twitter-button/>
- <mk-line-button/>
- </div>
-
- <div class="body">
- <p v-if="notesFetching">読み込み中<mk-ellipsis/></p>
- <div v-if="!notesFetching">
- <p v-if="notes == null || notes.length == 0">まだ投稿がありません</p>
- <template v-if="notes != null">
- <mk-channel-note each={ note in notes.slice().reverse() } note={ note } form={ parent.refs.form }/>
- </template>
- </div>
- </div>
- <hr>
- <mk-channel-form v-if="$root.$data.os.isSignedIn" channel={ channel } ref="form"/>
- <div v-if="!$root.$data.os.isSignedIn">
- <p>参加するには<a href={ _URL_ }>ログインまたは新規登録</a>してください</p>
- </div>
- <hr>
- <footer>
- <small><a href={ _URL_ }>Misskey</a> ver { _VERSION_ } (葵 aoi)</small>
- </footer>
- </main>
- <style lang="stylus" scoped>
- :scope
- display block
-
- > main
- > h1
- font-size 1.5em
- color #f00
-
- > .share
- > *
- margin-right 4px
-
- > .body
- margin 8px 0 0 0
-
- > mk-channel-form
- max-width 500px
-
- </style>
- <script lang="typescript">
- import Progress from '../../common/scripts/loading';
- import ChannelStream from '../../common/scripts/streaming/channel-stream';
-
- this.mixin('i');
- this.mixin('api');
-
- this.id = this.opts.id;
- this.fetching = true;
- this.notesFetching = true;
- this.channel = null;
- this.notes = null;
- this.connection = new ChannelStream(this.id);
- this.unreadCount = 0;
-
- this.on('mount', () => {
- document.documentElement.style.background = '#efefef';
-
- Progress.start();
-
- let fetched = false;
-
- // チャンネル概要読み込み
- this.$root.$data.os.api('channels/show', {
- channelId: this.id
- }).then(channel => {
- if (fetched) {
- Progress.done();
- } else {
- Progress.set(0.5);
- fetched = true;
- }
-
- this.update({
- fetching: false,
- channel: channel
- });
-
- document.title = channel.title + ' | Misskey'
- });
-
- // 投稿読み込み
- this.$root.$data.os.api('channels/notes', {
- channelId: this.id
- }).then(notes => {
- if (fetched) {
- Progress.done();
- } else {
- Progress.set(0.5);
- fetched = true;
- }
-
- this.update({
- notesFetching: false,
- notes: notes
- });
- });
-
- this.connection.on('note', this.onNote);
- document.addEventListener('visibilitychange', this.onVisibilitychange, false);
- });
-
- this.on('unmount', () => {
- this.connection.off('note', this.onNote);
- this.connection.close();
- document.removeEventListener('visibilitychange', this.onVisibilitychange);
- });
-
- this.onNote = note => {
- this.notes.unshift(note);
- this.update();
-
- if (document.hidden && this.$root.$data.os.isSignedIn && note.userId !== this.$root.$data.os.i.id) {
- this.unreadCount++;
- document.title = `(${this.unreadCount}) ${this.channel.title} | Misskey`;
- }
- };
-
- this.onVisibilitychange = () => {
- if (!document.hidden) {
- this.unreadCount = 0;
- document.title = this.channel.title + ' | Misskey'
- }
- };
-
- this.watch = () => {
- this.$root.$data.os.api('channels/watch', {
- channelId: this.id
- }).then(() => {
- this.channel.isWatching = true;
- this.update();
- }, e => {
- alert('error');
- });
- };
-
- this.unwatch = () => {
- this.$root.$data.os.api('channels/unwatch', {
- channelId: this.id
- }).then(() => {
- this.channel.isWatching = false;
- this.update();
- }, e => {
- alert('error');
- });
- };
- </script>
-</mk-channel>
-
-<mk-channel-note>
- <header>
- <a class="index" @click="reply">{ note.index }:</a>
- <a class="name" href={ _URL_ + '/@' + acct }><b>{ getUserName(note.user) }</b></a>
- <mk-time time={ note.createdAt }/>
- <mk-time time={ note.createdAt } mode="detail"/>
- <span>ID:<i>{ acct }</i></span>
- </header>
- <div>
- <a v-if="note.reply">&gt;&gt;{ note.reply.index }</a>
- { note.text }
- <div class="media" v-if="note.media">
- <template each={ file in note.media }>
- <a href={ file.url } target="_blank">
- <img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/>
- </a>
- </template>
- </div>
- </div>
- <style lang="stylus" scoped>
- :scope
- display block
- margin 0
- padding 0
-
- > header
- position -webkit-sticky
- position sticky
- z-index 1
- top 0
- background rgba(239, 239, 239, 0.9)
-
- > .index
- margin-right 0.25em
- color #000
-
- > .name
- margin-right 0.5em
- color #008000
-
- > mk-time
- margin-right 0.5em
-
- &:first-of-type
- display none
-
- @media (max-width 600px)
- > mk-time
- &:first-of-type
- display initial
-
- &:last-of-type
- display none
-
- > div
- padding 0 0 1em 2em
-
- > .media
- > a
- display inline-block
-
- > img
- max-width 100%
- vertical-align bottom
-
- </style>
- <script lang="typescript">
- import getAcct from '../../../../acct/render';
- import getUserName from '../../../../renderers/get-user-name';
-
- this.note = this.opts.note;
- this.form = this.opts.form;
- this.acct = getAcct(this.note.user);
- this.name = getUserName(this.note.user);
-
- this.reply = () => {
- this.form.update({
- reply: this.note
- });
- };
- </script>
-</mk-channel-note>
-
-<mk-channel-form>
- <p v-if="reply"><b>&gt;&gt;{ reply.index }</b> ({ getUserName(reply.user) }): <a @click="clearReply">[x]</a></p>
- <textarea ref="text" disabled={ wait } oninput={ update } onkeydown={ onkeydown } onpaste={ onpaste } placeholder="%i18n:ch.tags.mk-channel-form.textarea%"></textarea>
- <div class="actions">
- <button @click="selectFile">%fa:upload%%i18n:ch.tags.mk-channel-form.upload%</button>
- <button @click="drive">%fa:cloud%%i18n:ch.tags.mk-channel-form.drive%</button>
- <button :class="{ wait: wait }" ref="submit" disabled={ wait || (refs.text.value.length == 0) } @click="note">
- <template v-if="!wait">%fa:paper-plane%</template>{ wait ? '%i18n:!ch.tags.mk-channel-form.posting%' : '%i18n:!ch.tags.mk-channel-form.note%' }<mk-ellipsis v-if="wait"/>
- </button>
- </div>
- <mk-uploader ref="uploader"/>
- <ol v-if="files">
- <li each={ files }>{ name }</li>
- </ol>
- <input ref="file" type="file" accept="image/*" multiple="multiple" onchange={ changeFile }/>
- <style lang="stylus" scoped>
- :scope
- display block
-
- > textarea
- width 100%
- max-width 100%
- min-width 100%
- min-height 5em
-
- > .actions
- display flex
-
- > button
- > [data-fa]
- margin-right 0.25em
-
- &:last-child
- margin-left auto
-
- &.wait
- cursor wait
-
- > input[type='file']
- display none
-
- </style>
- <script lang="typescript">
- import getUserName from '../../../../renderers/get-user-name';
-
- this.mixin('api');
-
- this.channel = this.opts.channel;
- this.files = null;
-
- this.on('mount', () => {
- this.$refs.uploader.on('uploaded', file => {
- this.update({
- files: [file]
- });
- });
- });
-
- this.upload = file => {
- this.$refs.uploader.upload(file);
- };
-
- this.clearReply = () => {
- this.update({
- reply: null
- });
- };
-
- this.clear = () => {
- this.clearReply();
- this.update({
- files: null
- });
- this.$refs.text.value = '';
- };
-
- this.note = () => {
- this.update({
- wait: true
- });
-
- const files = this.files && this.files.length > 0
- ? this.files.map(f => f.id)
- : undefined;
-
- this.$root.$data.os.api('notes/create', {
- text: this.$refs.text.value == '' ? undefined : this.$refs.text.value,
- mediaIds: files,
- replyId: this.reply ? this.reply.id : undefined,
- channelId: this.channel.id
- }).then(data => {
- this.clear();
- }).catch(err => {
- alert('失敗した');
- }).then(() => {
- this.update({
- wait: false
- });
- });
- };
-
- this.changeFile = () => {
- Array.from(this.$refs.file.files).forEach(this.upload);
- };
-
- this.selectFile = () => {
- this.$refs.file.click();
- };
-
- this.drive = () => {
- window['cb'] = files => {
- this.update({
- files: files
- });
- };
-
- window.open(_URL_ + '/selectdrive?multiple=true',
- 'drive_window',
- 'height=500,width=800');
- };
-
- this.onkeydown = e => {
- if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey)) this.post();
- };
-
- this.onpaste = e => {
- Array.from(e.clipboardData.items).forEach(item => {
- if (item.kind == 'file') {
- this.upload(item.getAsFile());
- }
- });
- };
-
- this.getUserName = getUserName;
- </script>
-</mk-channel-form>
-
-<mk-twitter-button>
- <a href="https://twitter.com/share?ref_src=twsrc%5Etfw" class="twitter-share-button" data-show-count="false">Tweet</a>
- <script lang="typescript">
- this.on('mount', () => {
- const head = document.getElementsByTagName('head')[0];
- const script = document.createElement('script');
- script.setAttribute('src', 'https://platform.twitter.com/widgets.js');
- script.setAttribute('async', 'async');
- head.appendChild(script);
- });
- </script>
-</mk-twitter-button>
-
-<mk-line-button>
- <div class="line-it-button" data-lang="ja" data-type="share-a" data-url={ _CH_URL_ } style="display: none;"></div>
- <script lang="typescript">
- this.on('mount', () => {
- const head = document.getElementsByTagName('head')[0];
- const script = document.createElement('script');
- script.setAttribute('src', 'https://d.line-scdn.net/r/web/social-plugin/js/thirdparty/loader.min.js');
- script.setAttribute('async', 'async');
- head.appendChild(script);
- });
- </script>
-</mk-line-button>
diff --git a/src/client/app/ch/tags/header.tag b/src/client/app/ch/tags/header.tag
deleted file mode 100644
index 901123d63b..0000000000
--- a/src/client/app/ch/tags/header.tag
+++ /dev/null
@@ -1,20 +0,0 @@
-<mk-header>
- <div>
- <a href={ _CH_URL_ }>Index</a> | <a href={ _URL_ }>Misskey</a>
- </div>
- <div>
- <a v-if="!$root.$data.os.isSignedIn" href={ _URL_ }>ログイン(新規登録)</a>
- <a v-if="$root.$data.os.isSignedIn" href={ _URL_ + '/@' + I.username }>{ I.username }</a>
- </div>
- <style lang="stylus" scoped>
- :scope
- display flex
-
- > div:last-child
- margin-left auto
-
- </style>
- <script lang="typescript">
- this.mixin('i');
- </script>
-</mk-header>
diff --git a/src/client/app/ch/tags/index.tag b/src/client/app/ch/tags/index.tag
deleted file mode 100644
index 529b83b2c7..0000000000
--- a/src/client/app/ch/tags/index.tag
+++ /dev/null
@@ -1,37 +0,0 @@
-<mk-index>
- <mk-header/>
- <hr>
- <button @click="n">%i18n:ch.tags.mk-index.new%</button>
- <hr>
- <ul v-if="channels">
- <li each={ channels }><a href={ '/' + this.id }>{ this.title }</a></li>
- </ul>
- <style lang="stylus" scoped>
- :scope
- display block
-
- </style>
- <script lang="typescript">
- this.mixin('api');
-
- this.on('mount', () => {
- this.$root.$data.os.api('channels', {
- limit: 100
- }).then(channels => {
- this.update({
- channels: channels
- });
- });
- });
-
- this.n = () => {
- const title = window.prompt('%i18n:!ch.tags.mk-index.channel-title%');
-
- this.$root.$data.os.api('channels/create', {
- title: title
- }).then(channel => {
- location.href = '/' + channel.id;
- });
- };
- </script>
-</mk-index>
diff --git a/src/client/app/ch/tags/index.ts b/src/client/app/ch/tags/index.ts
deleted file mode 100644
index 12ffdaeb84..0000000000
--- a/src/client/app/ch/tags/index.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-require('./index.tag');
-require('./channel.tag');
-require('./header.tag');
diff --git a/src/client/app/common/scripts/streaming/channel.ts b/src/client/app/common/scripts/streaming/channel.ts
deleted file mode 100644
index be68ec0997..0000000000
--- a/src/client/app/common/scripts/streaming/channel.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import Stream from './stream';
-import MiOS from '../../../mios';
-
-/**
- * Channel stream connection
- */
-export default class Connection extends Stream {
- constructor(os: MiOS, channelId) {
- super(os, 'channel', {
- channel: channelId
- });
- }
-}
diff --git a/src/client/app/common/views/widgets/access-log.vue b/src/client/app/common/views/widgets/access-log.vue
deleted file mode 100644
index 8652e35645..0000000000
--- a/src/client/app/common/views/widgets/access-log.vue
+++ /dev/null
@@ -1,91 +0,0 @@
-<template>
-<div class="mkw-access-log">
- <mk-widget-container :show-header="props.design == 0">
- <template slot="header">%fa:server%%i18n:@title%</template>
-
- <div :class="$style.logs" ref="log">
- <p v-for="req in requests">
- <span :class="$style.ip" :style="`color:${ req.fg }; background:${ req.bg }`">{{ req.ip }}</span>
- <b>{{ req.method }}</b>
- <span>{{ req.path }}</span>
- </p>
- </div>
- </mk-widget-container>
-</div>
-</template>
-
-<script lang="ts">
-import define from '../../../common/define-widget';
-import * as seedrandom from 'seedrandom';
-
-export default define({
- name: 'broadcast',
- props: () => ({
- design: 0
- })
-}).extend({
- data() {
- return {
- requests: [],
- connection: null,
- connectionId: null
- };
- },
- mounted() {
- this.connection = (this as any).os.streams.requestsStream.getConnection();
- this.connectionId = (this as any).os.streams.requestsStream.use();
- this.connection.on('request', this.onRequest);
- },
- beforeDestroy() {
- this.connection.off('request', this.onRequest);
- (this as any).os.streams.requestsStream.dispose(this.connectionId);
- },
- methods: {
- onRequest(request) {
- const random = seedrandom(request.ip);
- const r = Math.floor(random() * 255);
- const g = Math.floor(random() * 255);
- const b = Math.floor(random() * 255);
- const luma = (0.2126 * r) + (0.7152 * g) + (0.0722 * b); // SMPTE C, Rec. 709 weightings
- request.bg = `rgb(${r}, ${g}, ${b})`;
- request.fg = luma >= 165 ? '#000' : '#fff';
-
- this.requests.push(request);
- if (this.requests.length > 30) this.requests.shift();
-
- (this.$refs.log as any).scrollTop = (this.$refs.log as any).scrollHeight;
- },
- func() {
- if (this.props.design == 1) {
- this.props.design = 0;
- } else {
- this.props.design++;
- }
- this.save();
- }
- }
-});
-</script>
-
-<style lang="stylus" module>
-.logs
- max-height 250px
- overflow auto
-
- > p
- margin 0
- padding 8px
- font-size 0.8em
- color #555
-
- &:nth-child(odd)
- background rgba(#000, 0.025)
-
- > b
- margin-right 4px
-
-.ip
- margin-right 4px
- padding 0 4px
-
-</style>
diff --git a/src/client/app/common/views/widgets/index.ts b/src/client/app/common/views/widgets/index.ts
index e41030e85a..9107d90ce7 100644
--- a/src/client/app/common/views/widgets/index.ts
+++ b/src/client/app/common/views/widgets/index.ts
@@ -1,6 +1,5 @@
import Vue from 'vue';
-import wAccessLog from './access-log.vue';
import wVersion from './version.vue';
import wRss from './rss.vue';
import wServer from './server.vue';
@@ -22,4 +21,3 @@ Vue.component('mkw-broadcast', wBroadcast);
Vue.component('mkw-server', wServer);
Vue.component('mkw-rss', wRss);
Vue.component('mkw-version', wVersion);
-Vue.component('mkw-access-log', wAccessLog);
diff --git a/src/client/app/desktop/views/components/activity.chart.vue b/src/client/app/desktop/views/components/activity.chart.vue
index 175c5d37ed..ff489d988b 100644
--- a/src/client/app/desktop/views/components/activity.chart.vue
+++ b/src/client/app/desktop/views/components/activity.chart.vue
@@ -1,6 +1,6 @@
<template>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none" @mousedown.prevent="onMousedown">
- <title>Black ... Total<br/>Blue ... Notes<br/>Red ... Replies<br/>Green ... Renotes</title>
+ <title>%i18n:@total%<br/>%i18n:@notes%<br/>%i18n:@replies%<br/>%i18n:@renotes%</title>
<polyline
:points="pointsNote"
fill="none"
diff --git a/src/client/app/desktop/views/components/choose-file-from-drive-window.vue b/src/client/app/desktop/views/components/choose-file-from-drive-window.vue
index 9a1e9c958a..30e59429d2 100644
--- a/src/client/app/desktop/views/components/choose-file-from-drive-window.vue
+++ b/src/client/app/desktop/views/components/choose-file-from-drive-window.vue
@@ -2,7 +2,7 @@
<mk-window ref="window" is-modal width="800px" height="500px" @closed="$destroy">
<span slot="header">
<span v-html="title" :class="$style.title"></span>
- <span :class="$style.count" v-if="multiple && files.length > 0">({{ files.length }}ファイル選択中)</span>
+ <span :class="$style.count" v-if="multiple && files.length > 0">({{ files.length }}%i18n:@choose-file%)</span>
</span>
<mk-drive
@@ -13,9 +13,9 @@
@change-selection="onChangeSelection"
/>
<div :class="$style.footer">
- <button :class="$style.upload" title="PCからドライブにファイルをアップロード" @click="upload">%fa:upload%</button>
- <button :class="$style.cancel" @click="cancel">キャンセル</button>
- <button :class="$style.ok" :disabled="multiple && files.length == 0" @click="ok">決定</button>
+ <button :class="$style.upload" title="%i18n:@upload%" @click="upload">%fa:upload%</button>
+ <button :class="$style.cancel" @click="cancel">%i18n:@cancel%</button>
+ <button :class="$style.ok" :disabled="multiple && files.length == 0" @click="ok">%i18n:@ok%</button>
</div>
</mk-window>
</template>
@@ -28,7 +28,7 @@ export default Vue.extend({
default: false
},
title: {
- default: '%fa:R file%ファイルを選択'
+ default: '%fa:R file%%i18n:@choose-prompt%s'
}
},
data() {
@@ -177,4 +177,3 @@ export default Vue.extend({
border-color #dcdcdc
</style>
-
diff --git a/src/client/app/desktop/views/components/choose-folder-from-drive-window.vue b/src/client/app/desktop/views/components/choose-folder-from-drive-window.vue
index f99533176d..0c4643fdcb 100644
--- a/src/client/app/desktop/views/components/choose-folder-from-drive-window.vue
+++ b/src/client/app/desktop/views/components/choose-folder-from-drive-window.vue
@@ -10,8 +10,8 @@
:multiple="false"
/>
<div :class="$style.footer">
- <button :class="$style.cancel" @click="cancel">キャンセル</button>
- <button :class="$style.ok" @click="ok">決定</button>
+ <button :class="$style.cancel" @click="cancel">%i18n:@cancel%</button>
+ <button :class="$style.ok" @click="ok">%i18n:@ok%</button>
</div>
</mk-window>
</template>
@@ -21,7 +21,7 @@ import Vue from 'vue';
export default Vue.extend({
props: {
title: {
- default: '%fa:R folder%フォルダを選択'
+ default: '%fa:R folder%%i18n:@choose-prompt%'
}
},
methods: {
diff --git a/src/client/app/desktop/views/components/crop-window.vue b/src/client/app/desktop/views/components/crop-window.vue
index eb6a55d959..4fa258549f 100644
--- a/src/client/app/desktop/views/components/crop-window.vue
+++ b/src/client/app/desktop/views/components/crop-window.vue
@@ -10,9 +10,9 @@
/>
</div>
<div :class="$style.actions">
- <button :class="$style.skip" @click="skip">クロップをスキップ</button>
- <button :class="$style.cancel" @click="cancel">キャンセル</button>
- <button :class="$style.ok" @click="ok">決定</button>
+ <button :class="$style.skip" @click="skip">%i18n:@skip%</button>
+ <button :class="$style.cancel" @click="cancel">%i18n:@cancel%</button>
+ <button :class="$style.ok" @click="ok">%i18n:@ok%</button>
</div>
</mk-window>
</template>
diff --git a/src/client/app/desktop/views/components/follow-button.vue b/src/client/app/desktop/views/components/follow-button.vue
index 60c6129f61..dae7604957 100644
--- a/src/client/app/desktop/views/components/follow-button.vue
+++ b/src/client/app/desktop/views/components/follow-button.vue
@@ -3,15 +3,15 @@
:class="{ wait, follow: !user.isFollowing, unfollow: user.isFollowing, big: size == 'big' }"
@click="onClick"
:disabled="wait"
- :title="user.isFollowing ? 'フォロー解除' : 'フォローする'"
+ :title="user.isFollowing ? '%i18n:@unfollow%' : '%i18n:@follow%'"
>
<template v-if="!wait && user.isFollowing">
<template v-if="size == 'compact'">%fa:minus%</template>
- <template v-if="size == 'big'">%fa:minus%フォロー解除</template>
+ <template v-if="size == 'big'">%fa:minus%%i18n:@unfollow%</template>
</template>
<template v-if="!wait && !user.isFollowing">
<template v-if="size == 'compact'">%fa:plus%</template>
- <template v-if="size == 'big'">%fa:plus%フォロー</template>
+ <template v-if="size == 'big'">%fa:plus%%i18n:@follow%</template>
</template>
<template v-if="wait">%fa:spinner .pulse .fw%</template>
</button>
diff --git a/src/client/app/desktop/views/components/followers-window.vue b/src/client/app/desktop/views/components/followers-window.vue
index 16206299d7..f3eec13e0b 100644
--- a/src/client/app/desktop/views/components/followers-window.vue
+++ b/src/client/app/desktop/views/components/followers-window.vue
@@ -1,7 +1,7 @@
<template>
<mk-window width="400px" height="550px" @closed="$destroy">
<span slot="header" :class="$style.header">
- <img :src="`${user.avatarUrl}?thumbnail&size=64`" alt=""/>{{ user | userName }}のフォロワー
+ <img :src="`${user.avatarUrl}?thumbnail&size=64`" alt=""/>%i18n:!@followers%.replace('{}', {{ user | userName }})
</span>
<mk-followers :user="user"/>
</mk-window>
diff --git a/src/client/app/desktop/views/components/followers.vue b/src/client/app/desktop/views/components/followers.vue
index a1b98995d8..1ef9f69771 100644
--- a/src/client/app/desktop/views/components/followers.vue
+++ b/src/client/app/desktop/views/components/followers.vue
@@ -4,7 +4,7 @@
:count="user.followersCount"
:you-know-count="user.followersYouKnowCount"
>
- フォロワーはいないようです。
+ %i18n:@empty%
</mk-users-list>
</template>
diff --git a/src/client/app/desktop/views/components/following-window.vue b/src/client/app/desktop/views/components/following-window.vue
index cc3d77198e..153819b12e 100644
--- a/src/client/app/desktop/views/components/following-window.vue
+++ b/src/client/app/desktop/views/components/following-window.vue
@@ -1,7 +1,7 @@
<template>
<mk-window width="400px" height="550px" @closed="$destroy">
<span slot="header" :class="$style.header">
- <img :src="`${user.avatarUrl}?thumbnail&size=64`" alt=""/>{{ user | userName }}のフォロー
+ <img :src="`${user.avatarUrl}?thumbnail&size=64`" alt=""/>%i18n:!@following%.replace('{}', {{ user | userName }})
</span>
<mk-following :user="user"/>
</mk-window>
diff --git a/src/client/app/desktop/views/components/following.vue b/src/client/app/desktop/views/components/following.vue
index b7aedda84f..d55ce1c0d4 100644
--- a/src/client/app/desktop/views/components/following.vue
+++ b/src/client/app/desktop/views/components/following.vue
@@ -4,7 +4,7 @@
:count="user.followingCount"
:you-know-count="user.followingYouKnowCount"
>
- フォロー中のユーザーはいないようです。
+ %i18n:@empty%
</mk-users-list>
</template>
diff --git a/src/client/app/desktop/views/components/friends-maker.vue b/src/client/app/desktop/views/components/friends-maker.vue
index 3c1f8b8257..7dfd9e4359 100644
--- a/src/client/app/desktop/views/components/friends-maker.vue
+++ b/src/client/app/desktop/views/components/friends-maker.vue
@@ -1,6 +1,6 @@
<template>
<div class="mk-friends-maker">
- <p class="title">気になるユーザーをフォロー:</p>
+ <p class="title">%i18n:@title%</p>
<div class="users" v-if="!fetching && users.length > 0">
<div class="user" v-for="user in users" :key="user.id">
<mk-avatar class="avatar" :user="user" target="_blank"/>
@@ -11,10 +11,10 @@
<mk-follow-button :user="user"/>
</div>
</div>
- <p class="empty" v-if="!fetching && users.length == 0">おすすめのユーザーは見つかりませんでした。</p>
- <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%読み込んでいます<mk-ellipsis/></p>
- <a class="refresh" @click="refresh">もっと見る</a>
- <button class="close" @click="$destroy()" title="閉じる">%fa:times%</button>
+ <p class="empty" v-if="!fetching && users.length == 0">%i18n:@empty%</p>
+ <p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:@fetching%<mk-ellipsis/></p>
+ <a class="refresh" @click="refresh">%i18n:@refresh%</a>
+ <button class="close" @click="$destroy()" title="%i18n:@close%">%fa:times%</button>
</div>
</template>
diff --git a/src/client/app/desktop/views/components/game-window.vue b/src/client/app/desktop/views/components/game-window.vue
index 3c8bf40e12..c3c0f90075 100644
--- a/src/client/app/desktop/views/components/game-window.vue
+++ b/src/client/app/desktop/views/components/game-window.vue
@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="$destroy">
- <span slot="header" :class="$style.header">%fa:gamepad%オセロ</span>
+ <span slot="header" :class="$style.header">%fa:gamepad%%i18n:@game%</span>
<mk-othello :class="$style.content" @gamed="g => game = g"/>
</mk-window>
</template>
diff --git a/src/client/app/desktop/views/components/home.vue b/src/client/app/desktop/views/components/home.vue
index a3d7927cfc..87dae5a806 100644
--- a/src/client/app/desktop/views/components/home.vue
+++ b/src/client/app/desktop/views/components/home.vue
@@ -21,8 +21,6 @@
<option value="polls">%i18n:@polls%</option>
<option value="post-form">%i18n:@post-form%</option>
<option value="messaging">%i18n:@messaging%</option>
- <option value="channel">%i18n:@channel%</option>
- <option value="access-log">%i18n:@access-log%</option>
<option value="server">%i18n:@server%</option>
<option value="donation">%i18n:@donation%</option>
<option value="nav">%i18n:@nav%</option>
diff --git a/src/client/app/desktop/views/components/input-dialog.vue b/src/client/app/desktop/views/components/input-dialog.vue
index e939fc1903..e2cf4e48fd 100644
--- a/src/client/app/desktop/views/components/input-dialog.vue
+++ b/src/client/app/desktop/views/components/input-dialog.vue
@@ -8,8 +8,8 @@
<input ref="text" v-model="text" :type="type" @keydown="onKeydown" :placeholder="placeholder"/>
</div>
<div :class="$style.actions">
- <button :class="$style.cancel" @click="cancel">キャンセル</button>
- <button :class="$style.ok" :disabled="!allowEmpty && text.length == 0" @click="ok">決定</button>
+ <button :class="$style.cancel" @click="cancel">%i18n:@cancel%</button>
+ <button :class="$style.ok" :disabled="!allowEmpty && text.length == 0" @click="ok">%i18n:@ok%</button>
</div>
</mk-window>
</template>
diff --git a/src/client/app/desktop/views/components/mentions.vue b/src/client/app/desktop/views/components/mentions.vue
deleted file mode 100644
index 66bdab5c08..0000000000
--- a/src/client/app/desktop/views/components/mentions.vue
+++ /dev/null
@@ -1,125 +0,0 @@
-<template>
-<div class="mk-mentions">
- <header>
- <span :data-active="mode == 'all'" @click="mode = 'all'">すべて</span>
- <span :data-active="mode == 'following'" @click="mode = 'following'">フォロー中</span>
- </header>
- <div class="fetching" v-if="fetching">
- <mk-ellipsis-icon/>
- </div>
- <p class="empty" v-if="notes.length == 0 && !fetching">
- %fa:R comments%
- <span v-if="mode == 'all'">あなた宛ての投稿はありません。</span>
- <span v-if="mode == 'following'">あなたがフォローしているユーザーからの言及はありません。</span>
- </p>
- <mk-notes :notes="notes" ref="timeline"/>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- data() {
- return {
- fetching: true,
- moreFetching: false,
- mode: 'all',
- notes: []
- };
- },
- watch: {
- mode() {
- this.fetch();
- }
- },
- mounted() {
- document.addEventListener('keydown', this.onDocumentKeydown);
- window.addEventListener('scroll', this.onScroll);
-
- this.fetch(() => this.$emit('loaded'));
- },
- beforeDestroy() {
- document.removeEventListener('keydown', this.onDocumentKeydown);
- window.removeEventListener('scroll', this.onScroll);
- },
- methods: {
- onDocumentKeydown(e) {
- if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
- if (e.which == 84) { // t
- (this.$refs.timeline as any).focus();
- }
- }
- },
- onScroll() {
- const current = window.scrollY + window.innerHeight;
- if (current > document.body.offsetHeight - 8) this.more();
- },
- fetch(cb?) {
- this.fetching = true;
- this.notes = [];
- (this as any).api('notes/mentions', {
- following: this.mode == 'following'
- }).then(notes => {
- this.notes = notes;
- this.fetching = false;
- if (cb) cb();
- });
- },
- more() {
- if (this.moreFetching || this.fetching || this.notes.length == 0) return;
- this.moreFetching = true;
- (this as any).api('notes/mentions', {
- following: this.mode == 'following',
- untilId: this.notes[this.notes.length - 1].id
- }).then(notes => {
- this.notes = this.notes.concat(notes);
- this.moreFetching = false;
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-@import '~const.styl'
-
-.mk-mentions
- background #fff
- border solid 1px rgba(#000, 0.075)
- border-radius 6px
-
- > header
- padding 8px 16px
- border-bottom solid 1px #eee
-
- > span
- margin-right 16px
- line-height 27px
- font-size 18px
- color #555
-
- &:not([data-active])
- color $theme-color
- cursor pointer
-
- &:hover
- text-decoration underline
-
- > .fetching
- padding 64px 0
-
- > .empty
- display block
- margin 0 auto
- padding 32px
- max-width 400px
- text-align center
- color #999
-
- > [data-fa]
- display block
- margin-bottom 16px
- font-size 3em
- color #ccc
-
-</style>
diff --git a/src/client/app/desktop/views/components/messaging-room-window.vue b/src/client/app/desktop/views/components/messaging-room-window.vue
index dbe3266734..cbb58b5e99 100644
--- a/src/client/app/desktop/views/components/messaging-room-window.vue
+++ b/src/client/app/desktop/views/components/messaging-room-window.vue
@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="$destroy">
- <span slot="header" :class="$style.header">%fa:comments%メッセージ: {{ user | userName }}</span>
+ <span slot="header" :class="$style.header">%fa:comments%%i18n:@title% {{ user | userName }}</span>
<mk-messaging-room :user="user" :class="$style.content"/>
</mk-window>
</template>
diff --git a/src/client/app/desktop/views/components/note-detail.sub.vue b/src/client/app/desktop/views/components/note-detail.sub.vue
index 32119da50d..c798b41b25 100644
--- a/src/client/app/desktop/views/components/note-detail.sub.vue
+++ b/src/client/app/desktop/views/components/note-detail.sub.vue
@@ -15,7 +15,7 @@
</header>
<div class="body">
<div class="text">
- <span v-if="note.isHidden" style="opacity: 0.5">(この投稿は非公開です)</span>
+ <span v-if="note.isHidden" style="opacity: 0.5">%i18n:@private%</span>
<mk-note-html v-if="note.text" :text="note.text" :i="os.i"/>
</div>
<div class="media" v-if="note.mediaIds.length > 0">
diff --git a/src/client/app/desktop/views/components/notes.note.vue b/src/client/app/desktop/views/components/notes.note.vue
index 8660a5f899..e23d3e5a52 100644
--- a/src/client/app/desktop/views/components/notes.note.vue
+++ b/src/client/app/desktop/views/components/notes.note.vue
@@ -33,9 +33,6 @@
</div>
</header>
<div class="body">
- <p class="channel" v-if="p.channel">
- <a :href="`${_CH_URL_}/${p.channel.id}`" target="_blank">{{ p.channel.title }}</a>:
- </p>
<p v-if="p.cw != null" class="cw">
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
<span class="toggle" @click="showContent = !showContent">{{ showContent ? '隠す' : 'もっと見る' }}</span>
@@ -574,9 +571,6 @@ root(isDark)
.mk-url-preview
margin-top 8px
- > .channel
- margin 0
-
> .mk-poll
font-size 80%
diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue
index 7e80e6f74a..c041e5278c 100644
--- a/src/client/app/desktop/views/components/notes.vue
+++ b/src/client/app/desktop/views/components/notes.vue
@@ -5,8 +5,8 @@
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
<div v-if="!fetching && requestInitPromise != null">
- <p>読み込みに失敗しました。</p>
- <button @click="resolveInitPromise">リトライ</button>
+ <p>%i18n:@error%</p>
+ <button @click="resolveInitPromise">%i18n:@retry%</button>
</div>
<transition-group name="mk-notes" class="transition">
diff --git a/src/client/app/desktop/views/components/progress-dialog.vue b/src/client/app/desktop/views/components/progress-dialog.vue
index a4292e1aec..2f59733d99 100644
--- a/src/client/app/desktop/views/components/progress-dialog.vue
+++ b/src/client/app/desktop/views/components/progress-dialog.vue
@@ -2,7 +2,7 @@
<mk-window ref="window" :is-modal="false" :can-close="false" width="500px" @closed="$destroy">
<span slot="header">{{ title }}<mk-ellipsis/></span>
<div :class="$style.body">
- <p :class="$style.init" v-if="isNaN(value)">待機中<mk-ellipsis/></p>
+ <p :class="$style.init" v-if="isNaN(value)">%i18n:@waiting%<mk-ellipsis/></p>
<p :class="$style.percentage" v-if="!isNaN(value)">{{ Math.floor((value / max) * 100) }}</p>
<progress :class="$style.progress"
v-if="!isNaN(value) && value < max"
diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue
index 4e5e281fd0..6652a8ac37 100644
--- a/src/client/app/desktop/views/components/settings.vue
+++ b/src/client/app/desktop/views/components/settings.vue
@@ -19,91 +19,91 @@
</section>
<section class="web" v-show="page == 'web'">
- <h1>動作</h1>
- <mk-switch v-model="clientSettings.fetchOnScroll" @change="onChangeFetchOnScroll" text="スクロールで自動読み込み">
- <span>ページを下までスクロールしたときに自動で追加のコンテンツを読み込みます。</span>
+ <h1>%i18n:@behaviour%</h1>
+ <mk-switch v-model="clientSettings.fetchOnScroll" @change="onChangeFetchOnScroll" text="%i18n:@fetch-on-scroll%">
+ <span>%i18n:@fetch-on-scroll-desc%</span>
</mk-switch>
- <mk-switch v-model="autoPopout" text="ウィンドウの自動ポップアウト">
- <span>ウィンドウが開かれるとき、ポップアウト(ブラウザ外に切り離す)可能なら自動でポップアウトします。この設定はブラウザに記憶されます。</span>
+ <mk-switch v-model="autoPopout" text="%i18n:@auto-popout%">
+ <span>%i18n:@auto-popout-desc%</span>
</mk-switch>
<details>
- <summary>詳細設定</summary>
- <mk-switch v-model="apiViaStream" text="ストリームを経由したAPIリクエスト">
- <span>この設定をオンにすると、websocket接続を経由してAPIリクエストが行われます(パフォーマンス向上が期待できます)。オフにすると、ネイティブの fetch APIが利用されます。この設定はこのデバイスのみ有効です。</span>
+ <summary>%i18n:@advanced%</summary>
+ <mk-switch v-model="apiViaStream" text="%i18n:@api-via-stream%">
+ <span>%i18n:@api-via-stream-desc%</span>
</mk-switch>
</details>
</section>
<section class="web" v-show="page == 'web'">
- <h1>デザインと表示</h1>
+ <h1>%i18n:@display%</h1>
<div class="div">
- <button class="ui button" @click="customizeHome" style="margin-bottom: 16px">ホームをカスタマイズ</button>
+ <button class="ui button" @click="customizeHome" style="margin-bottom: 16px">%i18n:@customize%</button>
</div>
<div class="div">
- <mk-switch v-model="darkmode" text="ダークモード"/>
- <mk-switch v-model="clientSettings.circleIcons" @change="onChangeCircleIcons" text="円形のアイコンを使用"/>
- <mk-switch v-model="clientSettings.gradientWindowHeader" @change="onChangeGradientWindowHeader" text="ウィンドウのタイトルバーにグラデーションを使用"/>
+ <mk-switch v-model="darkmode" text="%i18n:@dark-mode%"/>
+ <mk-switch v-model="clientSettings.circleIcons" @change="onChangeCircleIcons" text="%i18n:@circle-icons%"/>
+ <mk-switch v-model="clientSettings.gradientWindowHeader" @change="onChangeGradientWindowHeader" text="%i18n:@gradient-window-header%"/>
</div>
- <mk-switch v-model="clientSettings.showPostFormOnTopOfTl" @change="onChangeShowPostFormOnTopOfTl" text="タイムライン上部に投稿フォームを表示する"/>
- <mk-switch v-model="clientSettings.showReplyTarget" @change="onChangeShowReplyTarget" text="リプライ先を表示する"/>
- <mk-switch v-model="clientSettings.showMyRenotes" @change="onChangeShowMyRenotes" text="自分の行ったRenoteをタイムラインに表示する"/>
- <mk-switch v-model="clientSettings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes" text="Renoteされた自分の投稿をタイムラインに表示する"/>
- <mk-switch v-model="clientSettings.showMaps" @change="onChangeShowMaps" text="マップの自動展開">
- <span>位置情報が添付された投稿のマップを自動的に展開します。</span>
+ <mk-switch v-model="clientSettings.showPostFormOnTopOfTl" @change="onChangeShowPostFormOnTopOfTl" text="%i18n:@post-form-on-timeline%"/>
+ <mk-switch v-model="clientSettings.showReplyTarget" @change="onChangeShowReplyTarget" text="%i18n:@show-reply-target%"/>
+ <mk-switch v-model="clientSettings.showMyRenotes" @change="onChangeShowMyRenotes" text="%i18n:@show-my-renotes%"/>
+ <mk-switch v-model="clientSettings.showRenotedMyNotes" @change="onChangeShowRenotedMyNotes" text="%i18n:@show-renoted-my-notes%"/>
+ <mk-switch v-model="clientSettings.showMaps" @change="onChangeShowMaps" text="%i18n:@show-maps%">
+ <span>%i18n:@show-maps-desc%</span>
</mk-switch>
</section>
<section class="web" v-show="page == 'web'">
- <h1>サウンド</h1>
- <mk-switch v-model="enableSounds" text="サウンドを有効にする">
- <span>投稿やメッセージを送受信したときなどにサウンドを再生します。この設定はブラウザに記憶されます。</span>
+ <h1>%i18n:@sound%</h1>
+ <mk-switch v-model="enableSounds" text="%i18n:@enable-sounds%">
+ <span>%i18n:@enable-sounds-desc%</span>
</mk-switch>
- <label>ボリューム</label>
+ <label>%i18n:@volume%</label>
<el-slider
v-model="soundVolume"
:show-input="true"
:format-tooltip="v => `${v}%`"
:disabled="!enableSounds"
/>
- <button class="ui button" @click="soundTest">%fa:volume-up% テスト</button>
+ <button class="ui button" @click="soundTest">%fa:volume-up% %i18n:@test%</button>
</section>
<section class="web" v-show="page == 'web'">
- <h1>モバイル</h1>
- <mk-switch v-model="clientSettings.disableViaMobile" @change="onChangeDisableViaMobile" text="「モバイルからの投稿」フラグを付けない"/>
+ <h1>%i18n:@mobile%</h1>
+ <mk-switch v-model="clientSettings.disableViaMobile" @change="onChangeDisableViaMobile" text="%i18n:@disable-via-mobile%"/>
</section>
<section class="web" v-show="page == 'web'">
- <h1>言語</h1>
- <el-select v-model="lang" placeholder="言語を選択">
- <el-option-group label="推奨">
- <el-option label="自動" value=""/>
+ <h1>%i18n:@language%</h1>
+ <el-select v-model="lang" placeholder="%i18n:@pick-language%">
+ <el-option-group label="%i18n:@recommended%">
+ <el-option label="%i18n:@auto%" value=""/>
</el-option-group>
- <el-option-group label="言語を指定">
- <el-option label="ja" value="ja"/>
- <el-option label="en" value="en"/>
- <el-option label="fr" value="fr"/>
- <el-option label="pl" value="pl"/>
- <el-option label="de" value="de"/>
+ <el-option-group label="%i18n:@specify-language%">
+ <el-option label="日本語" value="ja"/>
+ <el-option label="English" value="en"/>
+ <el-option label="Français" value="fr"/>
+ <el-option label="Polski" value="pl"/>
+ <el-option label="Deutsch" value="de"/>
</el-option-group>
</el-select>
<div class="none ui info">
- <p>%fa:info-circle%変更はページの再度読み込み後に反映されます。</p>
+ <p>%fa:info-circle%%i18n:@language-desc%</p>
</div>
</section>
<section class="web" v-show="page == 'web'">
- <h1>キャッシュ</h1>
- <button class="ui button" @click="clean">クリーンアップ</button>
+ <h1>%i18n:@cache%</h1>
+ <button class="ui button" @click="clean">%i18n:@clean-cache%</button>
<div class="none ui info warn">
- <p>%fa:exclamation-triangle%クリーンアップを行うと、ブラウザに記憶されたアカウント情報のキャッシュ、書きかけの投稿・返信・メッセージ、およびその他のデータ(設定情報含む)が削除されます。クリーンアップを行った後はページを再度読み込みする必要があります。</p>
+ <p>%fa:exclamation-triangle%%i18n:@cache-warn%</p>
</div>
</section>
<section class="notification" v-show="page == 'notification'">
<h1>%i18n:@notification%</h1>
- <mk-switch v-model="os.i.settings.autoWatch" @change="onChangeAutoWatch" text="投稿の自動ウォッチ">
- <span>リアクションしたり返信したりした投稿に関する通知を自動的に受け取るようにします。</span>
+ <mk-switch v-model="os.i.settings.autoWatch" @change="onChangeAutoWatch" text="%i18n:@auto-watch%">
+ <span>%i18n:@auto-watch-desc%</span>
</mk-switch>
</section>
@@ -148,57 +148,57 @@
</section>
<section class="other" v-show="page == 'other'">
- <h1>Misskeyについて</h1>
- <p v-if="meta">このサーバーの運営者: <i><a :href="meta.maintainer.url" target="_blank">{{ meta.maintainer.name }}</a></i></p>
+ <h1>%i18n:@about%</h1>
+ <p v-if="meta">%i18n:@operator%: <i><a :href="meta.maintainer.url" target="_blank">{{ meta.maintainer.name }}</a></i></p>
</section>
<section class="other" v-show="page == 'other'">
- <h1>Misskey Update</h1>
+ <h1>%i18n:@update%</h1>
<p>
- <span>バージョン: <i>{{ version }}</i></span>
+ <span>%i18n:@version% <i>{{ version }}</i></span>
<template v-if="latestVersion !== undefined">
<br>
- <span>最新のバージョン: <i>{{ latestVersion ? latestVersion : version }}</i></span>
+ <span>%i18n:@latest-version% <i>{{ latestVersion ? latestVersion : version }}</i></span>
</template>
</p>
<button class="ui button block" @click="checkForUpdate" :disabled="checkingForUpdate">
- <template v-if="checkingForUpdate">アップデートを確認中<mk-ellipsis/></template>
- <template v-else>アップデートを確認</template>
+ <template v-if="checkingForUpdate">%i18n:@update-checking%<mk-ellipsis/></template>
+ <template v-else>%i18n:@do-update%</template>
</button>
<details>
- <summary>詳細設定</summary>
- <mk-switch v-model="preventUpdate" text="アップデートを延期する(非推奨)">
- <span>この設定をオンにしてもアップデートが反映される場合があります。この設定はこのデバイスのみ有効です。</span>
+ <summary>%i18n:@update-settings%</summary>
+ <mk-switch v-model="preventUpdate" text="%i18n:@prevent-update%">
+ <span>%i18n:@prevent-update-desc%</span>
</mk-switch>
</details>
</section>
<section class="other" v-show="page == 'other'">
- <h1>高度な設定</h1>
- <mk-switch v-model="debug" text="デバッグモードを有効にする">
- <span>この設定はブラウザに記憶されます。</span>
+ <h1>%i18n:@advanced-settings%</h1>
+ <mk-switch v-model="debug" text="%i18n:@debug-mode%">
+ <span>%i18n:@debug-mode-desc%</span>
</mk-switch>
<template v-if="debug">
- <mk-switch v-model="useRawScript" text="生のスクリプトを読み込む">
- <span>圧縮されていない「生の」スクリプトを使用します。サイズが大きいため、読み込みに時間がかかる場合があります。この設定はブラウザに記憶されます。</span>
+ <mk-switch v-model="useRawScript" text="%i18n:@use-raw-script%">
+ <span>%i18n:@use-raw-script-desc%</span>
</mk-switch>
<div class="none ui info">
- <p>%fa:info-circle%Misskeyはソースマップも提供しています。</p>
+ <p>%fa:info-circle%%i18n:@source-info%</p>
</div>
</template>
- <mk-switch v-model="enableExperimental" text="実験的機能を有効にする">
- <span>実験的機能を有効にするとMisskeyの動作が不安定になる可能性があります。この設定はブラウザに記憶されます。</span>
+ <mk-switch v-model="enableExperimental" text="%i18n:@experimental%">
+ <span>%i18n:@experimental-desc%</span>
</mk-switch>
<details v-if="debug">
- <summary>ツール</summary>
- <button class="ui button block" @click="taskmngr">タスクマネージャ</button>
+ <summary>%i18n:@tools%</summary>
+ <button class="ui button block" @click="taskmngr">%i18n:@task-manager%</button>
</details>
</section>
<section class="other" v-show="page == 'other'">
<h1>%i18n:@license%</h1>
<div v-html="license"></div>
- <a :href="licenseUrl" target="_blank">サードパーティ</a>
+ <a :href="licenseUrl" target="_blank">%i18n:@third-parties%</a>
</section>
</div>
</div>
@@ -371,13 +371,13 @@ export default Vue.extend({
this.latestVersion = newer;
if (newer == null) {
(this as any).apis.dialog({
- title: '利用可能な更新はありません',
- text: 'お使いのMisskeyは最新です。'
+ title: '%i18n:!@no-updates%',
+ text: '%i18n:!@no-updates-desc%'
});
} else {
(this as any).apis.dialog({
- title: '新しいバージョンが利用可能です',
- text: 'ページを再度読み込みすると更新が適用されます。'
+ title: '%i18n:!@update-available%',
+ text: '%i18n:!@update-available-desc%'
});
}
});
@@ -385,8 +385,8 @@ export default Vue.extend({
clean() {
localStorage.clear();
(this as any).apis.dialog({
- title: 'キャッシュを削除しました',
- text: 'ページを再度読み込みしてください。'
+ title: '%i18n:!@cache-cleared%',
+ text: '%i18n:!@caache-cleared-desc%'
});
},
soundTest() {
diff --git a/src/client/app/desktop/views/components/taskmanager.vue b/src/client/app/desktop/views/components/taskmanager.vue
index a00fabb047..1f1385add8 100644
--- a/src/client/app/desktop/views/components/taskmanager.vue
+++ b/src/client/app/desktop/views/components/taskmanager.vue
@@ -1,6 +1,6 @@
<template>
<mk-window ref="window" width="750px" height="500px" @closed="$destroy" name="TaskManager">
- <span slot="header" :class="$style.header">%fa:stethoscope%タスクマネージャ</span>
+ <span slot="header" :class="$style.header">%fa:stethoscope%%i18n:@title%</span>
<el-tabs :class="$style.content">
<el-tab-pane label="Requests">
<el-table
diff --git a/src/client/app/desktop/views/components/user-preview.vue b/src/client/app/desktop/views/components/user-preview.vue
index f40e60dff9..b8854a8032 100644
--- a/src/client/app/desktop/views/components/user-preview.vue
+++ b/src/client/app/desktop/views/components/user-preview.vue
@@ -5,7 +5,7 @@
<mk-avatar class="avatar" :user="u" :disable-preview="true"/>
<div class="title">
<router-link class="name" :to="u | userPage">{{ u | userName }}</router-link>
- <p class="username">@{{ u | acct }}</p>
+ <p class="username"><mk-acct :user="u"/></p>
</div>
<div class="description">{{ u.description }}</div>
<div class="status">
diff --git a/src/client/app/desktop/views/pages/user/user.header.vue b/src/client/app/desktop/views/pages/user/user.header.vue
index edb248dac7..73af3a4a4c 100644
--- a/src/client/app/desktop/views/pages/user/user.header.vue
+++ b/src/client/app/desktop/views/pages/user/user.header.vue
@@ -10,7 +10,7 @@
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
<div class="title">
<p class="name">{{ user | userName }}</p>
- <p class="username">@{{ user | acct }}</p>
+ <p class="username"><mk-acct :user="user"/></p>
<p class="location" v-if="user.host === null && user.profile.location">%fa:map-marker%{{ user.profile.location }}</p>
</div>
<footer>
diff --git a/src/client/app/desktop/views/widgets/channel.channel.form.vue b/src/client/app/desktop/views/widgets/channel.channel.form.vue
deleted file mode 100644
index f2744268bb..0000000000
--- a/src/client/app/desktop/views/widgets/channel.channel.form.vue
+++ /dev/null
@@ -1,67 +0,0 @@
-<template>
-<div class="form">
- <input v-model="text" :disabled="wait" @keydown="onKeydown" placeholder="書いて">
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-export default Vue.extend({
- data() {
- return {
- text: '',
- wait: false
- };
- },
- methods: {
- onKeydown(e) {
- if (e.which == 10 || e.which == 13) this.post();
- },
- post() {
- this.wait = true;
-
- let reply = null;
-
- if (/^>>([0-9]+) /.test(this.text)) {
- const index = this.text.match(/^>>([0-9]+) /)[1];
- reply = (this.$parent as any).notes.find(p => p.index.toString() == index);
- this.text = this.text.replace(/^>>([0-9]+) /, '');
- }
-
- (this as any).api('notes/create', {
- text: this.text,
- replyId: reply ? reply.id : undefined,
- channelId: (this.$parent as any).channel.id
- }).then(data => {
- this.text = '';
- }).catch(err => {
- alert('失敗した');
- }).then(() => {
- this.wait = false;
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.form
- width 100%
- height 38px
- padding 4px
- border-top solid 1px #ddd
-
- > input
- padding 0 8px
- width 100%
- height 100%
- font-size 14px
- color #55595c
- border solid 1px #dadada
- border-radius 4px
-
- &:hover
- &:focus
- border-color #aeaeae
-
-</style>
diff --git a/src/client/app/desktop/views/widgets/channel.channel.note.vue b/src/client/app/desktop/views/widgets/channel.channel.note.vue
deleted file mode 100644
index 7767919066..0000000000
--- a/src/client/app/desktop/views/widgets/channel.channel.note.vue
+++ /dev/null
@@ -1,65 +0,0 @@
-<template>
-<div class="note">
- <header>
- <a class="index" @click="reply">{{ note.index }}:</a>
- <router-link class="name" :to="note.user | userPage" v-user-preview="note.user.id"><b>{{ note.user | userName }}</b></router-link>
- <span>ID:<i>{{ note.user | acct }}</i></span>
- </header>
- <div>
- <a v-if="note.reply">&gt;&gt;{{ note.reply.index }}</a>
- {{ note.text }}
- <div class="media" v-if="note.media">
- <a v-for="file in note.media" :href="file.url" target="_blank">
- <img :src="`${file.url}?thumbnail&size=512`" :alt="file.name" :title="file.name"/>
- </a>
- </div>
- </div>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-
-export default Vue.extend({
- props: ['note'],
- methods: {
- reply() {
- this.$emit('reply', this.note);
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.note
- margin 0
- padding 0
- color #444
-
- > header
- position -webkit-sticky
- position sticky
- z-index 1
- top 0
- padding 8px 4px 4px 16px
- background rgba(255, 255, 255, 0.9)
-
- > .index
- margin-right 0.25em
-
- > .name
- margin-right 0.5em
- color #008000
-
- > div
- padding 0 16px 16px 16px
-
- > .media
- > a
- display inline-block
-
- > img
- max-width 100%
- vertical-align bottom
-
-</style>
diff --git a/src/client/app/desktop/views/widgets/channel.channel.vue b/src/client/app/desktop/views/widgets/channel.channel.vue
deleted file mode 100644
index ea4d8f8454..0000000000
--- a/src/client/app/desktop/views/widgets/channel.channel.vue
+++ /dev/null
@@ -1,106 +0,0 @@
-<template>
-<div class="channel">
- <p v-if="fetching">読み込み中<mk-ellipsis/></p>
- <div v-if="!fetching" ref="notes" class="notes">
- <p v-if="notes.length == 0">まだ投稿がありません</p>
- <x-note class="note" v-for="note in notes.slice().reverse()" :note="note" :key="note.id" @reply="reply"/>
- </div>
- <x-form class="form" ref="form"/>
-</div>
-</template>
-
-<script lang="ts">
-import Vue from 'vue';
-import ChannelStream from '../../../common/scripts/streaming/channel';
-import XForm from './channel.channel.form.vue';
-import XNote from './channel.channel.note.vue';
-
-export default Vue.extend({
- components: {
- XForm,
- XNote
- },
- props: ['channel'],
- data() {
- return {
- fetching: true,
- notes: [],
- connection: null
- };
- },
- watch: {
- channel() {
- this.zap();
- }
- },
- mounted() {
- this.zap();
- },
- beforeDestroy() {
- this.disconnect();
- },
- methods: {
- zap() {
- this.fetching = true;
-
- (this as any).api('channels/notes', {
- channelId: this.channel.id
- }).then(notes => {
- this.notes = notes;
- this.fetching = false;
-
- this.$nextTick(() => {
- this.scrollToBottom();
- });
-
- this.disconnect();
- this.connection = new ChannelStream((this as any).os, this.channel.id);
- this.connection.on('note', this.onNote);
- });
- },
- disconnect() {
- if (this.connection) {
- this.connection.off('note', this.onNote);
- this.connection.close();
- }
- },
- onNote(note) {
- this.notes.unshift(note);
- this.scrollToBottom();
- },
- scrollToBottom() {
- (this.$refs.notes as any).scrollTop = (this.$refs.notes as any).scrollHeight;
- },
- reply(note) {
- (this.$refs.form as any).text = `>>${ note.index } `;
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.channel
-
- > p
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > .notes
- height calc(100% - 38px)
- overflow auto
- font-size 0.9em
-
- > .note
- border-bottom solid 1px #eee
-
- &:last-child
- border-bottom none
-
- > .form
- position absolute
- left 0
- bottom 0
-
-</style>
diff --git a/src/client/app/desktop/views/widgets/channel.vue b/src/client/app/desktop/views/widgets/channel.vue
deleted file mode 100644
index d21aed40fd..0000000000
--- a/src/client/app/desktop/views/widgets/channel.vue
+++ /dev/null
@@ -1,108 +0,0 @@
-<template>
-<div class="mkw-channel">
- <template v-if="!props.compact">
- <p class="title">%fa:tv%{{ channel ? channel.title : '%i18n:!@title%' }}</p>
- <button @click="settings" title="%i18n:@settings%">%fa:cog%</button>
- </template>
- <p class="get-started" v-if="props.channel == null">%i18n:@get-started%</p>
- <x-channel class="channel" :channel="channel" v-if="channel != null"/>
-</div>
-</template>
-
-<script lang="ts">
-import define from '../../../common/define-widget';
-import XChannel from './channel.channel.vue';
-
-export default define({
- name: 'server',
- props: () => ({
- channel: null,
- compact: false
- })
-}).extend({
- components: {
- XChannel
- },
- data() {
- return {
- fetching: true,
- channel: null
- };
- },
- mounted() {
- if (this.props.channel) {
- this.zap();
- }
- },
- methods: {
- func() {
- this.props.compact = !this.props.compact;
- this.save();
- },
- settings() {
- const id = window.prompt('チャンネルID');
- if (!id) return;
- this.props.channel = id;
- this.zap();
- },
- zap() {
- this.fetching = true;
-
- (this as any).api('channels/show', {
- channelId: this.props.channel
- }).then(channel => {
- this.channel = channel;
- this.fetching = false;
- });
- }
- }
-});
-</script>
-
-<style lang="stylus" scoped>
-.mkw-channel
- background #fff
- border solid 1px rgba(#000, 0.075)
- border-radius 6px
- overflow hidden
-
- > .title
- z-index 2
- margin 0
- padding 0 16px
- line-height 42px
- font-size 0.9em
- font-weight bold
- color #888
- box-shadow 0 1px rgba(#000, 0.07)
-
- > [data-fa]
- margin-right 4px
-
- > button
- position absolute
- z-index 2
- top 0
- right 0
- padding 0
- width 42px
- font-size 0.9em
- line-height 42px
- color #ccc
-
- &:hover
- color #aaa
-
- &:active
- color #999
-
- > .get-started
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > .channel
- height 200px
-
-</style>
diff --git a/src/client/app/desktop/views/widgets/index.ts b/src/client/app/desktop/views/widgets/index.ts
index 77d771d6b3..7c074080c1 100644
--- a/src/client/app/desktop/views/widgets/index.ts
+++ b/src/client/app/desktop/views/widgets/index.ts
@@ -8,7 +8,6 @@ import wUsers from './users.vue';
import wPolls from './polls.vue';
import wPostForm from './post-form.vue';
import wMessaging from './messaging.vue';
-import wChannel from './channel.vue';
import wProfile from './profile.vue';
Vue.component('mkw-notifications', wNotifications);
@@ -19,5 +18,4 @@ Vue.component('mkw-users', wUsers);
Vue.component('mkw-polls', wPolls);
Vue.component('mkw-post-form', wPostForm);
Vue.component('mkw-messaging', wMessaging);
-Vue.component('mkw-channel', wChannel);
Vue.component('mkw-profile', wProfile);
diff --git a/src/client/app/mobile/script.ts b/src/client/app/mobile/script.ts
index 1405139be6..427c177a14 100644
--- a/src/client/app/mobile/script.ts
+++ b/src/client/app/mobile/script.ts
@@ -2,11 +2,17 @@
* Mobile Client
*/
+import Vue from 'vue';
import VueRouter from 'vue-router';
+import { MdCard, MdButton, MdField, MdMenu, MdList, MdSwitch } from 'vue-material/dist/components';
+import 'vue-material/dist/vue-material.min.css';
+import 'vue-material/dist/theme/default.css';
+
// Style
import './style.styl';
import '../../element.scss';
+import '../../md.scss';
import init from '../init';
@@ -34,6 +40,13 @@ import MkSettings from './views/pages/settings.vue';
import MkProfileSetting from './views/pages/profile-setting.vue';
import MkOthello from './views/pages/othello.vue';
+Vue.use(MdCard);
+Vue.use(MdButton);
+Vue.use(MdField);
+Vue.use(MdMenu);
+Vue.use(MdList);
+Vue.use(MdSwitch);
+
/**
* init
*/
diff --git a/src/client/app/mobile/style.styl b/src/client/app/mobile/style.styl
index 847ae8eec5..d1ab044eaf 100644
--- a/src/client/app/mobile/style.styl
+++ b/src/client/app/mobile/style.styl
@@ -8,10 +8,13 @@
html
height 100%
- background #ececed
+ background #ececed !important
+
+ // for md
+ transition none !important
&[data-darkmode]
- background #191B22
+ background #191B22 !important
body
display flex
diff --git a/src/client/app/mobile/views/components/note-detail.sub.vue b/src/client/app/mobile/views/components/note-detail.sub.vue
index e515fda8a6..3ad5af1719 100644
--- a/src/client/app/mobile/views/components/note-detail.sub.vue
+++ b/src/client/app/mobile/views/components/note-detail.sub.vue
@@ -4,7 +4,7 @@
<div class="main">
<header>
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
+ <span class="username"><mk-acct :user="note.user"/></span>
<router-link class="time" :to="note | notePage">
<mk-time :time="note.createdAt"/>
</router-link>
diff --git a/src/client/app/mobile/views/components/note-detail.vue b/src/client/app/mobile/views/components/note-detail.vue
index 5a7226faac..c6664a91da 100644
--- a/src/client/app/mobile/views/components/note-detail.vue
+++ b/src/client/app/mobile/views/components/note-detail.vue
@@ -25,7 +25,7 @@
<mk-avatar class="avatar" :user="p.user"/>
<div>
<router-link class="name" :to="p.user | userPage">{{ p.user | userName }}</router-link>
- <span class="username">@{{ p.user | acct }}</span>
+ <span class="username"><mk-acct :user="p.user"/></span>
</div>
</header>
<div class="body">
diff --git a/src/client/app/mobile/views/components/note-preview.vue b/src/client/app/mobile/views/components/note-preview.vue
index b3ab088ffe..b55cad792d 100644
--- a/src/client/app/mobile/views/components/note-preview.vue
+++ b/src/client/app/mobile/views/components/note-preview.vue
@@ -4,7 +4,7 @@
<div class="main">
<header>
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
+ <span class="username"><mk-acct :user="note.user"/></span>
<router-link class="time" :to="note | notePage">
<mk-time :time="note.createdAt"/>
</router-link>
diff --git a/src/client/app/mobile/views/components/note.sub.vue b/src/client/app/mobile/views/components/note.sub.vue
index 82025291da..2fb3b2ffcc 100644
--- a/src/client/app/mobile/views/components/note.sub.vue
+++ b/src/client/app/mobile/views/components/note.sub.vue
@@ -4,7 +4,7 @@
<div class="main">
<header>
<router-link class="name" :to="note.user | userPage">{{ note.user | userName }}</router-link>
- <span class="username">@{{ note.user | acct }}</span>
+ <span class="username"><mk-acct :user="note.user"/></span>
<div class="info">
<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
<router-link class="created-at" :to="note | notePage">
diff --git a/src/client/app/mobile/views/components/note.vue b/src/client/app/mobile/views/components/note.vue
index 77a766f327..83a957cfbd 100644
--- a/src/client/app/mobile/views/components/note.vue
+++ b/src/client/app/mobile/views/components/note.vue
@@ -17,7 +17,7 @@
<header>
<router-link class="name" :to="p.user | userPage">{{ p.user | userName }}</router-link>
<span class="is-bot" v-if="p.user.host === null && p.user.isBot">bot</span>
- <span class="username">@{{ p.user | acct }}</span>
+ <span class="username"><mk-acct :user="p.user"/></span>
<div class="info">
<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
<router-link class="created-at" :to="p | notePage">
@@ -32,7 +32,6 @@
</div>
</header>
<div class="body">
- <p class="channel" v-if="p.channel != null"><a target="_blank">{{ p.channel.title }}</a>:</p>
<p v-if="p.cw != null" class="cw">
<span class="text" v-if="p.cw != ''">{{ p.cw }}</span>
<span class="toggle" @click="showContent = !showContent">{{ showContent ? '隠す' : 'もっと見る' }}</span>
@@ -470,9 +469,6 @@ root(isDark)
.mk-url-preview
margin-top 8px
- > .channel
- margin 0
-
> .tags
margin 4px 0 0 0
diff --git a/src/client/app/mobile/views/components/user-card.vue b/src/client/app/mobile/views/components/user-card.vue
index 432560a54a..52c82115bf 100644
--- a/src/client/app/mobile/views/components/user-card.vue
+++ b/src/client/app/mobile/views/components/user-card.vue
@@ -6,7 +6,7 @@
</a>
</header>
<a class="name" :href="user | userPage" target="_blank">{{ user | userName }}</a>
- <p class="username">@{{ user | acct }}</p>
+ <p class="username"><mk-acct :user="user"/></p>
<mk-follow-button :user="user"/>
</div>
</template>
diff --git a/src/client/app/mobile/views/components/user-preview.vue b/src/client/app/mobile/views/components/user-preview.vue
index d258360911..a165e66a9d 100644
--- a/src/client/app/mobile/views/components/user-preview.vue
+++ b/src/client/app/mobile/views/components/user-preview.vue
@@ -4,7 +4,7 @@
<div class="main">
<header>
<router-link class="name" :to="user | userPage">{{ user | userName }}</router-link>
- <span class="username">@{{ user | acct }}</span>
+ <span class="username"><mk-acct :user="user"/></span>
</header>
<div class="body">
<div class="description">{{ user.description }}</div>
diff --git a/src/client/app/mobile/views/pages/settings.vue b/src/client/app/mobile/views/pages/settings.vue
index 0e9c5ea962..b16860d62c 100644
--- a/src/client/app/mobile/views/pages/settings.vue
+++ b/src/client/app/mobile/views/pages/settings.vue
@@ -1,18 +1,29 @@
<template>
<mk-ui>
<span slot="header">%fa:cog%%i18n:@settings%</span>
- <div :class="$style.content">
+ <main>
<p v-html="'%i18n:!@signed-in-as%'.replace('{}', '<b>' + name + '</b>')"></p>
- <ul>
- <li><router-link to="./settings/profile">%fa:user%%i18n:@profile%%fa:angle-right%</router-link></li>
- <li><router-link to="./settings/twitter">%fa:B twitter%%i18n:@twitter%%fa:angle-right%</router-link></li>
- <li><router-link to="./settings/signin-history">%fa:sign-in-alt%%i18n:@signin-history%%fa:angle-right%</router-link></li>
- </ul>
- <ul>
- <li><a @click="signout">%fa:power-off%%i18n:@signout%</a></li>
- </ul>
+ <div>
+ <x-profile/>
+
+ <md-card class="md-layout-item md-size-50 md-small-size-100">
+ <md-card-header>
+ <div class="md-title">%i18n:@design%</div>
+ </md-card-header>
+
+ <md-card-content>
+ <div>
+ <md-switch v-model="darkmode">%i18n:@dark-mode%</md-switch>
+ </div>
+
+ <div>
+ <md-switch v-model="clientSettings.circleIcons" @change="onChangeCircleIcons">%i18n:@circle-icons%</md-switch>
+ </div>
+ </md-card-content>
+ </md-card>
+ </div>
<p><small>ver {{ version }} ({{ codename }})</small></p>
- </div>
+ </main>
</mk-ui>
</template>
@@ -20,31 +31,59 @@
import Vue from 'vue';
import { version, codename } from '../../../config';
+import XProfile from './settings/settings.profile.vue';
+
export default Vue.extend({
+ components: {
+ XProfile
+ },
+
data() {
return {
version,
- codename
+ codename,
+ darkmode: localStorage.getItem('darkmode') == 'true'
};
},
+
computed: {
name(): string {
return Vue.filter('userName')((this as any).os.i);
}
},
+
+ watch: {
+ darkmode() {
+ (this as any)._updateDarkmode_(this.darkmode);
+ }
+ },
+
mounted() {
document.title = 'Misskey | %i18n:@settings%';
},
+
methods: {
signout() {
(this as any).os.signout();
+ },
+
+ onChangeCircleIcons(v) {
+ this.$store.dispatch('settings/set', {
+ key: 'circleIcons',
+ value: v
+ });
}
}
});
</script>
-<style lang="stylus" module>
-.content
+<style lang="stylus" scoped>
+main
+ padding 0 16px
+
+ > div
+ > *
+ margin-bottom 16px
> p
display block
diff --git a/src/client/app/mobile/views/pages/settings/settings.profile.vue b/src/client/app/mobile/views/pages/settings/settings.profile.vue
new file mode 100644
index 0000000000..6b5d07cce9
--- /dev/null
+++ b/src/client/app/mobile/views/pages/settings/settings.profile.vue
@@ -0,0 +1,126 @@
+<template>
+ <md-card class="md-layout-item md-size-50 md-small-size-100">
+ <md-card-header>
+ <div class="md-title">%i18n:@title%</div>
+ </md-card-header>
+
+ <md-card-content>
+ <md-field>
+ <label>%i18n:@name%</label>
+ <md-input v-model="name" :disabled="saving"/>
+ </md-field>
+
+ <md-field>
+ <label>%i18n:@location%</label>
+ <md-input v-model="location" :disabled="saving"/>
+ </md-field>
+
+ <md-field>
+ <label>%i18n:@description%</label>
+ <md-textarea v-model="description" :disabled="saving"/>
+ </md-field>
+
+ <md-field>
+ <label>%i18n:@birthday%</label>
+ <md-input type="date" v-model="birthday" :disabled="saving"/>
+ </md-field>
+
+ <div>
+ <div class="md-body-2">%i18n:@avatar%</div>
+ <md-menu md-direction="bottom-end" :md-close-on-select="true">
+ <md-button md-menu-trigger>%i18n:@set-avatar%</md-button>
+ <md-menu-content>
+ <md-menu-item @click="uploadAvatar">%i18n:@upload-avatar%</md-menu-item>
+ <md-menu-item @click="chooseAvatar">%i18n:@choose-avatar%</md-menu-item>
+ </md-menu-content>
+ </md-menu>
+ </div>
+
+ <div>
+ <div class="md-body-2">%i18n:@banner%</div>
+ <md-menu md-direction="bottom-end" :md-close-on-select="true">
+ <md-button md-menu-trigger>%i18n:@set-banner%</md-button>
+ <md-menu-content>
+ <md-menu-item @click="uploadAvatar">%i18n:@upload-banner%</md-menu-item>
+ <md-menu-item @click="chooseAvatar">%i18n:@choose-banner%</md-menu-item>
+ </md-menu-content>
+ </md-menu>
+ </div>
+ </md-card-content>
+
+ <md-card-actions>
+ <md-button class="md-primary" :disabled="saving" @click="save">%i18n:@save%</md-button>
+ </md-card-actions>
+ </md-card>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+
+export default Vue.extend({
+ data() {
+ return {
+ name: null,
+ location: null,
+ description: null,
+ birthday: null,
+ saving: false
+ };
+ },
+ created() {
+ this.name = (this as any).os.i.name || '';
+ this.location = (this as any).os.i.profile.location;
+ this.description = (this as any).os.i.description;
+ this.birthday = (this as any).os.i.profile.birthday;
+ },
+ methods: {
+ chooseAvatar() {
+ (this as any).apis.chooseDriveFile({
+ multiple: false
+ }).then(file => {
+ this.avatarSaving = true;
+
+ (this as any).api('i/update', {
+ avatarId: file.id
+ }).then(() => {
+ this.avatarSaving = false;
+ alert('%i18n:!@avatar-saved%');
+ });
+ });
+ },
+ chooseBanner() {
+ (this as any).apis.chooseDriveFile({
+ multiple: false
+ }).then(file => {
+ this.bannerSaving = true;
+
+ (this as any).api('i/update', {
+ bannerId: file.id
+ }).then(() => {
+ this.bannerSaving = false;
+ alert('%i18n:!@banner-saved%');
+ });
+ });
+ },
+ uploadAvatar() {
+ // a
+ },
+ uploadBanner() {
+ // a
+ },
+ save() {
+ this.saving = true;
+
+ (this as any).api('i/update', {
+ name: this.name || null,
+ location: this.location || null,
+ description: this.description || null,
+ birthday: this.birthday || null
+ }).then(() => {
+ this.saving = false;
+ alert('%i18n:!@saved%');
+ });
+ }
+ }
+});
+</script>
diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue
index f43454f9db..34adeb03cd 100644
--- a/src/client/app/mobile/views/pages/user.vue
+++ b/src/client/app/mobile/views/pages/user.vue
@@ -15,7 +15,7 @@
</div>
<div class="title">
<h1>{{ user | userName }}</h1>
- <span class="username">@{{ user | acct }}</span>
+ <span class="username"><mk-acct :user="user"/></span>
<span class="followed" v-if="user.isFollowed">%i18n:@follows-you%</span>
</div>
<div class="description">{{ user.description }}</div>
diff --git a/src/client/app/mobile/views/pages/widgets.vue b/src/client/app/mobile/views/pages/widgets.vue
index 509ce16eef..f0a0877862 100644
--- a/src/client/app/mobile/views/pages/widgets.vue
+++ b/src/client/app/mobile/views/pages/widgets.vue
@@ -15,7 +15,6 @@
<option value="photo-stream">フォトストリーム</option>
<option value="slideshow">スライドショー</option>
<option value="version">バージョン</option>
- <option value="access-log">アクセスログ</option>
<option value="server">サーバー情報</option>
<option value="donation">寄付のお願い</option>
<option value="nav">ナビゲーション</option>
diff --git a/src/client/md.scss b/src/client/md.scss
new file mode 100644
index 0000000000..d850863efd
--- /dev/null
+++ b/src/client/md.scss
@@ -0,0 +1,13 @@
+/* SEE: https://vuematerial.io/themes/configuration */
+
+@import '../const.json';
+
+@import "~vue-material/dist/theme/engine";
+
+@include md-register-theme("default", (
+ primary: $themeColor,
+ accent: md-get-palette-color(red, A200)
+));
+
+@import "~vue-material/dist/components/MdButton/theme";
+@import "~vue-material/dist/components/MdField/theme";
diff --git a/src/models/channel-watching.ts b/src/models/channel-watching.ts
deleted file mode 100644
index 44ca06883f..0000000000
--- a/src/models/channel-watching.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import * as mongo from 'mongodb';
-import db from '../db/mongodb';
-
-const ChannelWatching = db.get<IChannelWatching>('channelWatching');
-export default ChannelWatching;
-
-export interface IChannelWatching {
- _id: mongo.ObjectID;
- createdAt: Date;
- deletedAt: Date;
- channelId: mongo.ObjectID;
- userId: mongo.ObjectID;
-}
diff --git a/src/models/channel.ts b/src/models/channel.ts
deleted file mode 100644
index 67386ac072..0000000000
--- a/src/models/channel.ts
+++ /dev/null
@@ -1,75 +0,0 @@
-import * as mongo from 'mongodb';
-import deepcopy = require('deepcopy');
-import { IUser } from './user';
-import Watching from './channel-watching';
-import db from '../db/mongodb';
-
-const Channel = db.get<IChannel>('channels');
-export default Channel;
-
-export type IChannel = {
- _id: mongo.ObjectID;
- createdAt: Date;
- title: string;
- userId: mongo.ObjectID;
- index: number;
- watchingCount: number;
-};
-
-/**
- * Pack a channel for API response
- *
- * @param channel target
- * @param me? serializee
- * @return response
- */
-export const pack = (
- channel: string | mongo.ObjectID | IChannel,
- me?: string | mongo.ObjectID | IUser
-) => new Promise<any>(async (resolve, reject) => {
-
- let _channel: any;
-
- // Populate the channel if 'channel' is ID
- if (mongo.ObjectID.prototype.isPrototypeOf(channel)) {
- _channel = await Channel.findOne({
- _id: channel
- });
- } else if (typeof channel === 'string') {
- _channel = await Channel.findOne({
- _id: new mongo.ObjectID(channel)
- });
- } else {
- _channel = deepcopy(channel);
- }
-
- // Rename _id to id
- _channel.id = _channel._id;
- delete _channel._id;
-
- // Remove needless properties
- delete _channel.userId;
-
- // Me
- const meId: mongo.ObjectID = me
- ? mongo.ObjectID.prototype.isPrototypeOf(me)
- ? me as mongo.ObjectID
- : typeof me === 'string'
- ? new mongo.ObjectID(me)
- : (me as IUser)._id
- : null;
-
- if (me) {
- //#region Watchしているかどうか
- const watch = await Watching.findOne({
- userId: meId,
- channelId: _channel.id,
- deletedAt: { $exists: false }
- });
-
- _channel.isWatching = watch !== null;
- //#endregion
- }
-
- resolve(_channel);
-});
diff --git a/src/models/note.ts b/src/models/note.ts
index f42bb2a49d..5070923363 100644
--- a/src/models/note.ts
+++ b/src/models/note.ts
@@ -4,7 +4,6 @@ import rap from '@prezzemolo/rap';
import db from '../db/mongodb';
import { IUser, pack as packUser } from './user';
import { pack as packApp } from './app';
-import { pack as packChannel } from './channel';
import PollVote, { deletePollVote } from './poll-vote';
import Reaction, { deleteNoteReaction } from './note-reaction';
import { pack as packFile } from './drive-file';
@@ -29,7 +28,6 @@ export function isValidCw(text: string): boolean {
export type INote = {
_id: mongo.ObjectID;
- channelId: mongo.ObjectID;
createdAt: Date;
deletedAt: Date;
mediaIds: mongo.ObjectID[];
@@ -258,11 +256,6 @@ export const pack = async (
_note.app = packApp(_note.appId);
}
- // Populate channel
- if (_note.channelId) {
- _note.channel = packChannel(_note.channelId);
- }
-
// Populate media
_note.media = hide ? [] : Promise.all(_note.mediaIds.map(fileId =>
packFile(fileId)
diff --git a/src/publishers/stream.ts b/src/publishers/stream.ts
index dcc03e39f1..58a6ef49aa 100644
--- a/src/publishers/stream.ts
+++ b/src/publishers/stream.ts
@@ -45,10 +45,6 @@ class MisskeyEvent {
this.publish(`othello-game-stream:${gameId}`, type, typeof value === 'undefined' ? null : value);
}
- public publishChannelStream(channelId: ID, type: string, value?: any): void {
- this.publish(`channel-stream:${channelId}`, type, typeof value === 'undefined' ? null : value);
- }
-
public publishLocalTimelineStream(note: any): void {
this.redisClient.publish('misskey:local-timeline', JSON.stringify(note));
}
@@ -79,4 +75,3 @@ export const publishMessagingStream = ev.publishMessagingStream.bind(ev);
export const publishMessagingIndexStream = ev.publishMessagingIndexStream.bind(ev);
export const publishOthelloStream = ev.publishOthelloStream.bind(ev);
export const publishOthelloGameStream = ev.publishOthelloGameStream.bind(ev);
-export const publishChannelStream = ev.publishChannelStream.bind(ev);
diff --git a/src/renderers/get-note-summary.ts b/src/renderers/get-note-summary.ts
index 0844c0b184..643e2d09ba 100644
--- a/src/renderers/get-note-summary.ts
+++ b/src/renderers/get-note-summary.ts
@@ -9,9 +9,6 @@ const summarize = (note: any): string => {
let summary = '';
- // チャンネル
- summary += note.channel ? `${note.channel.title}:` : '';
-
// 本文
summary += note.text ? note.text : '';
diff --git a/src/server/api/endpoints.ts b/src/server/api/endpoints.ts
index 734b8273f1..7647c76d3d 100644
--- a/src/server/api/endpoints.ts
+++ b/src/server/api/endpoints.ts
@@ -621,33 +621,7 @@ const endpoints: Endpoint[] = [
name: 'messaging/messages/create',
withCredential: true,
kind: 'messaging-write'
- },
- {
- name: 'channels/create',
- withCredential: true,
- limit: {
- duration: ms('1hour'),
- max: 3,
- minInterval: ms('10seconds')
- }
- },
- {
- name: 'channels/show'
- },
- {
- name: 'channels/notes'
- },
- {
- name: 'channels/watch',
- withCredential: true
- },
- {
- name: 'channels/unwatch',
- withCredential: true
- },
- {
- name: 'channels'
- },
+ }
];
export default endpoints;
diff --git a/src/server/api/endpoints/channels.ts b/src/server/api/endpoints/channels.ts
deleted file mode 100644
index ceef4b9cb9..0000000000
--- a/src/server/api/endpoints/channels.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/**
- * Module dependencies
- */
-import $ from 'cafy'; import ID from '../../../cafy-id';
-import Channel, { pack } from '../../../models/channel';
-
-/**
- * Get all channels
- *
- * @param {any} params
- * @param {any} me
- * @return {Promise<any>}
- */
-module.exports = (params, me) => new Promise(async (res, rej) => {
- // Get 'limit' parameter
- const [limit = 10, limitErr] = $.num.optional().range(1, 100).get(params.limit);
- if (limitErr) return rej('invalid limit param');
-
- // Get 'sinceId' parameter
- const [sinceId, sinceIdErr] = $.type(ID).optional().get(params.sinceId);
- if (sinceIdErr) return rej('invalid sinceId param');
-
- // Get 'untilId' parameter
- const [untilId, untilIdErr] = $.type(ID).optional().get(params.untilId);
- if (untilIdErr) return rej('invalid untilId param');
-
- // Check if both of sinceId and untilId is specified
- if (sinceId && untilId) {
- return rej('cannot set sinceId and untilId');
- }
-
- // Construct query
- const sort = {
- _id: -1
- };
- const query = {} as any;
- if (sinceId) {
- sort._id = 1;
- query._id = {
- $gt: sinceId
- };
- } else if (untilId) {
- query._id = {
- $lt: untilId
- };
- }
-
- // Issue query
- const channels = await Channel
- .find(query, {
- limit: limit,
- sort: sort
- });
-
- // Serialize
- res(await Promise.all(channels.map(async channel =>
- await pack(channel, me))));
-});
diff --git a/src/server/api/endpoints/channels/create.ts b/src/server/api/endpoints/channels/create.ts
deleted file mode 100644
index 0e3c9dc5ac..0000000000
--- a/src/server/api/endpoints/channels/create.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-/**
- * Module dependencies
- */
-import $ from 'cafy';
-import Channel from '../../../../models/channel';
-import Watching from '../../../../models/channel-watching';
-import { pack } from '../../../../models/channel';
-
-/**
- * Create a channel
- */
-module.exports = async (params, user) => new Promise(async (res, rej) => {
- // Get 'title' parameter
- const [title, titleErr] = $.str.range(1, 100).get(params.title);
- if (titleErr) return rej('invalid title param');
-
- // Create a channel
- const channel = await Channel.insert({
- createdAt: new Date(),
- userId: user._id,
- title: title,
- index: 0,
- watchingCount: 1
- });
-
- // Response
- res(await pack(channel));
-
- // Create Watching
- await Watching.insert({
- createdAt: new Date(),
- userId: user._id,
- channelId: channel._id
- });
-});
diff --git a/src/server/api/endpoints/channels/notes.ts b/src/server/api/endpoints/channels/notes.ts
deleted file mode 100644
index 463152e74a..0000000000
--- a/src/server/api/endpoints/channels/notes.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- * Module dependencies
- */
-import $ from 'cafy'; import ID from '../../../../cafy-id';
-import { default as Channel, IChannel } from '../../../../models/channel';
-import Note, { pack } from '../../../../models/note';
-
-/**
- * Show a notes of a channel
- */
-module.exports = (params, user) => new Promise(async (res, rej) => {
- // Get 'limit' parameter
- const [limit = 1000, limitErr] = $.num.optional().range(1, 1000).get(params.limit);
- if (limitErr) return rej('invalid limit param');
-
- // Get 'sinceId' parameter
- const [sinceId, sinceIdErr] = $.type(ID).optional().get(params.sinceId);
- if (sinceIdErr) return rej('invalid sinceId param');
-
- // Get 'untilId' parameter
- const [untilId, untilIdErr] = $.type(ID).optional().get(params.untilId);
- if (untilIdErr) return rej('invalid untilId param');
-
- // Check if both of sinceId and untilId is specified
- if (sinceId && untilId) {
- return rej('cannot set sinceId and untilId');
- }
-
- // Get 'channelId' parameter
- const [channelId, channelIdErr] = $.type(ID).get(params.channelId);
- if (channelIdErr) return rej('invalid channelId param');
-
- // Fetch channel
- const channel: IChannel = await Channel.findOne({
- _id: channelId
- });
-
- if (channel === null) {
- return rej('channel not found');
- }
-
- //#region Construct query
- const sort = {
- _id: -1
- };
-
- const query = {
- channelId: channel._id
- } as any;
-
- if (sinceId) {
- sort._id = 1;
- query._id = {
- $gt: sinceId
- };
- } else if (untilId) {
- query._id = {
- $lt: untilId
- };
- }
- //#endregion Construct query
-
- // Issue query
- const notes = await Note
- .find(query, {
- limit: limit,
- sort: sort
- });
-
- // Serialize
- res(await Promise.all(notes.map(async (note) =>
- await pack(note, user)
- )));
-});
diff --git a/src/server/api/endpoints/channels/show.ts b/src/server/api/endpoints/channels/show.ts
deleted file mode 100644
index 1bba63d490..0000000000
--- a/src/server/api/endpoints/channels/show.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Module dependencies
- */
-import $ from 'cafy'; import ID from '../../../../cafy-id';
-import Channel, { IChannel, pack } from '../../../../models/channel';
-
-/**
- * Show a channel
- */
-module.exports = (params, user) => new Promise(async (res, rej) => {
- // Get 'channelId' parameter
- const [channelId, channelIdErr] = $.type(ID).get(params.channelId);
- if (channelIdErr) return rej('invalid channelId param');
-
- // Fetch channel
- const channel: IChannel = await Channel.findOne({
- _id: channelId
- });
-
- if (channel === null) {
- return rej('channel not found');
- }
-
- // Serialize
- res(await pack(channel, user));
-});
diff --git a/src/server/api/endpoints/channels/unwatch.ts b/src/server/api/endpoints/channels/unwatch.ts
deleted file mode 100644
index f7dddff461..0000000000
--- a/src/server/api/endpoints/channels/unwatch.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * Module dependencies
- */
-import $ from 'cafy'; import ID from '../../../../cafy-id';
-import Channel from '../../../../models/channel';
-import Watching from '../../../../models/channel-watching';
-
-/**
- * Unwatch a channel
- */
-module.exports = (params, user) => new Promise(async (res, rej) => {
- // Get 'channelId' parameter
- const [channelId, channelIdErr] = $.type(ID).get(params.channelId);
- if (channelIdErr) return rej('invalid channelId param');
-
- //#region Fetch channel
- const channel = await Channel.findOne({
- _id: channelId
- });
-
- if (channel === null) {
- return rej('channel not found');
- }
- //#endregion
-
- //#region Check whether not watching
- const exist = await Watching.findOne({
- userId: user._id,
- channelId: channel._id,
- deletedAt: { $exists: false }
- });
-
- if (exist === null) {
- return rej('already not watching');
- }
- //#endregion
-
- // Delete watching
- await Watching.update({
- _id: exist._id
- }, {
- $set: {
- deletedAt: new Date()
- }
- });
-
- // Send response
- res();
-
- // Decrement watching count
- Channel.update(channel._id, {
- $inc: {
- watchingCount: -1
- }
- });
-});
diff --git a/src/server/api/endpoints/channels/watch.ts b/src/server/api/endpoints/channels/watch.ts
deleted file mode 100644
index 34243ff68b..0000000000
--- a/src/server/api/endpoints/channels/watch.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-/**
- * Module dependencies
- */
-import $ from 'cafy'; import ID from '../../../../cafy-id';
-import Channel from '../../../../models/channel';
-import Watching from '../../../../models/channel-watching';
-
-/**
- * Watch a channel
- */
-module.exports = (params, user) => new Promise(async (res, rej) => {
- // Get 'channelId' parameter
- const [channelId, channelIdErr] = $.type(ID).get(params.channelId);
- if (channelIdErr) return rej('invalid channelId param');
-
- //#region Fetch channel
- const channel = await Channel.findOne({
- _id: channelId
- });
-
- if (channel === null) {
- return rej('channel not found');
- }
- //#endregion
-
- //#region Check whether already watching
- const exist = await Watching.findOne({
- userId: user._id,
- channelId: channel._id,
- deletedAt: { $exists: false }
- });
-
- if (exist !== null) {
- return rej('already watching');
- }
- //#endregion
-
- // Create Watching
- await Watching.insert({
- createdAt: new Date(),
- userId: user._id,
- channelId: channel._id
- });
-
- // Send response
- res();
-
- // Increment watching count
- Channel.update(channel._id, {
- $inc: {
- watchingCount: 1
- }
- });
-});
diff --git a/src/server/api/endpoints/notes/create.ts b/src/server/api/endpoints/notes/create.ts
index 429b6d370a..182359f637 100644
--- a/src/server/api/endpoints/notes/create.ts
+++ b/src/server/api/endpoints/notes/create.ts
@@ -4,7 +4,6 @@
import $ from 'cafy'; import ID from '../../../../cafy-id';
import Note, { INote, isValidText, isValidCw, pack } from '../../../../models/note';
import User, { ILocalUser } from '../../../../models/user';
-import Channel, { IChannel } from '../../../../models/channel';
import DriveFile from '../../../../models/drive-file';
import create from '../../../../services/note/create';
import { IApp } from '../../../../models/app';
@@ -89,7 +88,6 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
if (renoteIdErr) return rej('invalid renoteId');
let renote: INote = null;
- let isQuote = false;
if (renoteId !== undefined) {
// Fetch renote to note
renote = await Note.findOne({
@@ -101,8 +99,6 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
} else if (renote.renoteId && !renote.text && !renote.mediaIds) {
return rej('cannot renote to renote');
}
-
- isQuote = text != null || files != null;
}
// Get 'replyId' parameter
@@ -126,47 +122,6 @@ module.exports = (params, user: ILocalUser, app: IApp) => new Promise(async (res
}
}
- // Get 'channelId' parameter
- const [channelId, channelIdErr] = $.type(ID).optional().get(params.channelId);
- if (channelIdErr) return rej('invalid channelId');
-
- let channel: IChannel = null;
- if (channelId !== undefined) {
- // Fetch channel
- channel = await Channel.findOne({
- _id: channelId
- });
-
- if (channel === null) {
- return rej('channel not found');
- }
-
- // 返信対象の投稿がこのチャンネルじゃなかったらダメ
- if (reply && !channelId.equals(reply.channelId)) {
- return rej('チャンネル内部からチャンネル外部の投稿に返信することはできません');
- }
-
- // Renote対象の投稿がこのチャンネルじゃなかったらダメ
- if (renote && !channelId.equals(renote.channelId)) {
- return rej('チャンネル内部からチャンネル外部の投稿をRenoteすることはできません');
- }
-
- // 引用ではないRenoteはダメ
- if (renote && !isQuote) {
- return rej('チャンネル内部では引用ではないRenoteをすることはできません');
- }
- } else {
- // 返信対象の投稿がチャンネルへの投稿だったらダメ
- if (reply && reply.channelId != null) {
- return rej('チャンネル外部からチャンネル内部の投稿に返信することはできません');
- }
-
- // Renote対象の投稿がチャンネルへの投稿だったらダメ
- if (renote && renote.channelId != null) {
- return rej('チャンネル外部からチャンネル内部の投稿をRenoteすることはできません');
- }
- }
-
// Get 'poll' parameter
const [poll, pollErr] = $.obj.optional().strict()
.have('choices', $.arr($.str)
diff --git a/src/server/api/endpoints/notes/timeline.ts b/src/server/api/endpoints/notes/timeline.ts
index 78786d4a16..9f32555649 100644
--- a/src/server/api/endpoints/notes/timeline.ts
+++ b/src/server/api/endpoints/notes/timeline.ts
@@ -4,7 +4,6 @@
import $ from 'cafy'; import ID from '../../../../cafy-id';
import Note from '../../../../models/note';
import Mute from '../../../../models/mute';
-import ChannelWatching from '../../../../models/channel-watching';
import { getFriends } from '../../common/get-friends';
import { pack } from '../../../../models/note';
@@ -45,18 +44,11 @@ module.exports = async (params, user, app) => {
const [includeRenotedMyNotes = true, includeRenotedMyNotesErr] = $.bool.optional().get(params.includeRenotedMyNotes);
if (includeRenotedMyNotesErr) throw 'invalid includeRenotedMyNotes param';
- const [followings, watchingChannelIds, mutedUserIds] = await Promise.all([
+ const [followings, mutedUserIds] = await Promise.all([
// フォローを取得
// Fetch following
getFriends(user._id),
- // Watchしているチャンネルを取得
- ChannelWatching.find({
- userId: user._id,
- // 削除されたドキュメントは除く
- deletedAt: { $exists: false }
- }).then(watches => watches.map(w => w.channelId)),
-
// ミュートしているユーザーを取得
Mute.find({
muterId: user._id
@@ -93,26 +85,9 @@ module.exports = async (params, user, app) => {
const query = {
$and: [{
- $or: [{
- $and: [{
- // フォローしている人のタイムラインへの投稿
- $or: followQuery
- }, {
- // 「タイムラインへの」投稿に限定するためにチャンネルが指定されていないもののみに限る
- $or: [{
- channelId: {
- $exists: false
- }
- }, {
- channelId: null
- }]
- }]
- }, {
- // Watchしているチャンネルへの投稿
- channelId: {
- $in: watchingChannelIds
- }
- }],
+ // フォローしている人の投稿
+ $or: followQuery,
+
// mute
userId: {
$nin: mutedUserIds
diff --git a/src/server/api/stream/channel.ts b/src/server/api/stream/channel.ts
deleted file mode 100644
index cb04278237..0000000000
--- a/src/server/api/stream/channel.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import * as websocket from 'websocket';
-import * as redis from 'redis';
-import { ParsedUrlQuery } from 'querystring';
-
-export default function(request: websocket.request, connection: websocket.connection, subscriber: redis.RedisClient): void {
- const q = request.resourceURL.query as ParsedUrlQuery;
- const channel = q.channel;
-
- // Subscribe channel stream
- subscriber.subscribe(`misskey:channel-stream:${channel}`);
- subscriber.on('message', (_, data) => {
- connection.send(data);
- });
-}
diff --git a/src/server/api/streaming.ts b/src/server/api/streaming.ts
index e4884ed7c4..6825b6336a 100644
--- a/src/server/api/streaming.ts
+++ b/src/server/api/streaming.ts
@@ -14,7 +14,6 @@ import othelloGameStream from './stream/othello-game';
import othelloStream from './stream/othello';
import serverStream from './stream/server';
import requestsStream from './stream/requests';
-import channelStream from './stream/channel';
import { ParsedUrlQuery } from 'querystring';
import authenticate from './authenticate';
@@ -48,11 +47,6 @@ module.exports = (server: http.Server) => {
subscriber.quit();
});
- if (request.resourceURL.pathname === '/channel') {
- channelStream(request, connection, subscriber);
- return;
- }
-
const q = request.resourceURL.query as ParsedUrlQuery;
const [user, app] = await authenticate(q.i as string);