diff options
Diffstat (limited to 'src/client')
59 files changed, 322 insertions, 658 deletions
diff --git a/src/client/app/common/scripts/compose-notification.ts b/src/client/app/common/scripts/compose-notification.ts index 2e58649ac2..47499e5490 100644 --- a/src/client/app/common/scripts/compose-notification.ts +++ b/src/client/app/common/scripts/compose-notification.ts @@ -1,6 +1,6 @@ -import getNoteSummary from '../../../../renderers/get-note-summary'; -import getReactionEmoji from '../../../../renderers/get-reaction-emoji'; -import getUserName from '../../../../renderers/get-user-name'; +import getNoteSummary from '../../../../misc/get-note-summary'; +import getReactionEmoji from '../../../../misc/get-reaction-emoji'; +import getUserName from '../../../../misc/get-user-name'; type Notification = { title: string; diff --git a/src/client/app/common/scripts/streaming/reversi-game.ts b/src/client/app/common/scripts/streaming/games/reversi/reversi-game.ts index 5638b3013f..e6b02fcfdb 100644 --- a/src/client/app/common/scripts/streaming/reversi-game.ts +++ b/src/client/app/common/scripts/streaming/games/reversi/reversi-game.ts @@ -1,9 +1,9 @@ -import Stream from './stream'; -import MiOS from '../../../mios'; +import Stream from '../../stream'; +import MiOS from '../../../../../mios'; export class ReversiGameStream extends Stream { constructor(os: MiOS, me, game) { - super(os, 'reversi-game', { + super(os, 'games/reversi-game', { i: me ? me.token : null, game: game.id }); diff --git a/src/client/app/common/scripts/streaming/reversi.ts b/src/client/app/common/scripts/streaming/games/reversi/reversi.ts index 2e4395f0f1..1f4fd8c63e 100644 --- a/src/client/app/common/scripts/streaming/reversi.ts +++ b/src/client/app/common/scripts/streaming/games/reversi/reversi.ts @@ -1,10 +1,10 @@ -import StreamManager from './stream-manager'; -import Stream from './stream'; -import MiOS from '../../../mios'; +import StreamManager from '../../stream-manager'; +import Stream from '../../stream'; +import MiOS from '../../../../../mios'; export class ReversiStream extends Stream { constructor(os: MiOS, me) { - super(os, 'reversi', { + super(os, 'games/reversi', { i: me.token }); } diff --git a/src/client/app/common/scripts/streaming/hybrid-timeline.ts b/src/client/app/common/scripts/streaming/hybrid-timeline.ts new file mode 100644 index 0000000000..cd290797c4 --- /dev/null +++ b/src/client/app/common/scripts/streaming/hybrid-timeline.ts @@ -0,0 +1,34 @@ +import Stream from './stream'; +import StreamManager from './stream-manager'; +import MiOS from '../../../mios'; + +/** + * Hybrid timeline stream connection + */ +export class HybridTimelineStream extends Stream { + constructor(os: MiOS, me) { + super(os, 'hybrid-timeline', { + i: me.token + }); + } +} + +export class HybridTimelineStreamManager extends StreamManager<HybridTimelineStream> { + private me; + private os: MiOS; + + constructor(os: MiOS, me) { + super(); + + this.me = me; + this.os = os; + } + + public getConnection() { + if (this.connection == null) { + this.connection = new HybridTimelineStream(this.os, this.me); + } + + return this.connection; + } +} diff --git a/src/client/app/common/views/components/reversi.game.vue b/src/client/app/common/views/components/games/reversi/reversi.game.vue index a2a6fd0dc1..ed9e718497 100644 --- a/src/client/app/common/views/components/reversi.game.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.game.vue @@ -58,8 +58,8 @@ <script lang="ts"> import Vue from 'vue'; import * as CRC32 from 'crc-32'; -import Reversi, { Color } from '../../../../../reversi/core'; -import { url } from '../../../config'; +import Reversi, { Color } from '../../../../../../../games/reversi/core'; +import { url } from '../../../../../config'; export default Vue.extend({ props: ['initGame', 'connection'], diff --git a/src/client/app/common/views/components/reversi.gameroom.vue b/src/client/app/common/views/components/games/reversi/reversi.gameroom.vue index 7ce0112451..4969a9347e 100644 --- a/src/client/app/common/views/components/reversi.gameroom.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.gameroom.vue @@ -9,7 +9,7 @@ import Vue from 'vue'; import XGame from './reversi.game.vue'; import XRoom from './reversi.room.vue'; -import { ReversiGameStream } from '../../scripts/streaming/reversi-game'; +import { ReversiGameStream } from '../../../../scripts/streaming/games/reversi/reversi-game'; export default Vue.extend({ components: { diff --git a/src/client/app/common/views/components/reversi.room.vue b/src/client/app/common/views/components/games/reversi/reversi.room.vue index 5074845758..03152dcf19 100644 --- a/src/client/app/common/views/components/reversi.room.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.room.vue @@ -94,7 +94,7 @@ <script lang="ts"> import Vue from 'vue'; -import * as maps from '../../../../../reversi/maps'; +import * as maps from '../../../../../../../games/reversi/maps'; export default Vue.extend({ props: ['game', 'connection'], diff --git a/src/client/app/common/views/components/reversi.vue b/src/client/app/common/views/components/games/reversi/reversi.vue index 61705163ac..6e64710823 100644 --- a/src/client/app/common/views/components/reversi.vue +++ b/src/client/app/common/views/components/games/reversi/reversi.vue @@ -99,18 +99,18 @@ export default Vue.extend({ this.connection.on('matched', this.onMatched); this.connection.on('invited', this.onInvited); - (this as any).api('reversi/games', { + (this as any).api('games/reversi/games', { my: true }).then(games => { this.myGames = games; }); - (this as any).api('reversi/games').then(games => { + (this as any).api('games/reversi/games').then(games => { this.games = games; this.gamesFetching = false; }); - (this as any).api('reversi/invitations').then(invitations => { + (this as any).api('games/reversi/invitations').then(invitations => { this.invitations = this.invitations.concat(invitations); }); @@ -132,7 +132,7 @@ export default Vue.extend({ }, methods: { go(game) { - (this as any).api('reversi/games/show', { + (this as any).api('games/reversi/games/show', { gameId: game.id }).then(game => { this.matching = null; @@ -146,7 +146,7 @@ export default Vue.extend({ (this as any).api('users/show', { username }).then(user => { - (this as any).api('reversi/match', { + (this as any).api('games/reversi/match', { userId: user.id }).then(res => { if (res == null) { @@ -160,10 +160,10 @@ export default Vue.extend({ }, cancel() { this.matching = null; - (this as any).api('reversi/match/cancel'); + (this as any).api('games/reversi/match/cancel'); }, accept(invitation) { - (this as any).api('reversi/match', { + (this as any).api('games/reversi/match', { userId: invitation.parent.id }).then(game => { if (game) { diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts index e2cba2e53b..c18a1c3b68 100644 --- a/src/client/app/common/views/components/index.ts +++ b/src/client/app/common/views/components/index.ts @@ -27,7 +27,7 @@ import urlPreview from './url-preview.vue'; import twitterSetting from './twitter-setting.vue'; import fileTypeIcon from './file-type-icon.vue'; import Switch from './switch.vue'; -import Reversi from './reversi.vue'; +import Reversi from './games/reversi/reversi.vue'; import welcomeTimeline from './welcome-timeline.vue'; import uiInput from './ui/input.vue'; import uiButton from './ui/button.vue'; diff --git a/src/client/app/common/views/components/messaging-room.vue b/src/client/app/common/views/components/messaging-room.vue index b2831d6928..291971d4df 100644 --- a/src/client/app/common/views/components/messaging-room.vue +++ b/src/client/app/common/views/components/messaging-room.vue @@ -255,7 +255,8 @@ root(isDark) width 100% max-width 600px margin 0 auto - flex 1 + flex 1 1 0 + overflow-y auto > .init width 100% @@ -341,10 +342,6 @@ root(isDark) background isDark ? #191b22 : #fff > footer - position -webkit-sticky - position sticky - z-index 2 - bottom 0 width 100% max-width 600px margin 0 auto diff --git a/src/client/app/common/views/components/messaging.vue b/src/client/app/common/views/components/messaging.vue index 2ddec29984..6abfc92dca 100644 --- a/src/client/app/common/views/components/messaging.vue +++ b/src/client/app/common/views/components/messaging.vue @@ -51,7 +51,7 @@ <script lang="ts"> import Vue from 'vue'; -import getAcct from '../../../../../acct/render'; +import getAcct from '../../../../../misc/acct/render'; export default Vue.extend({ props: { diff --git a/src/client/app/common/views/components/misskey-flavored-markdown.ts b/src/client/app/common/views/components/misskey-flavored-markdown.ts index c321c76104..c93e09fb5f 100644 --- a/src/client/app/common/views/components/misskey-flavored-markdown.ts +++ b/src/client/app/common/views/components/misskey-flavored-markdown.ts @@ -1,7 +1,7 @@ import Vue from 'vue'; import * as emojilib from 'emojilib'; import parse from '../../../../../mfm/parse'; -import getAcct from '../../../../../acct/render'; +import getAcct from '../../../../../misc/acct/render'; import { url } from '../../../config'; import MkUrl from './url.vue'; import MkGoogle from './google.vue'; diff --git a/src/client/app/common/views/components/nav.vue b/src/client/app/common/views/components/nav.vue index cd1f99288a..e25dbc78ca 100644 --- a/src/client/app/common/views/components/nav.vue +++ b/src/client/app/common/views/components/nav.vue @@ -2,9 +2,9 @@ <span class="mk-nav"> <a :href="aboutUrl">%i18n:@about%</a> <i>・</i> - <a href="https://github.com/syuilo/misskey">%i18n:@repository%</a> + <a :href="repositoryUrl">%i18n:@repository%</a> <i>・</i> - <a href="https://github.com/syuilo/misskey/issues/new" target="_blank">%i18n:@feedback%</a> + <a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a> <i>・</i> <a :href="devUrl">%i18n:@develop%</a> <i>・</i> @@ -14,7 +14,7 @@ <script lang="ts"> import Vue from 'vue'; -import { docsUrl, statsUrl, statusUrl, devUrl, lang } from '../../../config'; +import { docsUrl, statsUrl, statusUrl, devUrl, repositoryUrl, feedbackUrl, lang } from '../../../config'; export default Vue.extend({ data() { @@ -22,7 +22,9 @@ export default Vue.extend({ aboutUrl: `${docsUrl}/${lang}/about`, statsUrl, statusUrl, - devUrl + devUrl, + repositoryUrl: repositoryUrl || `https://github.com/syuilo/misskey`, + feedbackUrl: feedbackUrl || `https://github.com/syuilo/misskey/issues/new` } } }); diff --git a/src/client/app/common/views/components/reaction-picker.vue b/src/client/app/common/views/components/reaction-picker.vue index ed7aedb58e..5a149cc4d1 100644 --- a/src/client/app/common/views/components/reaction-picker.vue +++ b/src/client/app/common/views/components/reaction-picker.vue @@ -183,7 +183,7 @@ root(isDark) border-right solid $balloon-size transparent border-bottom solid $balloon-size $bgcolor - &.compact + &.big > div width 280px diff --git a/src/client/app/common/views/filters/user.ts b/src/client/app/common/views/filters/user.ts index c5bb39f674..ca0910fc53 100644 --- a/src/client/app/common/views/filters/user.ts +++ b/src/client/app/common/views/filters/user.ts @@ -1,6 +1,6 @@ import Vue from 'vue'; -import getAcct from '../../../../../acct/render'; -import getUserName from '../../../../../renderers/get-user-name'; +import getAcct from '../../../../../misc/acct/render'; +import getUserName from '../../../../../misc/get-user-name'; Vue.filter('acct', user => { return getAcct(user); diff --git a/src/client/app/common/views/pages/follow.vue b/src/client/app/common/views/pages/follow.vue index c8e838be85..4b8c2d3b7c 100644 --- a/src/client/app/common/views/pages/follow.vue +++ b/src/client/app/common/views/pages/follow.vue @@ -31,8 +31,8 @@ <script lang="ts"> import Vue from 'vue'; -import parseAcct from '../../../../../acct/parse'; -import getUserName from '../../../../../renderers/get-user-name'; +import parseAcct from '../../../../../misc/acct/parse'; +import getUserName from '../../../../../misc/get-user-name'; import Progress from '../../../common/scripts/loading'; export default Vue.extend({ diff --git a/src/client/app/config.ts b/src/client/app/config.ts index c6efe26cd5..ceee0a2d62 100644 --- a/src/client/app/config.ts +++ b/src/client/app/config.ts @@ -9,6 +9,8 @@ declare const _DOCS_URL_: string; declare const _STATS_URL_: string; declare const _STATUS_URL_: string; declare const _DEV_URL_: string; +declare const _REPOSITORY_URL_: string; +declare const _FEEDBACK_URL_: string; declare const _LANG_: string; declare const _LANGS_: string; declare const _RECAPTCHA_SITEKEY_: string; @@ -32,6 +34,8 @@ export const docsUrl = _DOCS_URL_; export const statsUrl = _STATS_URL_; export const statusUrl = _STATUS_URL_; export const devUrl = _DEV_URL_; +export const repositoryUrl = _REPOSITORY_URL_; +export const feedbackUrl = _FEEDBACK_URL_; export const lang = _LANG_; export const langs = _LANGS_; export const recaptchaSitekey = _RECAPTCHA_SITEKEY_; 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 cbb58b5e99..41b421b0e7 100644 --- a/src/client/app/desktop/views/components/messaging-room-window.vue +++ b/src/client/app/desktop/views/components/messaging-room-window.vue @@ -8,7 +8,7 @@ <script lang="ts"> import Vue from 'vue'; import { url } from '../../../config'; -import getAcct from '../../../../../acct/render'; +import getAcct from '../../../../../misc/acct/render'; export default Vue.extend({ props: ['user'], diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue index 1206eb7136..c959508020 100644 --- a/src/client/app/desktop/views/components/notes.vue +++ b/src/client/app/desktop/views/components/notes.vue @@ -34,7 +34,7 @@ <script lang="ts"> import Vue from 'vue'; import { url } from '../../../config'; -import getNoteSummary from '../../../../../renderers/get-note-summary'; +import getNoteSummary from '../../../../../misc/get-note-summary'; import XNote from './notes.note.vue'; diff --git a/src/client/app/desktop/views/components/notifications.vue b/src/client/app/desktop/views/components/notifications.vue index 32b36994db..b291e1f54a 100644 --- a/src/client/app/desktop/views/components/notifications.vue +++ b/src/client/app/desktop/views/components/notifications.vue @@ -110,7 +110,7 @@ <script lang="ts"> import Vue from 'vue'; -import getNoteSummary from '../../../../../renderers/get-note-summary'; +import getNoteSummary from '../../../../../misc/get-note-summary'; export default Vue.extend({ data() { diff --git a/src/client/app/desktop/views/components/settings.vue b/src/client/app/desktop/views/components/settings.vue index 74ab45626d..00bd7a8783 100644 --- a/src/client/app/desktop/views/components/settings.vue +++ b/src/client/app/desktop/views/components/settings.vue @@ -410,7 +410,7 @@ export default Vue.extend({ localStorage.clear(); (this as any).apis.dialog({ title: '%i18n:@cache-cleared%', - text: '%i18n:@caache-cleared-desc%' + text: '%i18n:@cache-cleared-desc%' }); }, soundTest() { diff --git a/src/client/app/desktop/views/components/timeline.core.vue b/src/client/app/desktop/views/components/timeline.core.vue index 1728dad286..15e188be67 100644 --- a/src/client/app/desktop/views/components/timeline.core.vue +++ b/src/client/app/desktop/views/components/timeline.core.vue @@ -43,19 +43,21 @@ export default Vue.extend({ }, stream(): any { - return this.src == 'home' - ? (this as any).os.stream - : this.src == 'local' - ? (this as any).os.streams.localTimelineStream - : (this as any).os.streams.globalTimelineStream; + switch (this.src) { + case 'home': return (this as any).os.stream; + case 'local': return (this as any).os.streams.localTimelineStream; + case 'hybrid': return (this as any).os.streams.hybridTimelineStream; + case 'global': return (this as any).os.streams.globalTimelineStream; + } }, endpoint(): string { - return this.src == 'home' - ? 'notes/timeline' - : this.src == 'local' - ? 'notes/local-timeline' - : 'notes/global-timeline'; + switch (this.src) { + case 'home': return 'notes/timeline'; + case 'local': return 'notes/local-timeline'; + case 'hybrid': return 'notes/hybrid-timeline'; + case 'global': return 'notes/global-timeline'; + } }, canFetchMore(): boolean { diff --git a/src/client/app/desktop/views/components/timeline.vue b/src/client/app/desktop/views/components/timeline.vue index 0728b78aa9..a5ea1487ab 100644 --- a/src/client/app/desktop/views/components/timeline.vue +++ b/src/client/app/desktop/views/components/timeline.vue @@ -3,12 +3,14 @@ <header> <span :data-active="src == 'home'" @click="src = 'home'">%fa:home% %i18n:@home%</span> <span :data-active="src == 'local'" @click="src = 'local'">%fa:R comments% %i18n:@local%</span> + <span :data-active="src == 'hybrid'" @click="src = 'hybrid'">%fa:share-alt% %i18n:@hybrid%</span> <span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% %i18n:@global%</span> <span :data-active="src == 'list'" @click="src = 'list'" v-if="list">%fa:list% {{ list.title }}</span> <button @click="chooseList" title="%i18n:@list%">%fa:list%</button> </header> <x-core v-if="src == 'home'" ref="tl" key="home" src="home"/> <x-core v-if="src == 'local'" ref="tl" key="local" src="local"/> + <x-core v-if="src == 'hybrid'" ref="tl" key="hybrid" src="hybrid"/> <x-core v-if="src == 'global'" ref="tl" key="global" src="global"/> <mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/> </div> diff --git a/src/client/app/desktop/views/components/user-preview.vue b/src/client/app/desktop/views/components/user-preview.vue index 788881ead5..933e701e58 100644 --- a/src/client/app/desktop/views/components/user-preview.vue +++ b/src/client/app/desktop/views/components/user-preview.vue @@ -27,7 +27,7 @@ <script lang="ts"> import Vue from 'vue'; import * as anime from 'animejs'; -import parseAcct from '../../../../../acct/parse'; +import parseAcct from '../../../../../misc/acct/parse'; export default Vue.extend({ props: { diff --git a/src/client/app/desktop/views/pages/deck/deck.column-core.vue b/src/client/app/desktop/views/pages/deck/deck.column-core.vue index 28e7f13650..7f219c0be1 100644 --- a/src/client/app/desktop/views/pages/deck/deck.column-core.vue +++ b/src/client/app/desktop/views/pages/deck/deck.column-core.vue @@ -3,6 +3,7 @@ <x-notifications-column v-else-if="column.type == 'notifications'" :column="column" :is-stacked="isStacked"/> <x-tl-column v-else-if="column.type == 'home'" :column="column" :is-stacked="isStacked"/> <x-tl-column v-else-if="column.type == 'local'" :column="column" :is-stacked="isStacked"/> +<x-tl-column v-else-if="column.type == 'hybrid'" :column="column" :is-stacked="isStacked"/> <x-tl-column v-else-if="column.type == 'global'" :column="column" :is-stacked="isStacked"/> <x-tl-column v-else-if="column.type == 'list'" :column="column" :is-stacked="isStacked"/> </template> diff --git a/src/client/app/desktop/views/pages/deck/deck.notification.vue b/src/client/app/desktop/views/pages/deck/deck.notification.vue index a379adc69e..d0093ff282 100644 --- a/src/client/app/desktop/views/pages/deck/deck.notification.vue +++ b/src/client/app/desktop/views/pages/deck/deck.notification.vue @@ -81,7 +81,7 @@ <script lang="ts"> import Vue from 'vue'; -import getNoteSummary from '../../../../../../renderers/get-note-summary'; +import getNoteSummary from '../../../../../../misc/get-note-summary'; import XNote from './deck.note.vue'; export default Vue.extend({ diff --git a/src/client/app/desktop/views/pages/deck/deck.tl-column.vue b/src/client/app/desktop/views/pages/deck/deck.tl-column.vue index ffe1da670b..231b505f5d 100644 --- a/src/client/app/desktop/views/pages/deck/deck.tl-column.vue +++ b/src/client/app/desktop/views/pages/deck/deck.tl-column.vue @@ -3,6 +3,7 @@ <span slot="header"> <template v-if="column.type == 'home'">%fa:home%</template> <template v-if="column.type == 'local'">%fa:R comments%</template> + <template v-if="column.type == 'hybrid'">%fa:share-alt%</template> <template v-if="column.type == 'global'">%fa:globe%</template> <template v-if="column.type == 'list'">%fa:list%</template> <span>{{ name }}</span> @@ -61,6 +62,7 @@ export default Vue.extend({ switch (this.column.type) { case 'home': return '%i18n:common.deck.home%'; case 'local': return '%i18n:common.deck.local%'; + case 'hybrid': return '%i18n:common.deck.hybrid%'; case 'global': return '%i18n:common.deck.global%'; case 'list': return this.column.list.title; } diff --git a/src/client/app/desktop/views/pages/deck/deck.tl.vue b/src/client/app/desktop/views/pages/deck/deck.tl.vue index 8e05f09c5d..d402ee6a8b 100644 --- a/src/client/app/desktop/views/pages/deck/deck.tl.vue +++ b/src/client/app/desktop/views/pages/deck/deck.tl.vue @@ -41,27 +41,29 @@ export default Vue.extend({ }; }, - watch: { - mediaOnly() { - this.fetch(); - } - }, - computed: { stream(): any { - return this.src == 'home' - ? (this as any).os.stream - : this.src == 'local' - ? (this as any).os.streams.localTimelineStream - : (this as any).os.streams.globalTimelineStream; + switch (this.src) { + case 'home': return (this as any).os.stream; + case 'local': return (this as any).os.streams.localTimelineStream; + case 'hybrid': return (this as any).os.streams.hybridTimelineStream; + case 'global': return (this as any).os.streams.globalTimelineStream; + } }, endpoint(): string { - return this.src == 'home' - ? 'notes/timeline' - : this.src == 'local' - ? 'notes/local-timeline' - : 'notes/global-timeline'; + switch (this.src) { + case 'home': return 'notes/timeline'; + case 'local': return 'notes/local-timeline'; + case 'hybrid': return 'notes/hybrid-timeline'; + case 'global': return 'notes/global-timeline'; + } + }, + }, + + watch: { + mediaOnly() { + this.fetch(); } }, diff --git a/src/client/app/desktop/views/pages/deck/deck.vue b/src/client/app/desktop/views/pages/deck/deck.vue index da4acb8cca..26b989656e 100644 --- a/src/client/app/desktop/views/pages/deck/deck.vue +++ b/src/client/app/desktop/views/pages/deck/deck.vue @@ -120,6 +120,15 @@ export default Vue.extend({ }); } }, { + icon: '%fa:share-alt%', + text: '%i18n:common.deck.hybrid%', + action: () => { + this.$store.dispatch('settings/addDeckColumn', { + id: uuid(), + type: 'hybrid' + }); + } + }, { icon: '%fa:globe%', text: '%i18n:common.deck.global%', action: () => { diff --git a/src/client/app/desktop/views/pages/messaging-room.vue b/src/client/app/desktop/views/pages/messaging-room.vue index 06c32776c9..1ebd53cef4 100644 --- a/src/client/app/desktop/views/pages/messaging-room.vue +++ b/src/client/app/desktop/views/pages/messaging-room.vue @@ -7,8 +7,8 @@ <script lang="ts"> import Vue from 'vue'; import Progress from '../../../common/scripts/loading'; -import parseAcct from '../../../../../acct/parse'; -import getUserName from '../../../../../renderers/get-user-name'; +import parseAcct from '../../../../../misc/acct/parse'; +import getUserName from '../../../../../misc/get-user-name'; export default Vue.extend({ data() { diff --git a/src/client/app/desktop/views/pages/reversi.vue b/src/client/app/desktop/views/pages/reversi.vue index 098fc41f1c..b484b81b5d 100644 --- a/src/client/app/desktop/views/pages/reversi.vue +++ b/src/client/app/desktop/views/pages/reversi.vue @@ -33,7 +33,7 @@ export default Vue.extend({ Progress.start(); this.fetching = true; - (this as any).api('reversi/games/show', { + (this as any).api('games/reversi/games/show', { gameId: this.$route.params.game }).then(game => { this.game = game; diff --git a/src/client/app/desktop/views/pages/search.vue b/src/client/app/desktop/views/pages/search.vue index e79ac1c739..6ebb83cac8 100644 --- a/src/client/app/desktop/views/pages/search.vue +++ b/src/client/app/desktop/views/pages/search.vue @@ -7,19 +7,13 @@ <mk-ellipsis-icon/> </div> <p :class="$style.empty" v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p> - <mk-notes ref="timeline" :class="$style.notes" :notes="notes"> - <div slot="footer"> - <template v-if="!moreFetching">%fa:search%</template> - <template v-if="moreFetching">%fa:spinner .pulse .fw%</template> - </div> - </mk-notes> + <mk-notes ref="timeline" :class="$style.notes" :more="existMore ? more : null"/> </mk-ui> </template> <script lang="ts"> import Vue from 'vue'; import Progress from '../../../common/scripts/loading'; -import parse from '../../../common/scripts/parse-search-query'; const limit = 20; @@ -30,16 +24,13 @@ export default Vue.extend({ moreFetching: false, existMore: false, offset: 0, - notes: [] + empty: false }; }, watch: { $route: 'fetch' }, computed: { - empty(): boolean { - return this.notes.length == 0; - }, q(): string { return this.$route.query.q; } @@ -66,39 +57,43 @@ export default Vue.extend({ this.fetching = true; Progress.start(); - (this as any).api('notes/search', Object.assign({ - limit: limit + 1, - offset: this.offset - }, parse(this.q))).then(notes => { - if (notes.length == limit + 1) { - notes.pop(); - this.existMore = true; - } - this.notes = notes; - this.fetching = false; - Progress.done(); - }); + (this.$refs.timeline as any).init(() => new Promise((res, rej) => { + (this as any).api('notes/search', { + limit: limit + 1, + offset: this.offset, + query: this.q + }).then(notes => { + if (notes.length == 0) this.empty = true; + if (notes.length == limit + 1) { + notes.pop(); + this.existMore = true; + } + res(notes); + this.fetching = false; + Progress.done(); + }, rej); + })); }, more() { - if (this.moreFetching || this.fetching || this.notes.length == 0 || !this.existMore) return; this.offset += limit; - this.moreFetching = true; - return (this as any).api('notes/search', Object.assign({ + + const promise = (this as any).api('notes/search', { limit: limit + 1, - offset: this.offset - }, parse(this.q))).then(notes => { + offset: this.offset, + query: this.q + }); + + promise.then(notes => { if (notes.length == limit + 1) { notes.pop(); } else { this.existMore = false; } - this.notes = this.notes.concat(notes); + notes.forEach(n => (this.$refs.timeline as any).append(n)); this.moreFetching = false; }); - }, - onScroll() { - const current = window.scrollY + window.innerHeight; - if (current > document.body.offsetHeight - 16) this.more(); + + return promise; } } }); diff --git a/src/client/app/desktop/views/pages/user/user.vue b/src/client/app/desktop/views/pages/user/user.vue index fc5c900037..64a4eaa872 100644 --- a/src/client/app/desktop/views/pages/user/user.vue +++ b/src/client/app/desktop/views/pages/user/user.vue @@ -27,8 +27,8 @@ <script lang="ts"> import Vue from 'vue'; -import parseAcct from '../../../../../../acct/parse'; -import getUserName from '../../../../../../renderers/get-user-name'; +import parseAcct from '../../../../../../misc/acct/parse'; +import getUserName from '../../../../../../misc/get-user-name'; import Progress from '../../../../common/scripts/loading'; import XHeader from './user.header.vue'; import XTimeline from './user.timeline.vue'; diff --git a/src/client/app/mios.ts b/src/client/app/mios.ts index 9a8d19adbd..565c8bf1f5 100644 --- a/src/client/app/mios.ts +++ b/src/client/app/mios.ts @@ -11,10 +11,11 @@ import { DriveStreamManager } from './common/scripts/streaming/drive'; import { ServerStatsStreamManager } from './common/scripts/streaming/server-stats'; import { NotesStatsStreamManager } from './common/scripts/streaming/notes-stats'; import { MessagingIndexStreamManager } from './common/scripts/streaming/messaging-index'; -import { ReversiStreamManager } from './common/scripts/streaming/reversi'; +import { ReversiStreamManager } from './common/scripts/streaming/games/reversi/reversi'; import Err from './common/views/components/connect-failed.vue'; import { LocalTimelineStreamManager } from './common/scripts/streaming/local-timeline'; +import { HybridTimelineStreamManager } from './common/scripts/streaming/hybrid-timeline'; import { GlobalTimelineStreamManager } from './common/scripts/streaming/global-timeline'; //#region api requests @@ -103,6 +104,7 @@ export default class MiOS extends EventEmitter { */ public streams: { localTimelineStream: LocalTimelineStreamManager; + hybridTimelineStream: HybridTimelineStreamManager; globalTimelineStream: GlobalTimelineStreamManager; driveStream: DriveStreamManager; serverStatsStream: ServerStatsStreamManager; @@ -111,6 +113,7 @@ export default class MiOS extends EventEmitter { reversiStream: ReversiStreamManager; } = { localTimelineStream: null, + hybridTimelineStream: null, globalTimelineStream: null, driveStream: null, serverStatsStream: null, @@ -230,6 +233,7 @@ export default class MiOS extends EventEmitter { // Init other stream manager this.streams.localTimelineStream = new LocalTimelineStreamManager(this, this.store.state.i); + this.streams.hybridTimelineStream = new HybridTimelineStreamManager(this, this.store.state.i); this.streams.globalTimelineStream = new GlobalTimelineStreamManager(this, this.store.state.i); this.streams.driveStream = new DriveStreamManager(this, this.store.state.i); this.streams.messagingIndexStream = new MessagingIndexStreamManager(this, this.store.state.i); diff --git a/src/client/app/mobile/views/components/note-card.vue b/src/client/app/mobile/views/components/note-card.vue index 89700b5e82..6d94c852be 100644 --- a/src/client/app/mobile/views/components/note-card.vue +++ b/src/client/app/mobile/views/components/note-card.vue @@ -14,7 +14,7 @@ <script lang="ts"> import Vue from 'vue'; -import summary from '../../../../../renderers/get-note-summary'; +import summary from '../../../../../misc/get-note-summary'; export default Vue.extend({ props: ['note'], diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue index 06d22c7258..01f3d76c74 100644 --- a/src/client/app/mobile/views/components/notes.vue +++ b/src/client/app/mobile/views/components/notes.vue @@ -37,7 +37,7 @@ <script lang="ts"> import Vue from 'vue'; -import getNoteSummary from '../../../../../renderers/get-note-summary'; +import getNoteSummary from '../../../../../misc/get-note-summary'; const displayLimit = 30; diff --git a/src/client/app/mobile/views/components/notification-preview.vue b/src/client/app/mobile/views/components/notification-preview.vue index 5e2306932b..be2c7a60ed 100644 --- a/src/client/app/mobile/views/components/notification-preview.vue +++ b/src/client/app/mobile/views/components/notification-preview.vue @@ -66,7 +66,7 @@ <script lang="ts"> import Vue from 'vue'; -import getNoteSummary from '../../../../../renderers/get-note-summary'; +import getNoteSummary from '../../../../../misc/get-note-summary'; export default Vue.extend({ props: ['notification'], diff --git a/src/client/app/mobile/views/components/notification.vue b/src/client/app/mobile/views/components/notification.vue index bbcae05f10..ee90c6b46b 100644 --- a/src/client/app/mobile/views/components/notification.vue +++ b/src/client/app/mobile/views/components/notification.vue @@ -81,7 +81,7 @@ <script lang="ts"> import Vue from 'vue'; -import getNoteSummary from '../../../../../renderers/get-note-summary'; +import getNoteSummary from '../../../../../misc/get-note-summary'; export default Vue.extend({ props: ['notification'], diff --git a/src/client/app/mobile/views/pages/followers.vue b/src/client/app/mobile/views/pages/followers.vue index dfb9c62142..f53fadb589 100644 --- a/src/client/app/mobile/views/pages/followers.vue +++ b/src/client/app/mobile/views/pages/followers.vue @@ -19,8 +19,8 @@ <script lang="ts"> import Vue from 'vue'; import Progress from '../../../common/scripts/loading'; -import parseAcct from '../../../../../acct/parse'; -import getUserName from '../../../../../renderers/get-user-name'; +import parseAcct from '../../../../../misc/acct/parse'; +import getUserName from '../../../../../misc/get-user-name'; export default Vue.extend({ data() { diff --git a/src/client/app/mobile/views/pages/following.vue b/src/client/app/mobile/views/pages/following.vue index 35461ea2fc..b28d7dd7b9 100644 --- a/src/client/app/mobile/views/pages/following.vue +++ b/src/client/app/mobile/views/pages/following.vue @@ -19,7 +19,7 @@ <script lang="ts"> import Vue from 'vue'; import Progress from '../../../common/scripts/loading'; -import parseAcct from '../../../../../acct/parse'; +import parseAcct from '../../../../../misc/acct/parse'; export default Vue.extend({ data() { diff --git a/src/client/app/mobile/views/pages/home.timeline.vue b/src/client/app/mobile/views/pages/home.timeline.vue index 364367b940..93d1364e09 100644 --- a/src/client/app/mobile/views/pages/home.timeline.vue +++ b/src/client/app/mobile/views/pages/home.timeline.vue @@ -42,19 +42,21 @@ export default Vue.extend({ }, stream(): any { - return this.src == 'home' - ? (this as any).os.stream - : this.src == 'local' - ? (this as any).os.streams.localTimelineStream - : (this as any).os.streams.globalTimelineStream; + switch (this.src) { + case 'home': return (this as any).os.stream; + case 'local': return (this as any).os.streams.localTimelineStream; + case 'hybrid': return (this as any).os.streams.hybridTimelineStream; + case 'global': return (this as any).os.streams.globalTimelineStream; + } }, endpoint(): string { - return this.src == 'home' - ? 'notes/timeline' - : this.src == 'local' - ? 'notes/local-timeline' - : 'notes/global-timeline'; + switch (this.src) { + case 'home': return 'notes/timeline'; + case 'local': return 'notes/local-timeline'; + case 'hybrid': return 'notes/hybrid-timeline'; + case 'global': return 'notes/global-timeline'; + } }, canFetchMore(): boolean { diff --git a/src/client/app/mobile/views/pages/home.vue b/src/client/app/mobile/views/pages/home.vue index c0c2ee8ab5..09930aca5a 100644 --- a/src/client/app/mobile/views/pages/home.vue +++ b/src/client/app/mobile/views/pages/home.vue @@ -4,6 +4,7 @@ <span> <span v-if="src == 'home'">%fa:home%%i18n:@home%</span> <span v-if="src == 'local'">%fa:R comments%%i18n:@local%</span> + <span v-if="src == 'hybrid'">%fa:share-alt%%i18n:@hybrid%</span> <span v-if="src == 'global'">%fa:globe%%i18n:@global%</span> <span v-if="src == 'list'">%fa:list%{{ list.title }}</span> </span> @@ -24,6 +25,7 @@ <div> <span :data-active="src == 'home'" @click="src = 'home'">%fa:home% %i18n:@home%</span> <span :data-active="src == 'local'" @click="src = 'local'">%fa:R comments% %i18n:@local%</span> + <span :data-active="src == 'hybrid'" @click="src = 'hybrid'">%fa:share-alt% %i18n:@hybrid%</span> <span :data-active="src == 'global'" @click="src = 'global'">%fa:globe% %i18n:@global%</span> <template v-if="lists"> <span v-for="l in lists" :data-active="src == 'list' && list == l" @click="src = 'list'; list = l" :key="l.id">%fa:list% {{ l.title }}</span> @@ -35,6 +37,7 @@ <div class="tl"> <x-tl v-if="src == 'home'" ref="tl" key="home" src="home"/> <x-tl v-if="src == 'local'" ref="tl" key="local" src="local"/> + <x-tl v-if="src == 'hybrid'" ref="tl" key="hybrid" src="hybrid"/> <x-tl v-if="src == 'global'" ref="tl" key="global" src="global"/> <mk-user-list-timeline v-if="src == 'list'" ref="tl" :key="list.id" :list="list"/> </div> diff --git a/src/client/app/mobile/views/pages/messaging-room.vue b/src/client/app/mobile/views/pages/messaging-room.vue index 8b82b03fb9..35ae506761 100644 --- a/src/client/app/mobile/views/pages/messaging-room.vue +++ b/src/client/app/mobile/views/pages/messaging-room.vue @@ -10,7 +10,7 @@ <script lang="ts"> import Vue from 'vue'; -import parseAcct from '../../../../../acct/parse'; +import parseAcct from '../../../../../misc/acct/parse'; export default Vue.extend({ data() { diff --git a/src/client/app/mobile/views/pages/messaging.vue b/src/client/app/mobile/views/pages/messaging.vue index 057470efd9..8dcbc5d6c5 100644 --- a/src/client/app/mobile/views/pages/messaging.vue +++ b/src/client/app/mobile/views/pages/messaging.vue @@ -7,7 +7,7 @@ <script lang="ts"> import Vue from 'vue'; -import getAcct from '../../../../../acct/render'; +import getAcct from '../../../../../misc/acct/render'; export default Vue.extend({ mounted() { diff --git a/src/client/app/mobile/views/pages/reversi.vue b/src/client/app/mobile/views/pages/reversi.vue index e2f0db6d87..0cff1317aa 100644 --- a/src/client/app/mobile/views/pages/reversi.vue +++ b/src/client/app/mobile/views/pages/reversi.vue @@ -33,7 +33,7 @@ export default Vue.extend({ Progress.start(); this.fetching = true; - (this as any).api('reversi/games/show', { + (this as any).api('games/reversi/games/show', { gameId: this.$route.params.game }).then(game => { this.game = game; diff --git a/src/client/app/mobile/views/pages/search.vue b/src/client/app/mobile/views/pages/search.vue index 9850fbcfb4..2559922efb 100644 --- a/src/client/app/mobile/views/pages/search.vue +++ b/src/client/app/mobile/views/pages/search.vue @@ -1,14 +1,10 @@ <template> <mk-ui> <span slot="header">%fa:search% {{ q }}</span> - <main v-if="!fetching"> - <mk-notes :class="$style.notes" :notes="notes"> - <span v-if="notes.length == 0">{{ '%i18n:@empty%'.replace('{}', q) }}</span> - <button v-if="existMore" @click="more" :disabled="fetching" slot="tail"> - <span v-if="!fetching">%i18n:@load-more%</span> - <span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span> - </button> - </mk-notes> + + <main> + <p v-if="!fetching && empty">%fa:search%「{{ q }}」に関する投稿は見つかりませんでした。</p> + <mk-notes ref="timeline" :more="existMore ? more : null"/> </main> </mk-ui> </template> @@ -16,7 +12,6 @@ <script lang="ts"> import Vue from 'vue'; import Progress from '../../../common/scripts/loading'; -import parse from '../../../common/scripts/parse-search-query'; const limit = 20; @@ -24,8 +19,9 @@ export default Vue.extend({ data() { return { fetching: true, + moreFetching: false, existMore: false, - notes: [], + empty: false, offset: 0 }; }, @@ -47,31 +43,43 @@ export default Vue.extend({ this.fetching = true; Progress.start(); - (this as any).api('notes/search', Object.assign({ - limit: limit + 1 - }, parse(this.q))).then(notes => { - if (notes.length == limit + 1) { - notes.pop(); - this.existMore = true; - } - this.notes = notes; - this.fetching = false; - Progress.done(); - }); + (this.$refs.timeline as any).init(() => new Promise((res, rej) => { + (this as any).api('notes/search', { + limit: limit + 1, + offset: this.offset, + query: this.q + }).then(notes => { + if (notes.length == 0) this.empty = true; + if (notes.length == limit + 1) { + notes.pop(); + this.existMore = true; + } + res(notes); + this.fetching = false; + Progress.done(); + }, rej); + })); }, more() { this.offset += limit; - (this as any).api('notes/search', Object.assign({ + + const promise = (this as any).api('notes/search', { limit: limit + 1, - offset: this.offset - }, parse(this.q))).then(notes => { + offset: this.offset, + query: this.q + }); + + promise.then(notes => { if (notes.length == limit + 1) { notes.pop(); } else { this.existMore = false; } - this.notes = this.notes.concat(notes); + notes.forEach(n => (this.$refs.timeline as any).append(n)); + this.moreFetching = false; }); + + return promise; } } }); diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue index ba9de9f8a6..0b51c8b4ce 100644 --- a/src/client/app/mobile/views/pages/user.vue +++ b/src/client/app/mobile/views/pages/user.vue @@ -64,7 +64,7 @@ <script lang="ts"> import Vue from 'vue'; import * as age from 's-age'; -import parseAcct from '../../../../../acct/parse'; +import parseAcct from '../../../../../misc/acct/parse'; import Progress from '../../../common/scripts/loading'; import XHome from './user/home.vue'; diff --git a/src/client/docs/api/endpoints/notes/create.yaml b/src/client/docs/api/endpoints/notes/create.yaml deleted file mode 100644 index 04ada2ecd5..0000000000 --- a/src/client/docs/api/endpoints/notes/create.yaml +++ /dev/null @@ -1,59 +0,0 @@ -endpoint: "notes/create" - -desc: - ja: "投稿します。" - en: "Compose new note." - -params: - - name: "text" - type: "string" - optional: true - desc: - ja: "投稿の本文" - en: "The text of your note" - - name: "cw" - type: "string" - optional: true - desc: - ja: "コンテンツの警告。このパラメータを指定すると設定したテキストで投稿のコンテンツを隠す事が出来ます。" - en: "Content Warning" - - name: "mediaIds" - type: "id(DriveFile)[]" - optional: true - desc: - ja: "添付するメディア(1~4つ)" - en: "Media you want to attach (1~4)" - - name: "replyId" - type: "id(Note)" - optional: true - desc: - ja: "返信する投稿" - en: "The note you want to reply" - - name: "renoteId" - type: "id(Note)" - optional: true - desc: - ja: "引用する投稿" - en: "The note you want to quote" - - name: "poll" - type: "object" - optional: true - desc: - ja: "投票" - en: "The poll" - defName: "poll" - def: - - name: "choices" - type: "string[]" - optional: false - desc: - ja: "投票の選択肢" - en: "Choices of a poll" - -res: - - name: "createdNote" - type: "entity(Note)" - optional: false - desc: - ja: "作成した投稿" - en: "A note that created" diff --git a/src/client/docs/api/endpoints/notes/timeline.yaml b/src/client/docs/api/endpoints/notes/timeline.yaml deleted file mode 100644 index 71c346f355..0000000000 --- a/src/client/docs/api/endpoints/notes/timeline.yaml +++ /dev/null @@ -1,32 +0,0 @@ -endpoint: "notes/timeline" - -desc: - ja: "タイムラインを取得します。" - en: "Get your timeline." - -params: - - name: "limit" - type: "number" - optional: true - desc: - ja: "取得する最大の数" - - name: "sinceId" - type: "id(Note)" - optional: true - desc: - ja: "指定すると、この投稿を基点としてより新しい投稿を取得します" - - name: "untilId" - type: "id(Note)" - optional: true - desc: - ja: "指定すると、この投稿を基点としてより古い投稿を取得します" - - name: "sinceDate" - type: "number" - optional: true - desc: - ja: "指定した時間を基点としてより新しい投稿を取得します。数値は、1970 年 1 月 1 日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。" - - name: "untilDate" - type: "number" - optional: true - desc: - ja: "指定した時間を基点としてより古い投稿を取得します。数値は、1970 年 1 月 1 日 00:00:00 UTC から指定した日時までの経過時間をミリ秒単位で表します。" diff --git a/src/client/docs/api/endpoints/view.pug b/src/client/docs/api/endpoints/view.pug index f8795c8442..e046b3fc33 100644 --- a/src/client/docs/api/endpoints/view.pug +++ b/src/client/docs/api/endpoints/view.pug @@ -17,7 +17,7 @@ block main p#desc= desc[lang] || desc['ja'] section - h2 %i18n:docs.api.endpoints.params% + h2= i18n('docs.api.endpoints.params') +propTable(params) if paramDefs @@ -28,5 +28,11 @@ block main if res section - h2 %i18n:docs.api.endpoints.res% + h2= i18n('docs.api.endpoints.res') +propTable(res) + + if resDefs + each resDef in resDefs + section(id= resDef.name) + h3= resDef.name + +propTable(resDef.props) diff --git a/src/client/docs/api/entities/note.yaml b/src/client/docs/api/entities/note.yaml index 6fd26543bb..c508dab3db 100644 --- a/src/client/docs/api/entities/note.yaml +++ b/src/client/docs/api/entities/note.yaml @@ -27,8 +27,8 @@ props: type: "string" optional: true desc: - ja: "投稿の本文 (ローカルの場合Markdown風のフォーマット)" - en: "The text of this note (in Markdown like format if local)" + ja: "投稿の本文" + en: "The text of this note" - name: "mediaIds" type: "id(DriveFile)[]" optional: true diff --git a/src/client/docs/api/entities/user.yaml b/src/client/docs/api/entities/user.yaml index cccf42f221..3328734d2b 100644 --- a/src/client/docs/api/entities/user.yaml +++ b/src/client/docs/api/entities/user.yaml @@ -5,169 +5,173 @@ desc: en: "A user." props: - - name: "id" + id: type: "id" optional: false desc: ja: "ユーザーID" en: "The ID of this user" - - name: "createdAt" + + createdAt: type: "date" optional: false desc: ja: "アカウント作成日時" en: "The registered date of this user" - - name: "username" + + username: type: "string" optional: false desc: ja: "ユーザー名" en: "The username of this user" - - name: "description" + + description: type: "string" optional: false desc: ja: "アカウントの説明(自己紹介)" en: "The description of this user" - - name: "avatarId" + + avatarId: type: "id(DriveFile)" optional: true desc: ja: "アバターのID" en: "The ID of the avatar of this user" - - name: "avatarUrl" + + avatarUrl: type: "string" optional: false desc: ja: "アバターのURL" en: "The URL of the avatar of this user" - - name: "bannerId" + + bannerId: type: "id(DriveFile)" optional: true desc: ja: "バナーのID" en: "The ID of the banner of this user" - - name: "bannerUrl" + + bannerUrl: type: "string" optional: false desc: ja: "バナーのURL" en: "The URL of the banner of this user" - - name: "followersCount" + + followersCount: type: "number" optional: false desc: ja: "フォロワーの数" en: "The number of the followers for this user" - - name: "followingCount" + + followingCount: type: "number" optional: false desc: ja: "フォローしているユーザーの数" en: "The number of the following users for this user" - - name: "isFollowing" + + isFollowing: type: "boolean" optional: true desc: ja: "自分がこのユーザーをフォローしているか" - - name: "isFollowed" + + isFollowed: type: "boolean" optional: true desc: ja: "自分がこのユーザーにフォローされているか" - - name: "isMuted" + + isMuted: type: "boolean" optional: true desc: ja: "自分がこのユーザーをミュートしているか" en: "Whether you muted this user" - - name: "notesCount" + + notesCount: type: "number" optional: false desc: ja: "投稿の数" en: "The number of the notes of this user" - - name: "pinnedNote" + + pinnedNote: type: "entity(Note)" optional: true desc: ja: "ピン留めされた投稿" en: "The pinned note of this user" - - name: "pinnedNoteId" + + pinnedNoteId: type: "id(Note)" optional: true desc: ja: "ピン留めされた投稿のID" en: "The ID of the pinned note of this user" - - name: "driveCapacity" + + driveCapacity: type: "number" optional: false desc: ja: "ドライブの容量(bytes)" en: "The capacity of drive of this user (bytes)" - - name: "host" + + host: type: "string | null" optional: false desc: ja: "ホスト (例: example.com:3000)" en: "Host (e.g. example.com:3000)" - - name: "account" + + twitter: type: "object" - optional: false + optional: true desc: - ja: "このサーバーにおけるアカウント" - en: "The account of this user on this server" - defName: "account" - def: - - name: "lastUsedAt" - type: "date" + ja: "連携されているTwitterアカウント情報" + en: "The info of the connected twitter account of this user" + props: + userId: + type: "string" optional: false desc: - ja: "最終利用日時" - en: "The last used date of this user" - - name: "isBot" - type: "boolean" - optional: true + ja: "ユーザーID" + en: "The user ID" + screenName: + type: "string" + optional: false desc: - ja: "botか否か(自己申告であることに留意)" - en: "Whether is bot or not" - - name: "twitter" - type: "object" + ja: "ユーザー名" + en: "The screen name of this user" + + isBot: + type: "boolean" + optional: true + desc: + ja: "botか否か(自己申告であることに留意)" + en: "Whether is bot or not" + + profile: + type: "object" + optional: false + desc: + ja: "プロフィール" + en: "The profile of this user" + props: + location: + type: "string" optional: true desc: - ja: "連携されているTwitterアカウント情報" - en: "The info of the connected twitter account of this user" - defName: "twitter" - def: - - name: "userId" - type: "string" - optional: false - desc: - ja: "ユーザーID" - en: "The user ID" - - name: "screenName" - type: "string" - optional: false - desc: - ja: "ユーザー名" - en: "The screen name of this user" - - name: "profile" - type: "object" - optional: false + ja: "場所" + en: "The location of this user" + birthday: + type: "string" + optional: true desc: - ja: "プロフィール" - en: "The profile of this user" - defName: "profile" - def: - - name: "location" - type: "string" - optional: true - desc: - ja: "場所" - en: "The location of this user" - - name: "birthday" - type: "string" - optional: true - desc: - ja: "誕生日 (YYYY-MM-DD)" - en: "The birthday of this user (YYYY-MM-DD)" + ja: "誕生日 (YYYY-MM-DD)" + en: "The birthday of this user (YYYY-MM-DD)" diff --git a/src/client/docs/api/entities/view.pug b/src/client/docs/api/entities/view.pug index ac938151a7..3f50bfd3bd 100644 --- a/src/client/docs/api/entities/view.pug +++ b/src/client/docs/api/entities/view.pug @@ -10,11 +10,11 @@ block main p#desc= desc[lang] || desc['ja'] section - h2 %i18n:docs.api.entities.properties% + h2= i18n('docs.api.entities.properties') +propTable(props) if propDefs each propDef in propDefs section(id= propDef.name) h3= propDef.name - +propTable(propDef.params) + +propTable(propDef.props) diff --git a/src/client/docs/api/gulpfile.ts b/src/client/docs/api/gulpfile.ts deleted file mode 100644 index 0eb8b88287..0000000000 --- a/src/client/docs/api/gulpfile.ts +++ /dev/null @@ -1,193 +0,0 @@ -/** - * Gulp tasks - */ - -import * as fs from 'fs'; -import * as path from 'path'; -import * as glob from 'glob'; -import * as gulp from 'gulp'; -import * as pug from 'pug'; -import * as yaml from 'js-yaml'; -import * as mkdirp from 'mkdirp'; - -import locales from '../../../../locales'; -import I18nReplacer from '../../../build/i18n'; -import fa from '../../../build/fa'; -import config from './../../../config'; - -import generateVars from '../vars'; - -const langs = Object.keys(locales); - -const kebab = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase(); - -// WIP type -const parseParam = (param: any) => { - const id = param.type.match(/^id\((.+?)\)|^id/); - const entity = param.type.match(/^entity\((.+?)\)/); - const isObject = /^object/.test(param.type); - const isDate = /^date/.test(param.type); - const isArray = /\[\]$/.test(param.type); - if (id) { - param.kind = 'id'; - param.type = 'string'; - param.entity = id[1]; - if (isArray) { - param.type += '[]'; - } - } - if (entity) { - param.kind = 'entity'; - param.type = 'object'; - param.entity = entity[1]; - if (isArray) { - param.type += '[]'; - } - } - if (isObject) { - param.kind = 'object'; - } - if (isDate) { - param.kind = 'date'; - param.type = 'string'; - if (isArray) { - param.type += '[]'; - } - } - - return param; -}; - -const sortParams = (params: Array<{name: string}>) => { - params.sort((a, b) => { - if (a.name < b.name) - return -1; - if (a.name > b.name) - return 1; - return 0; - }); - return params; -}; - -// WIP type -const extractDefs = (params: any[]) => { - let defs: any[] = []; - - params.forEach(param => { - if (param.def) { - defs.push({ - name: param.defName, - params: sortParams(param.def.map((p: any) => parseParam(p))) - }); - - const childDefs = extractDefs(param.def); - - defs = defs.concat(childDefs); - } - }); - - return sortParams(defs); -}; - -gulp.task('doc:api', [ - 'doc:api:endpoints', - 'doc:api:entities' -]); - -gulp.task('doc:api:endpoints', async () => { - const commonVars = await generateVars(); - glob('./src/client/docs/api/endpoints/**/*.yaml', (globErr, files) => { - if (globErr) { - console.error(globErr); - return; - } - //console.log(files); - files.forEach(file => { - const ep: any = yaml.safeLoad(fs.readFileSync(file, 'utf-8')); - const vars = { - endpoint: ep.endpoint, - url: { - host: config.api_url, - path: ep.endpoint - }, - desc: ep.desc, - // @ts-ignore - params: sortParams(ep.params.map(p => parseParam(p))), - paramDefs: extractDefs(ep.params), - // @ts-ignore - res: ep.res ? sortParams(ep.res.map(p => parseParam(p))) : null, - resDefs: ep.res ? extractDefs(ep.res) : null, - }; - langs.forEach(lang => { - pug.renderFile('./src/client/docs/api/endpoints/view.pug', Object.assign({}, vars, { - lang, - title: ep.endpoint, - src: `https://github.com/syuilo/misskey/tree/master/src/client/docs/api/endpoints/${ep.endpoint}.yaml`, - kebab, - common: commonVars - }), (renderErr, html) => { - if (renderErr) { - console.error(renderErr); - return; - } - const i18n = new I18nReplacer(lang); - html = html.replace(i18n.pattern, i18n.replacement); - html = fa(html); - const htmlPath = `./built/client/docs/${lang}/api/endpoints/${ep.endpoint}.html`; - mkdirp(path.dirname(htmlPath), (mkdirErr) => { - if (mkdirErr) { - console.error(mkdirErr); - return; - } - fs.writeFileSync(htmlPath, html, 'utf-8'); - }); - }); - }); - }); - }); -}); - -gulp.task('doc:api:entities', async () => { - const commonVars = await generateVars(); - glob('./src/client/docs/api/entities/**/*.yaml', (globErr, files) => { - if (globErr) { - console.error(globErr); - return; - } - files.forEach(file => { - const entity = yaml.safeLoad(fs.readFileSync(file, 'utf-8')) as any; - const vars = { - name: entity.name, - desc: entity.desc, - // WIP type - props: sortParams(entity.props.map((p: any) => parseParam(p))), - propDefs: extractDefs(entity.props), - }; - langs.forEach(lang => { - pug.renderFile('./src/client/docs/api/entities/view.pug', Object.assign({}, vars, { - lang, - title: entity.name, - src: `https://github.com/syuilo/misskey/tree/master/src/client/docs/api/entities/${kebab(entity.name)}.yaml`, - kebab, - common: commonVars - }), (renderErr, html) => { - if (renderErr) { - console.error(renderErr); - return; - } - const i18n = new I18nReplacer(lang); - html = html.replace(i18n.pattern, i18n.replacement); - html = fa(html); - const htmlPath = `./built/client/docs/${lang}/api/entities/${kebab(entity.name)}.html`; - mkdirp(path.dirname(htmlPath), (mkdirErr) => { - if (mkdirErr) { - console.error(mkdirErr); - return; - } - fs.writeFileSync(htmlPath, html, 'utf-8'); - }); - }); - }); - }); - }); -}); diff --git a/src/client/docs/api/mixins.pug b/src/client/docs/api/mixins.pug index 913135a85f..9e03abefeb 100644 --- a/src/client/docs/api/mixins.pug +++ b/src/client/docs/api/mixins.pug @@ -1,10 +1,9 @@ mixin propTable(props) table.props thead: tr - th %i18n:docs.api.props.name% - th %i18n:docs.api.props.type% - th %i18n:docs.api.props.optional% - th %i18n:docs.api.props.description% + th= i18n('docs.api.props.name') + th= i18n('docs.api.props.type') + th= i18n('docs.api.props.description') tbody each prop in props tr @@ -23,15 +22,10 @@ mixin propTable(props) a(href=`/docs/${lang}/api/entities/${kebab(prop.entity)}`)= prop.entity | ) else if prop.kind == 'object' - if prop.def + if prop.hasDef | ( - a(href=`#${prop.defName}`)= prop.defName + a(href=`#${prop.name}`)= prop.name | ) else if prop.kind == 'date' | (Date) - td.optional - if prop.optional - | %i18n:docs.api.props.yes% - else - | %i18n:docs.api.props.no% - td.desc!= prop.desc[lang] || prop.desc['ja'] + td.desc!= prop.desc ? prop.desc[lang] || prop.desc['ja'] : null diff --git a/src/client/docs/gulpfile.ts b/src/client/docs/gulpfile.ts index 4683a04659..2a95dfbfee 100644 --- a/src/client/docs/gulpfile.ts +++ b/src/client/docs/gulpfile.ts @@ -2,73 +2,14 @@ * Gulp tasks */ -import * as fs from 'fs'; -import * as path from 'path'; -import * as glob from 'glob'; import * as gulp from 'gulp'; -import * as pug from 'pug'; -import * as mkdirp from 'mkdirp'; const stylus = require('gulp-stylus'); const cssnano = require('gulp-cssnano'); -import I18nReplacer from '../../build/i18n'; -import fa from '../../build/fa'; -import generateVars from './vars'; - -require('./api/gulpfile.ts'); - gulp.task('doc', [ - 'doc:docs', - 'doc:api', 'doc:styles' ]); -gulp.task('doc:docs', async () => { - const commonVars = await generateVars(); - - glob('./src/client/docs/**/*.*.pug', (globErr, files) => { - if (globErr) { - console.error(globErr); - return; - } - files.forEach(file => { - const [, name, lang] = file.match(/docs\/(.+?)\.(.+?)\.pug$/); - const vars = { - common: commonVars, - lang: lang, - title: fs.readFileSync(file, 'utf-8').match(/^h1 (.+?)\r?\n/)[1], - src: `https://github.com/syuilo/misskey/tree/master/src/client/docs/${name}.${lang}.pug`, - }; - pug.renderFile(file, vars, (renderErr, content) => { - if (renderErr) { - console.error(renderErr); - return; - } - - pug.renderFile('./src/client/docs/layout.pug', Object.assign({}, vars, { - content - }), (renderErr2, html) => { - if (renderErr2) { - console.error(renderErr2); - return; - } - const i18n = new I18nReplacer(lang); - html = html.replace(i18n.pattern, i18n.replacement); - html = fa(html); - const htmlPath = `./built/client/docs/${lang}/${name}.html`; - mkdirp(path.dirname(htmlPath), (mkdirErr) => { - if (mkdirErr) { - console.error(mkdirErr); - return; - } - fs.writeFileSync(htmlPath, html, 'utf-8'); - }); - }); - }); - }); - }); -}); - gulp.task('doc:styles', () => gulp.src('./src/client/docs/**/*.styl') .pipe(stylus()) diff --git a/src/client/docs/layout.pug b/src/client/docs/layout.pug index 1d9ebcb4cd..4186d2d365 100644 --- a/src/client/docs/layout.pug +++ b/src/client/docs/layout.pug @@ -10,24 +10,24 @@ html(lang= lang) block meta //- FontAwesome style - style #{common.facss} + style #{facss} body nav ul - each doc in common.docs + each doc in docs li: a(href=`/docs/${lang}/${doc.name}`)= doc.title[lang] || doc.title['ja'] section h2 API ul li Entities ul - each entity in common.entities - li: a(href=`/docs/${lang}/api/entities/${common.kebab(entity)}`)= entity + each entity in entities + li: a(href=`/docs/${lang}/api/entities/${kebab(entity)}`)= entity li Endpoints ul - each endpoint in common.endpoints - li: a(href=`/docs/${lang}/api/endpoints/${common.kebab(endpoint)}`)= endpoint + each endpoint in endpoints + li: a(href=`/docs/${lang}/api/endpoints/${kebab(endpoint)}`)= endpoint main article block main @@ -38,4 +38,4 @@ html(lang= lang) p | %i18n:docs.edit-this-page-on-github% a(href=src target="_blank") %i18n:docs.edit-this-page-on-github-link% - small= common.copyright + small= copyright diff --git a/src/client/docs/style.styl b/src/client/docs/style.styl index bc165f8728..3b13617588 100644 --- a/src/client/docs/style.styl +++ b/src/client/docs/style.styl @@ -10,7 +10,7 @@ main margin 0 0 0 256px padding 64px width 100% - max-width 768px + max-width 800px section margin 32px 0 diff --git a/src/client/docs/vars.ts b/src/client/docs/vars.ts deleted file mode 100644 index 93082767e3..0000000000 --- a/src/client/docs/vars.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as fs from 'fs'; -import * as util from 'util'; -import * as glob from 'glob'; -import * as yaml from 'js-yaml'; -import * as licenseChecker from 'license-checker'; -import * as tmp from 'tmp'; - -import { fa } from '../../build/fa'; -import config from '../../config'; -import { licenseHtml } from '../../build/license'; -const constants = require('../../const.json'); - -export default async function(): Promise<{ [key: string]: any }> { - const vars = {} as { [key: string]: any }; - - const endpoints = glob.sync('./src/client/docs/api/endpoints/**/*.yaml'); - vars['endpoints'] = endpoints.map(ep => { - const _ep = yaml.safeLoad(fs.readFileSync(ep, 'utf-8')) as any; - return _ep.endpoint; - }); - - const entities = glob.sync('./src/client/docs/api/entities/**/*.yaml'); - vars['entities'] = entities.map(x => { - const _x = yaml.safeLoad(fs.readFileSync(x, 'utf-8')) as any; - return _x.name; - }); - - const docs = glob.sync('./src/client/docs/**/*.*.pug'); - vars['docs'] = {}; - docs.forEach(x => { - const [, name, lang] = x.match(/docs\/(.+?)\.(.+?)\.pug$/); - if (vars['docs'][name] == null) { - vars['docs'][name] = { - name, - title: {} - }; - } - vars['docs'][name]['title'][lang] = fs.readFileSync(x, 'utf-8').match(/^h1 (.+?)\r?\n/)[1]; - }); - - vars['kebab'] = (string: string) => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase(); - - vars['config'] = config; - - vars['copyright'] = constants.copyright; - - vars['facss'] = fa.dom.css(); - - vars['license'] = licenseHtml; - - const tmpObj = tmp.fileSync(); - fs.writeFileSync(tmpObj.name, JSON.stringify({ - licenseText: '' - }), 'utf-8'); - const dependencies = await util.promisify(licenseChecker.init).bind(licenseChecker)({ - start: __dirname + '/../../../', - customPath: tmpObj.name - }); - tmpObj.removeCallback(); - - vars['dependencies'] = dependencies; - - return vars; -} |