diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c45b02b5e..1755ce43b4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,22 @@ If you encounter any problems with updating, please try the following: 1. `npm run clean` or `npm run cleanall` 2. Retry update (Don't forget `npm i`) +11.2.0 (2019/04/18) +------------------- +### Improvements +* 検索で日付(日時)を入力するとタイムラインをその時点まで遡るように +* APIコンソールでエンドポイントをサジェストするように +* モバイル版でドライブのメニューを使いやすく +* サイレンス時に確認を表示するように +* ユーザーメニューでブロックなどの操作を行う時に確認するように + +### Fixes +* アプリケーション連携画面でパーミッションが表示されない問題を修正 +* アンケートウィジットでもMFMを使用するように +* フォローしてないユーザーのホーム投稿がSTLに流れてくる問題を修正 +* モバイル版でウィジェットを設定できない問題を修正 +* スプラッシュがクリックに反応するように + 11.1.6 (2019/04/18) ------------------- ### Fixes diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index eb87d4f204..a475bc2c16 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -35,6 +35,7 @@ common: signup: "新規登録" signout: "ログアウト" reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?" + fetching-as-ap-object: "連合に照会中" got-it: "わかった" customization-tips: @@ -527,8 +528,12 @@ common/views/components/user-menu.vue: mention: "メンション" mute: "ミュート" unmute: "ミュート解除" + mute-confirm: "このユーザーをミュートしますか?" + unmute-confirm: "このユーザーをミュート解除しますか?" block: "ブロック" unblock: "ブロック解除" + block-confirm: "このユーザーをブロックしますか?" + unblock-confirm: "このユーザーをブロック解除しますか?" push-to-list: "リストに追加" select-list: "リストを選択してください" report-abuse: "スパムを報告" @@ -536,8 +541,12 @@ common/views/components/user-menu.vue: report-abuse-reported: "管理者に報告されました。ご協力ありがとうございました。" silence: "サイレンス" unsilence: "サイレンス解除" + silence-confirm: "このユーザーをサイレンスしますか?" + unsilence-confirm: "このユーザーをサイレンス解除しますか?" suspend: "凍結" unsuspend: "凍結解除" + suspend-confirm: "このユーザーを凍結しますか?" + unsuspend-confirm: "このユーザーを凍結解除しますか?" common/views/components/poll.vue: vote-to: "「{}」に投票する" @@ -739,6 +748,10 @@ common/views/components/user-list-editor.vue: delete-are-you-sure: "リスト「$1」を削除しますか?" deleted: "削除しました" +common/views/components/user-lists.vue: + create-list: "リストを作成" + list-name: "リスト名" + common/views/widgets/broadcast.vue: fetching: "確認中" no-broadcasts: "お知らせはありません" @@ -1145,8 +1158,6 @@ desktop/views/components/received-follow-requests-window.vue: desktop/views/components/user-lists-window.vue: title: "リスト" - create-list: "リストを作成" - list-name: "リスト名" desktop/views/components/user-preview.vue: notes: "投稿" @@ -1336,7 +1347,9 @@ admin/views/users.vue: unsuspend-confirm: "凍結を解除しますか?" unsuspended: "凍結を解除しました" make-silence: "サイレンス" + silence-confirm: "サイレンスしますか?" unmake-silence: "サイレンスの解除" + unsilence-confirm: "サイレンスを解除しますか?" verify: "公式アカウントにする" verify-confirm: "公式アカウントにしますか?" verified: "公式アカウントにしました" @@ -1573,12 +1586,11 @@ mobile/views/components/drive.vue: file-count: "ファイル" nothing-in-drive: "ドライブには何もありません" folder-is-empty: "このフォルダは空です" - prompt: "何をしますか?(数字を入力してください): <1 → ファイルをアップロード | 2 → ファイルをURLでアップロード | 3 → フォルダ作成 | 4 → このフォルダ名を変更 | 5 → このフォルダを移動 | 6 → このフォルダを削除>" - deletion-alert: "ごめんなさい!フォルダの削除は未実装です...。" folder-name: "フォルダー名" here-is-root: "現在いる場所はルートで、フォルダではありません。" url-prompt: "アップロードしたいファイルのURL" uploading: "アップロードをリクエストしました。アップロードが完了するまで時間がかかる場合があります。" + folder-name-cannot-empty: "フォルダ名を空白にすることはできません。" mobile/views/components/drive-file-chooser.vue: select-file: "ファイルを選択" @@ -1668,9 +1680,17 @@ mobile/views/components/ui.nav.vue: admin: "管理" about: "Misskeyについて" +mobile/views/pages/drive.vue: + contextmenu: + upload: "ファイルをアップロード" + url-upload: "ファイルをURLでアップロード" + create-folder: "フォルダーを作成" + rename-folder: "フォルダー名を変更" + move-folder: "このフォルダを移動" + delete-folder: "このフォルダを削除" + mobile/views/pages/user-lists.vue: title: "リスト" - enter-list-name: "リスト名を入力してください" mobile/views/pages/signup.vue: lets-start: "📦 始めましょう" diff --git a/package.json b/package.json index db2d9d0eac..409bdda154 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "misskey", "author": "syuilo ", - "version": "11.1.6", + "version": "11.2.0", "codename": "daybreak", "repository": { "type": "git", diff --git a/src/client/app/admin/views/users.vue b/src/client/app/admin/views/users.vue index 0f46b564a9..2d6aef3371 100644 --- a/src/client/app/admin/views/users.vue +++ b/src/client/app/admin/views/users.vue @@ -232,6 +232,8 @@ export default Vue.extend({ }, async silenceUser() { + if (!await this.getConfirmed(this.$t('silence-confirm'))) return; + const process = async () => { await this.$root.api('admin/silence-user', { userId: this.user.id }); this.$root.dialog({ @@ -251,6 +253,8 @@ export default Vue.extend({ }, async unsilenceUser() { + if (!await this.getConfirmed(this.$t('unsilence-confirm'))) return; + const process = async () => { await this.$root.api('admin/unsilence-user', { userId: this.user.id }); this.$root.dialog({ diff --git a/src/client/app/common/scripts/search.ts b/src/client/app/common/scripts/search.ts new file mode 100644 index 0000000000..c44581817b --- /dev/null +++ b/src/client/app/common/scripts/search.ts @@ -0,0 +1,64 @@ +import { faHistory } from '@fortawesome/free-solid-svg-icons'; + +export async function search(v: any, q: string) { + q = q.trim(); + + if (q.startsWith('@')) { + v.$router.push(`/${q}`); + return; + } + + if (q.startsWith('#')) { + v.$router.push(`/tags/${encodeURIComponent(q.substr(1))}`); + return; + } + + // like 2018/03/12 + if (/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}/.test(q.replace(/-/g, '/'))) { + const date = new Date(q.replace(/-/g, '/')); + + // 日付しか指定されてない場合、例えば 2018/03/12 ならユーザーは + // 2018/03/12 のコンテンツを「含む」結果になることを期待するはずなので + // 23時間59分進める(そのままだと 2018/03/12 00:00:00 「まで」の + // 結果になってしまい、2018/03/12 のコンテンツは含まれない) + if (q.replace(/-/g, '/').match(/^[0-9]{4}\/[0-9]{2}\/[0-9]{2}$/)) { + date.setHours(23, 59, 59, 999); + } + + v.$root.$emit('warp', date); + v.$root.dialog({ + icon: faHistory, + splash: true, + }); + return; + } + + if (q.startsWith('https://')) { + const dialog = v.$root.dialog({ + type: 'waiting', + text: v.$t('@.fetching-as-ap-object'), + showOkButton: false, + showCancelButton: false, + cancelableByBgClick: false + }); + + try { + const res = await v.$root.api('ap/show', { + uri: q + }); + dialog.close(); + if (res.type == 'User') { + v.$router.push(`/@${res.object.username}@${res.object.host}`); + } else if (res.type == 'Note') { + v.$router.push(`/notes/${res.object.id}`); + } + } catch (e) { + dialog.close(); + // TODO: Show error + } + + return; + } + + v.$router.push(`/search?q=${encodeURIComponent(q)}`); +} diff --git a/src/client/app/common/views/components/dialog.vue b/src/client/app/common/views/components/dialog.vue index 6a074769d8..c1ee7958c0 100644 --- a/src/client/app/common/views/components/dialog.vue +++ b/src/client/app/common/views/components/dialog.vue @@ -6,7 +6,17 @@ @@ -55,10 +65,21 @@ export default Vue.extend({ user: { required: false }, + icon: { + required: false + }, + showOkButton: { + type: Boolean, + default: true + }, showCancelButton: { type: Boolean, default: false }, + cancelableByBgClick: { + type: Boolean, + default: true + }, splash: { type: Boolean, default: false @@ -69,22 +90,11 @@ export default Vue.extend({ return { inputValue: this.input && this.input.default ? this.input.default : null, userInputValue: null, - selectedValue: null + selectedValue: null, + faTimesCircle, faQuestionCircle }; }, - computed: { - icon(): any { - switch (this.type) { - case 'success': return 'check'; - case 'error': return faTimesCircle; - case 'warning': return 'exclamation-triangle'; - case 'info': return 'info-circle'; - case 'question': return faQuestionCircle; - } - } - }, - mounted() { this.$nextTick(() => { (this.$refs.bg as any).style.pointerEvents = 'auto'; @@ -113,6 +123,8 @@ export default Vue.extend({ methods: { async ok() { + if (!this.showOkButton) return; + if (this.user) { const user = await this.$root.api('users/show', parseAcct(this.userInputValue)); if (user) { @@ -156,7 +168,9 @@ export default Vue.extend({ }, onBgClick() { - this.cancel(); + if (this.cancelableByBgClick) { + this.cancel(); + } }, onInputKeydown(e) { @@ -183,9 +197,6 @@ export default Vue.extend({ height 100% &.splash - &, * - pointer-events none !important - > .main min-width 0 width initial @@ -243,7 +254,7 @@ export default Vue.extend({ margin-top 8px > .body - margin 16px 0 + margin 16px 0 0 0 > .buttons margin-top 16px diff --git a/src/client/app/common/views/components/messaging-room.vue b/src/client/app/common/views/components/messaging-room.vue index 83a0c463e0..a8980e068f 100644 --- a/src/client/app/common/views/components/messaging-room.vue +++ b/src/client/app/common/views/components/messaging-room.vue @@ -6,7 +6,7 @@

{{ $t('@.loading') }}

{{ $t('empty') }}

-

{{ $t('no-history') }}

+

{{ $t('no-history') }}

@@ -35,6 +35,7 @@ import XMessage from './messaging-room.message.vue'; import XForm from './messaging-room.form.vue'; import { url } from '../../../config'; import { faArrowCircleDown } from '@fortawesome/free-solid-svg-icons'; +import { faFlag } from '@fortawesome/free-regular-svg-icons'; export default Vue.extend({ i18n: i18n('common/views/components/messaging-room.vue'), @@ -54,7 +55,7 @@ export default Vue.extend({ connection: null, showIndicator: false, timer: null, - faArrowCircleDown + faArrowCircleDown, faFlag }; }, diff --git a/src/client/app/common/views/components/settings/api.vue b/src/client/app/common/views/components/settings/api.vue index 4f1b755857..74e3eb0661 100644 --- a/src/client/app/common/views/components/settings/api.vue +++ b/src/client/app/common/views/components/settings/api.vue @@ -14,7 +14,7 @@
{{ $t('console.title') }}
- + {{ $t('console.endpoint') }} @@ -39,15 +39,23 @@ import * as JSON5 from 'json5'; export default Vue.extend({ i18n: i18n('common/views/components/api-settings.vue'), + data() { return { endpoint: '', body: '{}', res: null, - sending: false + sending: false, + endpoints: [] }; }, + created() { + this.$root.api('endpoints').then(endpoints => { + this.endpoints = endpoints; + }); + }, + methods: { regenerateToken() { this.$root.dialog({ diff --git a/src/client/app/common/views/components/ui/input.vue b/src/client/app/common/views/components/ui/input.vue index ae9ce249de..bcb87398ba 100644 --- a/src/client/app/common/views/components/ui/input.vue +++ b/src/client/app/common/views/components/ui/input.vue @@ -23,6 +23,7 @@ @focus="focused = true" @blur="focused = false" @keydown="$emit('keydown', $event)" + :list="id" > + + + + + + diff --git a/src/client/app/common/views/components/user-menu.vue b/src/client/app/common/views/components/user-menu.vue index a95f7a9225..0af0fdb7e4 100644 --- a/src/client/app/common/views/components/user-menu.vue +++ b/src/client/app/common/views/components/user-menu.vue @@ -89,8 +89,10 @@ export default Vue.extend({ }); }, - toggleMute() { + async toggleMute() { if (this.user.isMuted) { + if (!await this.getConfirmed(this.$t('unmute-confirm'))) return; + this.$root.api('mute/delete', { userId: this.user.id }).then(() => { @@ -102,6 +104,8 @@ export default Vue.extend({ }); }); } else { + if (!await this.getConfirmed(this.$t('mute-confirm'))) return; + this.$root.api('mute/create', { userId: this.user.id }).then(() => { @@ -115,8 +119,10 @@ export default Vue.extend({ } }, - toggleBlock() { + async toggleBlock() { if (this.user.isBlocking) { + if (!await this.getConfirmed(this.$t('unblock-confirm'))) return; + this.$root.api('blocking/delete', { userId: this.user.id }).then(() => { @@ -128,6 +134,8 @@ export default Vue.extend({ }); }); } else { + if (!await this.getConfirmed(this.$t('block-confirm'))) return; + this.$root.api('blocking/create', { userId: this.user.id }).then(() => { @@ -164,7 +172,9 @@ export default Vue.extend({ }); }, - toggleSilence() { + async toggleSilence() { + if (!await this.getConfirmed(this.$t(this.user.isSilenced ? 'unsilence-confirm' : 'silence-confirm'))) return; + this.$root.api(this.user.isSilenced ? 'admin/unsilence-user' : 'admin/silence-user', { userId: this.user.id }).then(() => { @@ -181,7 +191,9 @@ export default Vue.extend({ }); }, - toggleSuspend() { + async toggleSuspend() { + if (!await this.getConfirmed(this.$t(this.user.isSuspended ? 'unsuspend-confirm' : 'suspend-confirm'))) return; + this.$root.api(this.user.isSuspended ? 'admin/unsuspend-user' : 'admin/suspend-user', { userId: this.user.id }).then(() => { @@ -196,7 +208,18 @@ export default Vue.extend({ text: e }); }); - } + }, + + async getConfirmed(text: string): Promise { + const confirm = await this.$root.dialog({ + type: 'warning', + showCancelButton: true, + title: 'confirm', + text, + }); + + return !confirm.canceled; + }, } }); diff --git a/src/client/app/desktop/views/components/notes.vue b/src/client/app/desktop/views/components/notes.vue index b2c2d9e861..e15645089d 100644 --- a/src/client/app/desktop/views/components/notes.vue +++ b/src/client/app/desktop/views/components/notes.vue @@ -123,7 +123,7 @@ export default Vue.extend({ }, fetchMore() { - if (!this.more || this.moreFetching) return; + if (!this.more || this.moreFetching || this.notes.length === 0) return; this.moreFetching = true; this.makePromise(this.notes[this.notes.length - 1].id).then(x => { this.notes = this.notes.concat(x.notes); diff --git a/src/client/app/desktop/views/components/ui.header.account.vue b/src/client/app/desktop/views/components/ui.header.account.vue index effd0ef0fd..7f9decfdcd 100644 --- a/src/client/app/desktop/views/components/ui.header.account.vue +++ b/src/client/app/desktop/views/components/ui.header.account.vue @@ -90,9 +90,8 @@ import Vue from 'vue'; import i18n from '../../../i18n'; import MkUserListsWindow from './user-lists-window.vue'; -import MkUserListWindow from './user-list-window.vue'; import MkFollowRequestsWindow from './received-follow-requests-window.vue'; -import MkSettingsWindow from './settings-window.vue'; +// import MkSettingsWindow from './settings-window.vue'; import MkDriveWindow from './drive-window.vue'; import contains from '../../../common/scripts/contains'; import { faHome, faColumns } from '@fortawesome/free-solid-svg-icons'; @@ -143,12 +142,7 @@ export default Vue.extend({ }, list() { this.close(); - const w = this.$root.new(MkUserListsWindow); - w.$once('choosen', list => { - this.$root.new(MkUserListWindow, { - list - }); - }); + this.$root.new(MkUserListsWindow); }, followRequests() { this.close(); diff --git a/src/client/app/desktop/views/components/ui.header.search.vue b/src/client/app/desktop/views/components/ui.header.search.vue index 4ade74bc64..0cf5ca6f32 100644 --- a/src/client/app/desktop/views/components/ui.header.search.vue +++ b/src/client/app/desktop/views/components/ui.header.search.vue @@ -9,6 +9,7 @@ - diff --git a/src/client/app/desktop/views/home/timeline.core.vue b/src/client/app/desktop/views/home/timeline.core.vue index 12806365d4..654a1cc434 100644 --- a/src/client/app/desktop/views/home/timeline.core.vue +++ b/src/client/app/desktop/views/home/timeline.core.vue @@ -53,6 +53,12 @@ export default Vue.extend({ }, created() { + this.$root.$on('warp', this.warp); + this.$once('hook:beforeDestroy', () => { + this.$root.$off('warp', this.warp); + this.connection.dispose(); + }); + const prepend = note => { (this.$refs.timeline as any).prepend(note); }; @@ -124,13 +130,14 @@ export default Vue.extend({ }); }, - beforeDestroy() { - this.connection.dispose(); - }, - methods: { focus() { (this.$refs.timeline as any).focus(); + }, + + warp(date) { + this.date = date; + (this.$refs.timeline as any).reload(); } } }); diff --git a/src/client/app/desktop/views/home/user/user.timeline.vue b/src/client/app/desktop/views/home/user/user.timeline.vue index eafac4c7fd..0435d67dc7 100644 --- a/src/client/app/desktop/views/home/user/user.timeline.vue +++ b/src/client/app/desktop/views/home/user/user.timeline.vue @@ -36,7 +36,8 @@ export default Vue.extend({ includeReplies: this.mode == 'with-replies', includeMyRenotes: this.mode != 'my-posts', withFiles: this.mode == 'with-media', - untilDate: cursor ? cursor : new Date().getTime() + 1000 * 86400 * 365 + untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined), + untilId: cursor ? cursor : undefined }).then(notes => { if (notes.length == fetchLimit + 1) { notes.pop(); @@ -62,10 +63,11 @@ export default Vue.extend({ mounted() { document.addEventListener('keydown', this.onDocumentKeydown); - }, - - beforeDestroy() { - document.removeEventListener('keydown', this.onDocumentKeydown); + this.$root.$on('warp', this.warp); + this.$once('hook:beforeDestroy', () => { + this.$root.$off('warp', this.warp); + document.removeEventListener('keydown', this.onDocumentKeydown); + }); }, methods: { diff --git a/src/client/app/desktop/views/widgets/polls.vue b/src/client/app/desktop/views/widgets/polls.vue index 3567cae5ed..c77762ecdf 100644 --- a/src/client/app/desktop/views/widgets/polls.vue +++ b/src/client/app/desktop/views/widgets/polls.vue @@ -11,7 +11,9 @@
-

{{ poll.text }}

+

+ +

diff --git a/src/client/app/desktop/views/widgets/timemachine.vue b/src/client/app/desktop/views/widgets/timemachine.vue index 22a4120403..854b01c13e 100644 --- a/src/client/app/desktop/views/widgets/timemachine.vue +++ b/src/client/app/desktop/views/widgets/timemachine.vue @@ -14,7 +14,7 @@ export default define({ }).extend({ methods: { chosen(date) { - this.$emit('chosen', date); + this.$root.$emit('warp', date); }, func() { if (this.props.design == 5) { diff --git a/src/client/app/init.ts b/src/client/app/init.ts index 4b9f341612..230d8c3f1e 100644 --- a/src/client/app/init.ts +++ b/src/client/app/init.ts @@ -458,10 +458,14 @@ export default (callback: (launch: (router: VueRouter) => [Vue, MiOS], os: MiOS) }, dialog(opts) { const vm = this.new(Dialog, opts); - return new Promise((res) => { + const p: any = new Promise((res) => { vm.$once('ok', result => res({ canceled: false, result })); vm.$once('cancel', () => res({ canceled: true })); }); + p.close = () => { + vm.close(); + }; + return p; } }, router, diff --git a/src/client/app/mobile/views/components/drive.vue b/src/client/app/mobile/views/components/drive.vue index 4772333862..b79c0b3806 100644 --- a/src/client/app/mobile/views/components/drive.vue +++ b/src/client/app/mobile/views/components/drive.vue @@ -379,43 +379,30 @@ export default Vue.extend({ }); }, - openContextMenu() { - const fn = window.prompt(this.$t('prompt')); - if (fn == null || fn == '') return; - switch (fn) { - case '1': - this.selectLocalFile(); - break; - case '2': - this.urlUpload(); - break; - case '3': - this.createFolder(); - break; - case '4': - this.renameFolder(); - break; - case '5': - this.moveFolder(); - break; - case '6': - this.deleteFolder(); - break; - } - }, - selectLocalFile() { (this.$refs.file as any).click(); }, createFolder() { - const name = window.prompt(this.$t('folder-name')); - if (name == null || name == '') return; - this.$root.api('drive/folders/create', { - name: name, - parentId: this.folder ? this.folder.id : undefined - }).then(folder => { - this.addFolder(folder, true); + this.$root.dialog({ + title: this.$t('folder-name') + input: { + default: this.folder.name + } + }).then(({ result: name }) => { + if (!name) { + this.$root.dialog({ + type: 'error', + text: this.$t('folder-name-cannot-empty') + }); + return; + } + this.$root.api('drive/folders/create', { + name: name, + parentId: this.folder ? this.folder.id : undefined + }).then(folder => { + this.addFolder(folder, true); + }); }); }, @@ -427,13 +414,25 @@ export default Vue.extend({ }); return; } - const name = window.prompt(this.$t('folder-name'), this.folder.name); - if (name == null || name == '') return; - this.$root.api('drive/folders/update', { - name: name, - folderId: this.folder.id - }).then(folder => { - this.cd(folder); + this.$root.dialog({ + title: this.$t('folder-name') + input: { + default: this.folder.name + } + }).then(({ result: name }) => { + if (!name) { + this.$root.dialog({ + type: 'error', + text: this.$t('cannot-empty') + }); + return; + } + this.$root.api('drive/folders/update', { + name: name, + folderId: this.folder.id + }).then(folder => { + this.cd(folder); + }); }); }, diff --git a/src/client/app/mobile/views/components/notes.vue b/src/client/app/mobile/views/components/notes.vue index 5b9652e1e2..36f10d9d35 100644 --- a/src/client/app/mobile/views/components/notes.vue +++ b/src/client/app/mobile/views/components/notes.vue @@ -124,7 +124,7 @@ export default Vue.extend({ }, fetchMore() { - if (!this.more || this.moreFetching) return; + if (!this.more || this.moreFetching || this.notes.length === 0) return; this.moreFetching = true; this.makePromise(this.notes[this.notes.length - 1].id).then(x => { this.notes = this.notes.concat(x.notes); diff --git a/src/client/app/mobile/views/components/ui.nav.vue b/src/client/app/mobile/views/components/ui.nav.vue index 169c7fc07d..b26e2380ab 100644 --- a/src/client/app/mobile/views/components/ui.nav.vue +++ b/src/client/app/mobile/views/components/ui.nav.vue @@ -66,6 +66,7 @@ import i18n from '../../../i18n'; import { lang } from '../../../config'; import { faNewspaper, faHashtag, faHome, faColumns } from '@fortawesome/free-solid-svg-icons'; import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons'; +import { search } from '../../../common/scripts/search'; export default Vue.extend({ i18n: i18n('mobile/views/components/ui.nav.vue'), @@ -133,29 +134,10 @@ export default Vue.extend({ }).then(async ({ canceled, result: query }) => { if (canceled) return; - const q = query.trim(); - if (q.startsWith('@')) { - this.$router.push(`/${q}`); - } else if (q.startsWith('#')) { - this.$router.push(`/tags/${encodeURIComponent(q.substr(1))}`); - } else if (q.startsWith('https://')) { - this.searching = true; - try { - const res = await this.$root.api('ap/show', { - uri: q - }); - if (res.type == 'User') { - this.$router.push(`/@${res.object.username}@${res.object.host}`); - } else if (res.type == 'Note') { - this.$router.push(`/notes/${res.object.id}`); - } - } catch (e) { - // TODO - } + this.searching = true; + search(this, query).finally(() => { this.searching = false; - } else { - this.$router.push(`/search?q=${encodeURIComponent(q)}`); - } + }); }); }, diff --git a/src/client/app/mobile/views/components/user-list-timeline.vue b/src/client/app/mobile/views/components/user-list-timeline.vue index c3f09c9b15..73bc6f3034 100644 --- a/src/client/app/mobile/views/components/user-list-timeline.vue +++ b/src/client/app/mobile/views/components/user-list-timeline.vue @@ -15,10 +15,12 @@ export default Vue.extend({ data() { return { connection: null, + date: null, makePromise: cursor => this.$root.api('notes/user-list-timeline', { listId: this.list.id, limit: fetchLimit + 1, untilId: cursor ? cursor : undefined, + untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined), includeMyRenotes: this.$store.state.settings.showMyRenotes, includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes, includeLocalRenotes: this.$store.state.settings.showLocalRenotes @@ -45,6 +47,11 @@ export default Vue.extend({ mounted() { this.init(); + + this.$root.$on('warp', this.warp); + this.$once('hook:beforeDestroy', () => { + this.$root.$off('warp', this.warp); + }); }, beforeDestroy() { @@ -73,6 +80,11 @@ export default Vue.extend({ onUserRemoved() { (this.$refs.timeline as any).reload(); + }, + + warp(date) { + this.date = date; + (this.$refs.timeline as any).reload(); } } }); diff --git a/src/client/app/mobile/views/components/user-timeline.vue b/src/client/app/mobile/views/components/user-timeline.vue index 8ae9794b75..8c7c8c6d7d 100644 --- a/src/client/app/mobile/views/components/user-timeline.vue +++ b/src/client/app/mobile/views/components/user-timeline.vue @@ -17,11 +17,13 @@ export default Vue.extend({ data() { return { + date: null, makePromise: cursor => this.$root.api('users/notes', { userId: this.user.id, limit: fetchLimit + 1, withFiles: this.withMedia, - untilDate: cursor ? cursor : new Date().getTime() + 1000 * 86400 * 365 + untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined), + untilId: cursor ? cursor : undefined }).then(notes => { if (notes.length == fetchLimit + 1) { notes.pop(); @@ -37,6 +39,20 @@ export default Vue.extend({ } }) }; + }, + + created() { + this.$root.$on('warp', this.warp); + this.$once('hook:beforeDestroy', () => { + this.$root.$off('warp', this.warp); + }); + }, + + methods: { + warp(date) { + this.date = date; + (this.$refs.timeline as any).reload(); + } } }); diff --git a/src/client/app/mobile/views/pages/drive.vue b/src/client/app/mobile/views/pages/drive.vue index d2f51dc899..05163c6ed9 100644 --- a/src/client/app/mobile/views/pages/drive.vue +++ b/src/client/app/mobile/views/pages/drive.vue @@ -5,7 +5,7 @@ - + import('../components/drive.vue').then(m => m.default), }, @@ -63,9 +66,6 @@ export default Vue.extend({ (this.$refs as any).browser.goRoot(true); } }, - fn() { - (this.$refs as any).browser.openContextMenu(); - }, onMoveRoot(silent) { const title = `${this.$root.instanceName} Drive`; @@ -104,6 +104,42 @@ export default Vue.extend({ this.file = file; this.folder = null; + }, + openContextMenu() { + this.$root.new(XMenu, { + items: [{ + type: 'item', + text: this.$t('contextmenu.upload'), + icon: 'upload', + action: this.$refs.browser.selectLocalFile + }, { + type: 'item', + text: this.$t('contextmenu.url-upload'), + icon: faCloudUploadAlt, + action: this.$refs.browser.urlUpload + }, { + type: 'item', + text: this.$t('contextmenu.create-folder'), + icon: ['far', 'folder'], + action: this.$refs.browser.createFolder + }, ...(this.folder ? [{ + type: 'item', + text: this.$t('contextmenu.rename-folder'), + icon: 'i-cursor', + action: this.$refs.browser.renameFolder + }, { + type: 'item', + text: this.$t('contextmenu.move-folder'), + icon: ['far', 'folder-open'], + action: this.$refs.browser.moveFolder + }, { + type: 'item', + text: this.$t('contextmenu.delete-folder'), + icon: faTrashAlt, + action: this.$refs.browser.deleteFolder + }] : [])], + source: this.$refs.contextSource, + }); } } }); diff --git a/src/client/app/mobile/views/pages/home.timeline.vue b/src/client/app/mobile/views/pages/home.timeline.vue index 809158dd29..e0754e88b4 100644 --- a/src/client/app/mobile/views/pages/home.timeline.vue +++ b/src/client/app/mobile/views/pages/home.timeline.vue @@ -54,6 +54,12 @@ export default Vue.extend({ }, created() { + this.$root.$on('warp', this.warp); + this.$once('hook:beforeDestroy', () => { + this.$root.$off('warp', this.warp); + this.connection.dispose(); + }); + const prepend = note => { (this.$refs.timeline as any).prepend(note); }; @@ -125,10 +131,6 @@ export default Vue.extend({ }); }, - beforeDestroy() { - this.connection.dispose(); - }, - methods: { focus() { (this.$refs.timeline as any).focus(); diff --git a/src/client/app/mobile/views/pages/user-lists.vue b/src/client/app/mobile/views/pages/user-lists.vue index 49006f41f6..a3e9bd78ba 100644 --- a/src/client/app/mobile/views/pages/user-lists.vue +++ b/src/client/app/mobile/views/pages/user-lists.vue @@ -1,20 +1,15 @@