mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-03 23:14:13 +00:00 
			
		
		
		
	Refactoring
This commit is contained in:
		
							parent
							
								
									262d5ead51
								
							
						
					
					
						commit
						5511b6e013
					
				
					 41 changed files with 764 additions and 1408 deletions
				
			
		| 
						 | 
					@ -144,8 +144,6 @@ export default prop => ({
 | 
				
			||||||
					break;
 | 
										break;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.$emit(`update:${prop}`, this.$_ns_note_);
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										169
									
								
								src/client/app/common/scripts/paging.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/client/app/common/scripts/paging.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,169 @@
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default (opts) => ({
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								items: [],
 | 
				
			||||||
 | 
								queue: [],
 | 
				
			||||||
 | 
								fetching: true,
 | 
				
			||||||
 | 
								moreFetching: false,
 | 
				
			||||||
 | 
								inited: false,
 | 
				
			||||||
 | 
								more: false
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						computed: {
 | 
				
			||||||
 | 
							empty(): boolean {
 | 
				
			||||||
 | 
								return this.items.length == 0 && !this.fetching && this.inited;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							error(): boolean {
 | 
				
			||||||
 | 
								return !this.fetching && !this.inited;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						watch: {
 | 
				
			||||||
 | 
							queue(x) {
 | 
				
			||||||
 | 
								if (opts.onQueueChanged) opts.onQueueChanged(this, x);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							pagination() {
 | 
				
			||||||
 | 
								this.init();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						created() {
 | 
				
			||||||
 | 
							opts.displayLimit = opts.displayLimit || 30;
 | 
				
			||||||
 | 
							this.init();
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							if (opts.captureWindowScroll) {
 | 
				
			||||||
 | 
								this.isScrollTop = () => {
 | 
				
			||||||
 | 
									return window.scrollY <= 8;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								window.addEventListener('scroll', this.onWindowScroll, { passive: true });
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						beforeDestroy() {
 | 
				
			||||||
 | 
							if (opts.captureWindowScroll) {
 | 
				
			||||||
 | 
								window.removeEventListener('scroll', this.onWindowScroll);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							updateItem(i, item) {
 | 
				
			||||||
 | 
								Vue.set((this as any).items, i, item);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							reload() {
 | 
				
			||||||
 | 
								this.queue = [];
 | 
				
			||||||
 | 
								this.items = [];
 | 
				
			||||||
 | 
								this.init();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async init() {
 | 
				
			||||||
 | 
								this.fetching = true;
 | 
				
			||||||
 | 
								let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params;
 | 
				
			||||||
 | 
								if (params && params.then) params = await params;
 | 
				
			||||||
 | 
								await this.$root.api(this.pagination.endpoint, {
 | 
				
			||||||
 | 
									limit: (this.pagination.limit || 10) + 1,
 | 
				
			||||||
 | 
									...params
 | 
				
			||||||
 | 
								}).then(x => {
 | 
				
			||||||
 | 
									if (x.length == (this.pagination.limit || 10) + 1) {
 | 
				
			||||||
 | 
										x.pop();
 | 
				
			||||||
 | 
										this.items = x;
 | 
				
			||||||
 | 
										this.more = true;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										this.items = x;
 | 
				
			||||||
 | 
										this.more = false;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									this.inited = true;
 | 
				
			||||||
 | 
									this.fetching = false;
 | 
				
			||||||
 | 
								}, e => {
 | 
				
			||||||
 | 
									this.fetching = false;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							async fetchMore() {
 | 
				
			||||||
 | 
								if (!this.more || this.moreFetching || this.items.length === 0) return;
 | 
				
			||||||
 | 
								this.moreFetching = true;
 | 
				
			||||||
 | 
								let params = typeof this.pagination.params === 'function' ? this.pagination.params(false) : this.pagination.params;
 | 
				
			||||||
 | 
								if (params && params.then) params = await params;
 | 
				
			||||||
 | 
								await this.$root.api(this.pagination.endpoint, {
 | 
				
			||||||
 | 
									limit: (this.pagination.limit || 10) + 1,
 | 
				
			||||||
 | 
									untilId: this.items[this.items.length - 1].id,
 | 
				
			||||||
 | 
									...params
 | 
				
			||||||
 | 
								}).then(x => {
 | 
				
			||||||
 | 
									if (x.length == (this.pagination.limit || 10) + 1) {
 | 
				
			||||||
 | 
										x.pop();
 | 
				
			||||||
 | 
										this.items = this.items.concat(x);
 | 
				
			||||||
 | 
										this.more = true;
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										this.items = this.items.concat(x);
 | 
				
			||||||
 | 
										this.more = false;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									this.moreFetching = false;
 | 
				
			||||||
 | 
								}, e => {
 | 
				
			||||||
 | 
									this.moreFetching = false;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							prepend(item, silent = false) {
 | 
				
			||||||
 | 
								if (opts.onPrepend) {
 | 
				
			||||||
 | 
									const cancel = opts.onPrepend(this, item, silent);
 | 
				
			||||||
 | 
									if (cancel) return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (this.isScrollTop()) {
 | 
				
			||||||
 | 
									// Prepend the item
 | 
				
			||||||
 | 
									this.items.unshift(item);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// オーバーフローしたら古い投稿は捨てる
 | 
				
			||||||
 | 
									if (this.items.length >= opts.displayLimit) {
 | 
				
			||||||
 | 
										this.items = this.items.slice(0, opts.displayLimit);
 | 
				
			||||||
 | 
										this.more = true;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									this.queue.push(item);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							append(item) {
 | 
				
			||||||
 | 
								this.items.push(item);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							releaseQueue() {
 | 
				
			||||||
 | 
								for (const n of this.queue) {
 | 
				
			||||||
 | 
									this.prepend(n, true);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								this.queue = [];
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onWindowScroll() {
 | 
				
			||||||
 | 
								if (this.isScrollTop()) {
 | 
				
			||||||
 | 
									this.onTop();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (this.$store.state.settings.fetchOnScroll) {
 | 
				
			||||||
 | 
									// 親要素が display none だったら弾く
 | 
				
			||||||
 | 
									// https://github.com/syuilo/misskey/issues/1569
 | 
				
			||||||
 | 
									// http://d.hatena.ne.jp/favril/20091105/1257403319
 | 
				
			||||||
 | 
									if (this.$el.offsetHeight == 0) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									const current = window.scrollY + window.innerHeight;
 | 
				
			||||||
 | 
									if (current > document.body.offsetHeight - 8) this.onBottom();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onTop() {
 | 
				
			||||||
 | 
								this.releaseQueue();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onBottom() {
 | 
				
			||||||
 | 
								this.fetchMore();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -2,13 +2,13 @@
 | 
				
			||||||
<ui-container :body-togglable="true">
 | 
					<ui-container :body-togglable="true">
 | 
				
			||||||
	<template #header><slot></slot></template>
 | 
						<template #header><slot></slot></template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<mk-error v-if="!fetching && !inited" @retry="init()"/>
 | 
						<mk-error v-if="error" @retry="init()"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="efvhhmdq" :class="{ iconOnly }" v-size="[{ lt: 500, class: 'narrow' }]">
 | 
						<div class="efvhhmdq" :class="{ iconOnly }" v-size="[{ lt: 500, class: 'narrow' }]">
 | 
				
			||||||
		<div class="no-users" v-if="inited && us.length == 0">
 | 
							<div class="no-users" v-if="empty">
 | 
				
			||||||
			<p>{{ $t('no-users') }}</p>
 | 
								<p>{{ $t('no-users') }}</p>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<div class="user" v-for="user in us" :key="user.id">
 | 
							<div class="user" v-for="user in users" :key="user.id">
 | 
				
			||||||
			<mk-avatar class="avatar" :user="user"/>
 | 
								<mk-avatar class="avatar" :user="user"/>
 | 
				
			||||||
			<div class="body" v-if="!iconOnly">
 | 
								<div class="body" v-if="!iconOnly">
 | 
				
			||||||
				<div class="name">
 | 
									<div class="name">
 | 
				
			||||||
| 
						 | 
					@ -21,8 +21,8 @@
 | 
				
			||||||
				<mk-follow-button class="follow-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/>
 | 
									<mk-follow-button class="follow-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
		<button class="more" :class="{ fetching: fetchingMoreUsers }" v-if="cursor != null" @click="fetchMoreUsers()" :disabled="fetchingMoreUsers">
 | 
							<button class="more" :class="{ fetching: moreFetching }" v-if="more" @click="fetchMoreUsers()" :disabled="moreFetching">
 | 
				
			||||||
			<template v-if="fetchingMoreUsers"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreUsers ? $t('@.loading') : $t('@.load-more') }}
 | 
								<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('@.loading') : $t('@.load-more') }}
 | 
				
			||||||
		</button>
 | 
							</button>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</ui-container>
 | 
					</ui-container>
 | 
				
			||||||
| 
						 | 
					@ -31,60 +31,31 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					import paging from '../../../common/scripts/paging';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('common/views/components/user-list.vue'),
 | 
						i18n: i18n('common/views/components/user-list.vue'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mixins: [
 | 
				
			||||||
 | 
							paging({}),
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	props: {
 | 
						props: {
 | 
				
			||||||
		makePromise: {
 | 
							pagination: {
 | 
				
			||||||
			required: true
 | 
								required: true
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							extract: {
 | 
				
			||||||
 | 
								required: false
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		iconOnly: {
 | 
							iconOnly: {
 | 
				
			||||||
			type: Boolean,
 | 
								type: Boolean,
 | 
				
			||||||
			default: false
 | 
								default: false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						computed: {
 | 
				
			||||||
		return {
 | 
							users() {
 | 
				
			||||||
			fetching: true,
 | 
								return this.extract ? this.extract(this.items) : this.items;
 | 
				
			||||||
			fetchingMoreUsers: false,
 | 
					 | 
				
			||||||
			us: [],
 | 
					 | 
				
			||||||
			inited: false,
 | 
					 | 
				
			||||||
			more: false
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		this.init();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		async init() {
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
			await (this.makePromise()).then(x => {
 | 
					 | 
				
			||||||
				if (Array.isArray(x)) {
 | 
					 | 
				
			||||||
					this.us = x;
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					this.us = x.users;
 | 
					 | 
				
			||||||
					this.cursor = x.cursor;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				this.inited = true;
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
			}, e => {
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async fetchMoreUsers() {
 | 
					 | 
				
			||||||
			this.fetchingMoreUsers = true;
 | 
					 | 
				
			||||||
			await (this.makePromise(this.cursor)).then(x => {
 | 
					 | 
				
			||||||
				this.us = this.us.concat(x.users);
 | 
					 | 
				
			||||||
				this.cursor = x.cursor;
 | 
					 | 
				
			||||||
				this.fetchingMoreUsers = false;
 | 
					 | 
				
			||||||
			}, e => {
 | 
					 | 
				
			||||||
				this.fetchingMoreUsers = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,11 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import XNotes from './deck.notes.vue';
 | 
					import XNotes from './deck.notes.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XNotes
 | 
							XNotes
 | 
				
			||||||
| 
						 | 
					@ -16,27 +14,16 @@ export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/mentions', {
 | 
								pagination: {
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									endpoint: 'notes/mentions',
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									limit: 10,
 | 
				
			||||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
									params: {
 | 
				
			||||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
										includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
				
			||||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
 | 
										includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
				
			||||||
				visibility: 'specified'
 | 
										includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
 | 
				
			||||||
			}).then(notes => {
 | 
										visibility: 'specified'
 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			})
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,58 +0,0 @@
 | 
				
			||||||
<template>
 | 
					 | 
				
			||||||
<x-column>
 | 
					 | 
				
			||||||
	<template #header>
 | 
					 | 
				
			||||||
		<fa :icon="['fa', 'star']"/>{{ $t('@.favorites') }}
 | 
					 | 
				
			||||||
	</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<div>
 | 
					 | 
				
			||||||
		<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</x-column>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue';
 | 
					 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					 | 
				
			||||||
import XColumn from './deck.column.vue';
 | 
					 | 
				
			||||||
import XNotes from './deck.notes.vue';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	i18n: i18n(),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	components: {
 | 
					 | 
				
			||||||
		XColumn,
 | 
					 | 
				
			||||||
		XNotes,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			makePromise: cursor => this.$root.api('i/favorites', {
 | 
					 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
					 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
					 | 
				
			||||||
			}).then(notes => {
 | 
					 | 
				
			||||||
				notes = notes.map(x => x.note);
 | 
					 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		focus() {
 | 
					 | 
				
			||||||
			this.$refs.timeline.focus();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,46 +0,0 @@
 | 
				
			||||||
<template>
 | 
					 | 
				
			||||||
<x-column>
 | 
					 | 
				
			||||||
	<template #header>
 | 
					 | 
				
			||||||
		<fa :icon="faNewspaper"/>{{ $t('@.featured-notes') }}
 | 
					 | 
				
			||||||
	</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<div>
 | 
					 | 
				
			||||||
		<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</x-column>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue';
 | 
					 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					 | 
				
			||||||
import XColumn from './deck.column.vue';
 | 
					 | 
				
			||||||
import XNotes from './deck.notes.vue';
 | 
					 | 
				
			||||||
import { faNewspaper } from '@fortawesome/free-solid-svg-icons';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	i18n: i18n(),
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	components: {
 | 
					 | 
				
			||||||
		XColumn,
 | 
					 | 
				
			||||||
		XNotes,
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			faNewspaper,
 | 
					 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/featured', {
 | 
					 | 
				
			||||||
				limit: 30,
 | 
					 | 
				
			||||||
			}).then(notes => {
 | 
					 | 
				
			||||||
				notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
 | 
					 | 
				
			||||||
				return notes;
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		focus() {
 | 
					 | 
				
			||||||
			this.$refs.timeline.focus();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,11 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import XNotes from './deck.notes.vue';
 | 
					import XNotes from './deck.notes.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XNotes
 | 
							XNotes
 | 
				
			||||||
| 
						 | 
					@ -28,28 +26,18 @@ export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/search-by-tag', {
 | 
								pagination: {
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									endpoint: 'notes/search-by-tag',
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									limit: 10,
 | 
				
			||||||
				withFiles: this.mediaOnly,
 | 
									params: init => ({
 | 
				
			||||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
										untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
										withFiles: this.mediaOnly,
 | 
				
			||||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
 | 
										includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
				
			||||||
				query: this.tagTl.query
 | 
										includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
				
			||||||
			}).then(notes => {
 | 
										includeLocalRenotes: this.$store.state.settings.showLocalRenotes,
 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
										query: this.tagTl.query
 | 
				
			||||||
					notes.pop();
 | 
									})
 | 
				
			||||||
					return {
 | 
								}
 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,11 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import XNotes from './deck.notes.vue';
 | 
					import XNotes from './deck.notes.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XNotes
 | 
							XNotes
 | 
				
			||||||
| 
						 | 
					@ -28,28 +26,18 @@ export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/user-list-timeline', {
 | 
								pagination: {
 | 
				
			||||||
				listId: this.list.id,
 | 
									endpoint: 'notes/user-list-timeline',
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									limit: 10,
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									params: init => ({
 | 
				
			||||||
				withFiles: this.mediaOnly,
 | 
										listId: this.list.id,
 | 
				
			||||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
										untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
										withFiles: this.mediaOnly,
 | 
				
			||||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
										includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
				
			||||||
			}).then(notes => {
 | 
										includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
										includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
				
			||||||
					notes.pop();
 | 
									})
 | 
				
			||||||
					return {
 | 
								}
 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,13 +1,11 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import XNotes from './deck.notes.vue';
 | 
					import XNotes from './deck.notes.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XNotes
 | 
							XNotes
 | 
				
			||||||
| 
						 | 
					@ -16,26 +14,16 @@ export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/mentions', {
 | 
								pagination: {
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									endpoint: 'notes/mentions',
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									limit: 10,
 | 
				
			||||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
									params: init => ({
 | 
				
			||||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
										untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
										includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
				
			||||||
			}).then(notes => {
 | 
										includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
										includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
				
			||||||
					notes.pop();
 | 
									})
 | 
				
			||||||
					return {
 | 
								}
 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu">
 | 
					<div class="eamppglmnmimdhrlzhplwpvyeaqmmhxu">
 | 
				
			||||||
	<div class="empty" v-if="notes.length == 0 && !fetching && inited">{{ $t('@.no-notes') }}</div>
 | 
						<div class="empty" v-if="empty">{{ $t('@.no-notes') }}</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<mk-error v-if="!fetching && !inited" @retry="init()"/>
 | 
						<mk-error v-if="error" @retry="init()"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="placeholder" v-if="fetching">
 | 
						<div class="placeholder" v-if="fetching">
 | 
				
			||||||
		<template v-for="i in 10">
 | 
							<template v-for="i in 10">
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,6 @@
 | 
				
			||||||
			<mk-note
 | 
								<mk-note
 | 
				
			||||||
				:note="note"
 | 
									:note="note"
 | 
				
			||||||
				:key="note.id"
 | 
									:key="note.id"
 | 
				
			||||||
				@update:note="onNoteUpdated(i, $event)"
 | 
					 | 
				
			||||||
				:compact="true"
 | 
									:compact="true"
 | 
				
			||||||
			/>
 | 
								/>
 | 
				
			||||||
			<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
 | 
								<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
 | 
				
			||||||
| 
						 | 
					@ -39,33 +38,47 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
import shouldMuteNote from '../../../common/scripts/should-mute-note';
 | 
					import shouldMuteNote from '../../../common/scripts/should-mute-note';
 | 
				
			||||||
 | 
					import paging from '../../../common/scripts/paging';
 | 
				
			||||||
const displayLimit = 20;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n(),
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inject: ['column', 'isScrollTop', 'count'],
 | 
						inject: ['column', 'isScrollTop', 'count'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mixins: [
 | 
				
			||||||
 | 
							paging({
 | 
				
			||||||
 | 
								displayLimit: 20,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								onQueueChanged: (self, q) => {
 | 
				
			||||||
 | 
									self.count(q.length);
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								onPrepend: (self, note, silent) => {
 | 
				
			||||||
 | 
									// 弾く
 | 
				
			||||||
 | 
									if (shouldMuteNote(self.$store.state.i, self.$store.state.settings, note)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
 | 
				
			||||||
 | 
									if (document.hidden || !self.isScrollTop()) {
 | 
				
			||||||
 | 
										self.$store.commit('pushBehindNote', note);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	props: {
 | 
						props: {
 | 
				
			||||||
		makePromise: {
 | 
							pagination: {
 | 
				
			||||||
			required: true
 | 
								required: true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							extract: {
 | 
				
			||||||
 | 
								required: false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			rootEl: null,
 | 
					 | 
				
			||||||
			notes: [],
 | 
					 | 
				
			||||||
			queue: [],
 | 
					 | 
				
			||||||
			fetching: true,
 | 
					 | 
				
			||||||
			moreFetching: false,
 | 
					 | 
				
			||||||
			inited: false,
 | 
					 | 
				
			||||||
			more: false
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
 | 
							notes() {
 | 
				
			||||||
 | 
								return this.extract ? this.extract(this.items) : this.items;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		_notes(): any[] {
 | 
							_notes(): any[] {
 | 
				
			||||||
			return (this.notes as any).map(note => {
 | 
								return (this.notes as any).map(note => {
 | 
				
			||||||
				const date = new Date(note.createdAt).getDate();
 | 
									const date = new Date(note.createdAt).getDate();
 | 
				
			||||||
| 
						 | 
					@ -77,15 +90,6 @@ export default Vue.extend({
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	watch: {
 | 
					 | 
				
			||||||
		queue(q) {
 | 
					 | 
				
			||||||
			this.count(q.length);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		makePromise() {
 | 
					 | 
				
			||||||
			this.init();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	created() {
 | 
						created() {
 | 
				
			||||||
		this.column.$on('top', this.onTop);
 | 
							this.column.$on('top', this.onTop);
 | 
				
			||||||
		this.column.$on('bottom', this.onBottom);
 | 
							this.column.$on('bottom', this.onBottom);
 | 
				
			||||||
| 
						 | 
					@ -101,87 +105,6 @@ export default Vue.extend({
 | 
				
			||||||
		focus() {
 | 
							focus() {
 | 
				
			||||||
			(this.$refs.notes as any).children[0].focus ? (this.$refs.notes as any).children[0].focus() : (this.$refs.notes as any).$el.children[0].focus();
 | 
								(this.$refs.notes as any).children[0].focus ? (this.$refs.notes as any).children[0].focus() : (this.$refs.notes as any).$el.children[0].focus();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					 | 
				
			||||||
		onNoteUpdated(i, note) {
 | 
					 | 
				
			||||||
			Vue.set((this as any).notes, i, note);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		reload() {
 | 
					 | 
				
			||||||
			this.init();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async init() {
 | 
					 | 
				
			||||||
			this.queue = [];
 | 
					 | 
				
			||||||
			this.notes = [];
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
			await (this.makePromise()).then(x => {
 | 
					 | 
				
			||||||
				if (Array.isArray(x)) {
 | 
					 | 
				
			||||||
					this.notes = x;
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					this.notes = x.notes;
 | 
					 | 
				
			||||||
					this.more = x.more;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				this.inited = true;
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
				this.$emit('inited');
 | 
					 | 
				
			||||||
			}, e => {
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async fetchMore() {
 | 
					 | 
				
			||||||
			if (!this.more || this.moreFetching) return;
 | 
					 | 
				
			||||||
			this.moreFetching = true;
 | 
					 | 
				
			||||||
			await (this.makePromise(this.notes[this.notes.length - 1].id)).then(x => {
 | 
					 | 
				
			||||||
				this.notes = this.notes.concat(x.notes);
 | 
					 | 
				
			||||||
				this.more = x.more;
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			}, e => {
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		prepend(note, silent = false) {
 | 
					 | 
				
			||||||
			// 弾く
 | 
					 | 
				
			||||||
			if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// タブが非表示ならタイトルで通知
 | 
					 | 
				
			||||||
			if (document.hidden) {
 | 
					 | 
				
			||||||
				this.$store.commit('pushBehindNote', note);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (this.isScrollTop()) {
 | 
					 | 
				
			||||||
				// Prepend the note
 | 
					 | 
				
			||||||
				this.notes.unshift(note);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// オーバーフローしたら古い投稿は捨てる
 | 
					 | 
				
			||||||
				if (this.notes.length >= displayLimit) {
 | 
					 | 
				
			||||||
					this.notes = this.notes.slice(0, displayLimit);
 | 
					 | 
				
			||||||
					this.more = true;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				this.queue.push(note);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		append(note) {
 | 
					 | 
				
			||||||
			this.notes.push(note);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		releaseQueue() {
 | 
					 | 
				
			||||||
			for (const n of this.queue) {
 | 
					 | 
				
			||||||
				this.prepend(n, true);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			this.queue = [];
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		onTop() {
 | 
					 | 
				
			||||||
			this.releaseQueue();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		onBottom() {
 | 
					 | 
				
			||||||
			this.fetchMore();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -81,15 +81,15 @@
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template v-if="notification.type == 'quote'">
 | 
						<template v-if="notification.type == 'quote'">
 | 
				
			||||||
		<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
 | 
							<mk-note :note="notification.note"/>
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template v-if="notification.type == 'reply'">
 | 
						<template v-if="notification.type == 'reply'">
 | 
				
			||||||
		<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
 | 
							<mk-note :note="notification.note"/>
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template v-if="notification.type == 'mention'">
 | 
						<template v-if="notification.type == 'mention'">
 | 
				
			||||||
		<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
 | 
							<mk-note :note="notification.note"/>
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					@ -105,17 +105,6 @@ export default Vue.extend({
 | 
				
			||||||
			getNoteSummary
 | 
								getNoteSummary
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		onNoteUpdated(note) {
 | 
					 | 
				
			||||||
			switch (this.notification.type) {
 | 
					 | 
				
			||||||
				case 'quote':
 | 
					 | 
				
			||||||
				case 'reply':
 | 
					 | 
				
			||||||
				case 'mention':
 | 
					 | 
				
			||||||
					Vue.set(this.notification, 'note', note);
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,7 @@
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div>
 | 
						<div>
 | 
				
			||||||
		<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
							<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
</x-column>
 | 
					</x-column>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					@ -16,8 +16,6 @@ import XColumn from './deck.column.vue';
 | 
				
			||||||
import XNotes from './deck.notes.vue';
 | 
					import XNotes from './deck.notes.vue';
 | 
				
			||||||
import { genSearchQuery } from '../../../common/scripts/gen-search-query';
 | 
					import { genSearchQuery } from '../../../common/scripts/gen-search-query';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const limit = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XColumn,
 | 
							XColumn,
 | 
				
			||||||
| 
						 | 
					@ -26,24 +24,11 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			makePromise: async cursor => this.$root.api('notes/search', {
 | 
								pagination: {
 | 
				
			||||||
				limit: limit + 1,
 | 
									endpoint: 'notes/search',
 | 
				
			||||||
				offset: cursor ? cursor : undefined,
 | 
									limit: 20,
 | 
				
			||||||
				...(await genSearchQuery(this, this.q))
 | 
									params: () => genSearchQuery(this, this.q)
 | 
				
			||||||
			}).then(notes => {
 | 
								}
 | 
				
			||||||
				if (notes.length == limit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						cursor: cursor ? cursor + limit : limit
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,7 +6,7 @@
 | 
				
			||||||
	</p>
 | 
						</p>
 | 
				
			||||||
	<p class="desc">{{ $t('disabled-timeline.description') }}</p>
 | 
						<p class="desc">{{ $t('disabled-timeline.description') }}</p>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
<x-notes v-else ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					<x-notes v-else ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
| 
						 | 
					@ -15,8 +15,6 @@ import XNotes from './deck.notes.vue';
 | 
				
			||||||
import { faMinusCircle } from '@fortawesome/free-solid-svg-icons';
 | 
					import { faMinusCircle } from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('deck'),
 | 
						i18n: i18n('deck'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,7 +40,7 @@ export default Vue.extend({
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			disabled: false,
 | 
								disabled: false,
 | 
				
			||||||
			faMinusCircle,
 | 
								faMinusCircle,
 | 
				
			||||||
			makePromise: null
 | 
								pagination: null
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -73,27 +71,17 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	created() {
 | 
						created() {
 | 
				
			||||||
		this.makePromise = cursor => this.$root.api(this.endpoint, {
 | 
							this.pagination = {
 | 
				
			||||||
			limit: fetchLimit + 1,
 | 
								endpoint: this.endpoint,
 | 
				
			||||||
			untilId: cursor ? cursor : undefined,
 | 
								limit: 10,
 | 
				
			||||||
			withFiles: this.mediaOnly,
 | 
								params: init => ({
 | 
				
			||||||
			includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
									untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
			includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
									withFiles: this.mediaOnly,
 | 
				
			||||||
			includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
									includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
				
			||||||
		}).then(notes => {
 | 
									includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
				
			||||||
			if (notes.length == fetchLimit + 1) {
 | 
									includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
				
			||||||
				notes.pop();
 | 
								})
 | 
				
			||||||
				return {
 | 
							};
 | 
				
			||||||
					notes: notes,
 | 
					 | 
				
			||||||
					more: true
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				return {
 | 
					 | 
				
			||||||
					notes: notes,
 | 
					 | 
				
			||||||
					more: false
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@
 | 
				
			||||||
	<ui-container>
 | 
						<ui-container>
 | 
				
			||||||
		<template #header><fa :icon="['far', 'comment-alt']"/> {{ $t('timeline') }}</template>
 | 
							<template #header><fa :icon="['far', 'comment-alt']"/> {{ $t('timeline') }}</template>
 | 
				
			||||||
		<div>
 | 
							<div>
 | 
				
			||||||
			<x-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
								<x-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</ui-container>
 | 
						</ui-container>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
| 
						 | 
					@ -43,8 +43,6 @@ import XNotes from './deck.notes.vue';
 | 
				
			||||||
import { concat } from '../../../../../prelude/array';
 | 
					import { concat } from '../../../../../prelude/array';
 | 
				
			||||||
import ApexCharts from 'apexcharts';
 | 
					import ApexCharts from 'apexcharts';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('deck/deck.user-column.vue'),
 | 
						i18n: i18n('deck/deck.user-column.vue'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,49 +61,38 @@ export default Vue.extend({
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			withFiles: false,
 | 
								withFiles: false,
 | 
				
			||||||
			images: [],
 | 
								images: [],
 | 
				
			||||||
			makePromise: null,
 | 
					 | 
				
			||||||
			chart: null as ApexCharts
 | 
								chart: null as ApexCharts
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						computed: {
 | 
				
			||||||
 | 
							pagination() {
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									endpoint: 'users/notes',
 | 
				
			||||||
 | 
									limit: 10,
 | 
				
			||||||
 | 
									params: init => ({
 | 
				
			||||||
 | 
										userId: this.user.id,
 | 
				
			||||||
 | 
										untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
 | 
										withFiles: this.withFiles,
 | 
				
			||||||
 | 
										includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
				
			||||||
 | 
										includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
				
			||||||
 | 
										includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	watch: {
 | 
						watch: {
 | 
				
			||||||
		user() {
 | 
							user() {
 | 
				
			||||||
			this.fetch();
 | 
								this.fetch();
 | 
				
			||||||
			this.genPromiseMaker();
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	created() {
 | 
						created() {
 | 
				
			||||||
		this.fetch();
 | 
							this.fetch();
 | 
				
			||||||
		this.genPromiseMaker();
 | 
					 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		genPromiseMaker() {
 | 
					 | 
				
			||||||
			this.makePromise = cursor => this.$root.api('users/notes', {
 | 
					 | 
				
			||||||
				userId: this.user.id,
 | 
					 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
					 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
					 | 
				
			||||||
				withFiles: this.withFiles,
 | 
					 | 
				
			||||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
					 | 
				
			||||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
					 | 
				
			||||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
					 | 
				
			||||||
			}).then(notes => {
 | 
					 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		fetch() {
 | 
							fetch() {
 | 
				
			||||||
			const image = [
 | 
								const image = [
 | 
				
			||||||
				'image/jpeg',
 | 
									'image/jpeg',
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,24 +18,24 @@
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</ui-container>
 | 
						</ui-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<mk-user-list v-if="tag != null" :make-promise="tagUsers" :key="`${tag}-local`">
 | 
						<mk-user-list v-if="tag != null" :pagination="tagUsers" :key="`${tag}-local`">
 | 
				
			||||||
		<fa :icon="faHashtag" fixed-width/>{{ tag }}
 | 
							<fa :icon="faHashtag" fixed-width/>{{ tag }}
 | 
				
			||||||
	</mk-user-list>
 | 
						</mk-user-list>
 | 
				
			||||||
	<mk-user-list v-if="tag != null" :make-promise="tagRemoteUsers" :key="`${tag}-remote`">
 | 
						<mk-user-list v-if="tag != null" :pagination="tagRemoteUsers" :key="`${tag}-remote`">
 | 
				
			||||||
		<fa :icon="faHashtag" fixed-width/>{{ tag }} ({{ $t('federated') }})
 | 
							<fa :icon="faHashtag" fixed-width/>{{ tag }} ({{ $t('federated') }})
 | 
				
			||||||
	</mk-user-list>
 | 
						</mk-user-list>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template v-if="tag == null">
 | 
						<template v-if="tag == null">
 | 
				
			||||||
		<mk-user-list :make-promise="pinnedUsers">
 | 
							<mk-user-list :pagination="pinnedUsers">
 | 
				
			||||||
			<fa :icon="faBookmark" fixed-width/>{{ $t('pinned-users') }}
 | 
								<fa :icon="faBookmark" fixed-width/>{{ $t('pinned-users') }}
 | 
				
			||||||
		</mk-user-list>
 | 
							</mk-user-list>
 | 
				
			||||||
		<mk-user-list :make-promise="popularUsers">
 | 
							<mk-user-list :pagination="popularUsers">
 | 
				
			||||||
			<fa :icon="faChartLine" fixed-width/>{{ $t('popular-users') }}
 | 
								<fa :icon="faChartLine" fixed-width/>{{ $t('popular-users') }}
 | 
				
			||||||
		</mk-user-list>
 | 
							</mk-user-list>
 | 
				
			||||||
		<mk-user-list :make-promise="recentlyUpdatedUsers">
 | 
							<mk-user-list :pagination="recentlyUpdatedUsers">
 | 
				
			||||||
			<fa :icon="faCommentAlt" fixed-width/>{{ $t('recently-updated-users') }}
 | 
								<fa :icon="faCommentAlt" fixed-width/>{{ $t('recently-updated-users') }}
 | 
				
			||||||
		</mk-user-list>
 | 
							</mk-user-list>
 | 
				
			||||||
		<mk-user-list :make-promise="recentlyRegisteredUsers">
 | 
							<mk-user-list :pagination="recentlyRegisteredUsers">
 | 
				
			||||||
			<fa :icon="faPlus" fixed-width/>{{ $t('recently-registered-users') }}
 | 
								<fa :icon="faPlus" fixed-width/>{{ $t('recently-registered-users') }}
 | 
				
			||||||
		</mk-user-list>
 | 
							</mk-user-list>
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
| 
						 | 
					@ -60,24 +60,21 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			pinnedUsers: () => this.$root.api('pinned-users'),
 | 
								pinnedUsers: { endpoint: 'pinned-users' },
 | 
				
			||||||
			popularUsers: () => this.$root.api('users', {
 | 
								popularUsers: { endpoint: 'users', limit: 10, params: {
 | 
				
			||||||
				state: 'alive',
 | 
									state: 'alive',
 | 
				
			||||||
				origin: 'local',
 | 
									origin: 'local',
 | 
				
			||||||
				sort: '+follower',
 | 
									sort: '+follower',
 | 
				
			||||||
				limit: 10
 | 
								} },
 | 
				
			||||||
			}),
 | 
								recentlyUpdatedUsers: { endpoint: 'users', limit: 10, params: {
 | 
				
			||||||
			recentlyUpdatedUsers: () => this.$root.api('users', {
 | 
					 | 
				
			||||||
				origin: 'local',
 | 
									origin: 'local',
 | 
				
			||||||
				sort: '+updatedAt',
 | 
									sort: '+updatedAt',
 | 
				
			||||||
				limit: 10
 | 
								} },
 | 
				
			||||||
			}),
 | 
								recentlyRegisteredUsers: { endpoint: 'users', limit: 10, params: {
 | 
				
			||||||
			recentlyRegisteredUsers: () => this.$root.api('users', {
 | 
					 | 
				
			||||||
				origin: 'local',
 | 
									origin: 'local',
 | 
				
			||||||
				state: 'alive',
 | 
									state: 'alive',
 | 
				
			||||||
				sort: '+createdAt',
 | 
									sort: '+createdAt',
 | 
				
			||||||
				limit: 10
 | 
								} },
 | 
				
			||||||
			}),
 | 
					 | 
				
			||||||
			tagsLocal: [],
 | 
								tagsLocal: [],
 | 
				
			||||||
			tagsRemote: [],
 | 
								tagsRemote: [],
 | 
				
			||||||
			stats: null,
 | 
								stats: null,
 | 
				
			||||||
| 
						 | 
					@ -88,24 +85,30 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
		tagUsers(): () => Promise<any> {
 | 
							tagUsers(): any {
 | 
				
			||||||
			return () => this.$root.api('hashtags/users', {
 | 
								return {
 | 
				
			||||||
				tag: this.tag,
 | 
									endpoint: 'hashtags/users',
 | 
				
			||||||
				state: 'alive',
 | 
									limit: 30,
 | 
				
			||||||
				origin: 'local',
 | 
									params: {
 | 
				
			||||||
				sort: '+follower',
 | 
										tag: this.tag,
 | 
				
			||||||
				limit: 30
 | 
										state: 'alive',
 | 
				
			||||||
			});
 | 
										origin: 'local',
 | 
				
			||||||
 | 
										sort: '+follower',
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tagRemoteUsers(): () => Promise<any> {
 | 
							tagRemoteUsers(): any {
 | 
				
			||||||
			return () => this.$root.api('hashtags/users', {
 | 
								return {
 | 
				
			||||||
				tag: this.tag,
 | 
									endpoint: 'hashtags/users',
 | 
				
			||||||
				state: 'alive',
 | 
									limit: 30,
 | 
				
			||||||
				origin: 'remote',
 | 
									params: {
 | 
				
			||||||
				sort: '+follower',
 | 
										tag: this.tag,
 | 
				
			||||||
				limit: 30
 | 
										state: 'alive',
 | 
				
			||||||
			});
 | 
										origin: 'remote',
 | 
				
			||||||
 | 
										sort: '+follower',
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										44
									
								
								src/client/app/common/views/pages/favorites.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/client/app/common/views/pages/favorites.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
						<component :is="notesComponent" :pagination="pagination" :extract="items => items.map(item => item.note)"/>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import { faStar } from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					//import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						props: {
 | 
				
			||||||
 | 
							platform: {
 | 
				
			||||||
 | 
								type: String,
 | 
				
			||||||
 | 
								required: true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								pagination: {
 | 
				
			||||||
 | 
									endpoint: 'i/favorites',
 | 
				
			||||||
 | 
									limit: 10,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								notesComponent:
 | 
				
			||||||
 | 
									this.platform === 'desktop' ? () => import('../../../desktop/views/components/detail-notes.vue').then(m => m.default) :
 | 
				
			||||||
 | 
									this.platform === 'mobile' ? () => import('../../../mobile/views/components/detail-notes.vue').then(m => m.default) :
 | 
				
			||||||
 | 
									this.platform === 'deck' ? () => import('../deck/deck.notes.vue').then(m => m.default) : null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						created() {
 | 
				
			||||||
 | 
							this.$emit('init', {
 | 
				
			||||||
 | 
								title: this.$t('@.favorites'),
 | 
				
			||||||
 | 
								icon: faStar
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
							
								
								
									
										44
									
								
								src/client/app/common/views/pages/featured.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/client/app/common/views/pages/featured.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,44 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
						<component :is="notesComponent" :pagination="pagination"/>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import { faNewspaper } from '@fortawesome/free-solid-svg-icons';
 | 
				
			||||||
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					//import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						props: {
 | 
				
			||||||
 | 
							platform: {
 | 
				
			||||||
 | 
								type: String,
 | 
				
			||||||
 | 
								required: true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								pagination: {
 | 
				
			||||||
 | 
									endpoint: 'notes/featured',
 | 
				
			||||||
 | 
									limit: 30,
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								notesComponent:
 | 
				
			||||||
 | 
									this.platform === 'desktop' ? () => import('../../../desktop/views/components/detail-notes.vue').then(m => m.default) :
 | 
				
			||||||
 | 
									this.platform === 'mobile' ? () => import('../../../mobile/views/components/detail-notes.vue').then(m => m.default) :
 | 
				
			||||||
 | 
									this.platform === 'deck' ? () => import('../deck/deck.notes.vue').then(m => m.default) : null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						created() {
 | 
				
			||||||
 | 
							this.$emit('init', {
 | 
				
			||||||
 | 
								title: this.$t('@.featured-notes'),
 | 
				
			||||||
 | 
								icon: faNewspaper
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,5 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<mk-user-list :pagination="pagination" :extract="items => items.map(item => item.follower)">{{ $t('@.followers') }}</mk-user-list>
 | 
				
			||||||
	<mk-user-list :make-promise="makePromise">{{ $t('@.followers') }}</mk-user-list>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
| 
						 | 
					@ -9,31 +7,18 @@ import Vue from 'vue';
 | 
				
			||||||
import parseAcct from '../../../../../misc/acct/parse';
 | 
					import parseAcct from '../../../../../misc/acct/parse';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 30;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n(),
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			makePromise: cursor => this.$root.api('users/followers', {
 | 
								pagination: {
 | 
				
			||||||
				...parseAcct(this.$route.params.user),
 | 
									endpoint: 'users/followers',
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									limit: 30,
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									params: {
 | 
				
			||||||
			}).then(followings => {
 | 
										...parseAcct(this.$route.params.user),
 | 
				
			||||||
				if (followings.length == fetchLimit + 1) {
 | 
					 | 
				
			||||||
					followings.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						users: followings.map(following => following.follower),
 | 
					 | 
				
			||||||
						cursor: followings[followings.length - 1].id
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						users: followings.map(following => following.follower),
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}),
 | 
								},
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,5 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<mk-user-list :pagination="pagination" :extract="items => items.map(item => item.followee)">{{ $t('@.following') }}</mk-user-list>
 | 
				
			||||||
	<mk-user-list :make-promise="makePromise">{{ $t('@.following') }}</mk-user-list>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
| 
						 | 
					@ -9,31 +7,18 @@ import Vue from 'vue';
 | 
				
			||||||
import parseAcct from '../../../../../misc/acct/parse';
 | 
					import parseAcct from '../../../../../misc/acct/parse';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 30;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n(),
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			makePromise: cursor => this.$root.api('users/following', {
 | 
								pagination: {
 | 
				
			||||||
				...parseAcct(this.$route.params.user),
 | 
									endpoint: 'users/following',
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									limit: 30,
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									params: {
 | 
				
			||||||
			}).then(followings => {
 | 
										...parseAcct(this.$route.params.user),
 | 
				
			||||||
				if (followings.length == fetchLimit + 1) {
 | 
					 | 
				
			||||||
					followings.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						users: followings.map(following => following.followee),
 | 
					 | 
				
			||||||
						cursor: followings[followings.length - 1].id
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						users: followings.map(following => following.followee),
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}),
 | 
								},
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -138,10 +138,10 @@ init(async (launch, os) => {
 | 
				
			||||||
					{ path: '/notes/:note', name: 'note', component: () => import('../common/views/deck/deck.note-column.vue').then(m => m.default) },
 | 
										{ path: '/notes/:note', name: 'note', component: () => import('../common/views/deck/deck.note-column.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/search', component: () => import('../common/views/deck/deck.search-column.vue').then(m => m.default) },
 | 
										{ path: '/search', component: () => import('../common/views/deck/deck.search-column.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/tags/:tag', name: 'tag', component: () => import('../common/views/deck/deck.hashtag-column.vue').then(m => m.default) },
 | 
										{ path: '/tags/:tag', name: 'tag', component: () => import('../common/views/deck/deck.hashtag-column.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/featured', name: 'featured', component: () => import('../common/views/deck/deck.featured-column.vue').then(m => m.default) },
 | 
										{ path: '/featured', name: 'featured', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/featured.vue').then(m => m.default), platform: 'deck' }) },
 | 
				
			||||||
					{ path: '/explore', name: 'explore', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default) }) },
 | 
										{ path: '/explore', name: 'explore', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default) }) },
 | 
				
			||||||
					{ path: '/explore/tags/:tag', name: 'explore-tag', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default), tag: route.params.tag }) },
 | 
										{ path: '/explore/tags/:tag', name: 'explore-tag', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default), tag: route.params.tag }) },
 | 
				
			||||||
					{ path: '/i/favorites', component: () => import('../common/views/deck/deck.favorites-column.vue').then(m => m.default) },
 | 
										{ path: '/i/favorites', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/favorites.vue').then(m => m.default), platform: 'deck' }) },
 | 
				
			||||||
					{ path: '/i/pages', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/pages.vue').then(m => m.default) }) },
 | 
										{ path: '/i/pages', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/pages.vue').then(m => m.default) }) },
 | 
				
			||||||
					{ path: '/i/lists', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) }) },
 | 
										{ path: '/i/lists', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) }) },
 | 
				
			||||||
					{ path: '/i/lists/:listId', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-list-editor.vue').then(m => m.default), listId: route.params.listId }) },
 | 
										{ path: '/i/lists/:listId', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-list-editor.vue').then(m => m.default), listId: route.params.listId }) },
 | 
				
			||||||
| 
						 | 
					@ -158,10 +158,10 @@ init(async (launch, os) => {
 | 
				
			||||||
					{ path: '/notes/:note', name: 'note', component: () => import('./views/home/note.vue').then(m => m.default) },
 | 
										{ path: '/notes/:note', name: 'note', component: () => import('./views/home/note.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/search', component: () => import('./views/home/search.vue').then(m => m.default) },
 | 
										{ path: '/search', component: () => import('./views/home/search.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/tags/:tag', name: 'tag', component: () => import('./views/home/tag.vue').then(m => m.default) },
 | 
										{ path: '/tags/:tag', name: 'tag', component: () => import('./views/home/tag.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/featured', name: 'featured', component: () => import('./views/home/featured.vue').then(m => m.default) },
 | 
										{ path: '/featured', name: 'featured', component: () => import('../common/views/pages/featured.vue').then(m => m.default), props: { platform: 'desktop' } },
 | 
				
			||||||
					{ path: '/explore', name: 'explore', component: () => import('../common/views/pages/explore.vue').then(m => m.default) },
 | 
										{ path: '/explore', name: 'explore', component: () => import('../common/views/pages/explore.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/explore/tags/:tag', name: 'explore-tag', props: true, component: () => import('../common/views/pages/explore.vue').then(m => m.default) },
 | 
										{ path: '/explore/tags/:tag', name: 'explore-tag', props: true, component: () => import('../common/views/pages/explore.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/i/favorites', component: () => import('./views/home/favorites.vue').then(m => m.default) },
 | 
										{ path: '/i/favorites', component: () => import('../common/views/pages/favorites.vue').then(m => m.default), props: { platform: 'desktop' } },
 | 
				
			||||||
					{ path: '/i/pages', component: () => import('../common/views/pages/pages.vue').then(m => m.default) },
 | 
										{ path: '/i/pages', component: () => import('../common/views/pages/pages.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/i/lists', component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) },
 | 
										{ path: '/i/lists', component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/i/lists/:listId', props: true, component: () => import('../common/views/pages/user-list-editor.vue').then(m => m.default) },
 | 
										{ path: '/i/lists/:listId', props: true, component: () => import('../common/views/pages/user-list-editor.vue').then(m => m.default) },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										56
									
								
								src/client/app/desktop/views/components/detail-notes.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/client/app/desktop/views/components/detail-notes.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,56 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="ecsvsegy" v-if="!fetching">
 | 
				
			||||||
 | 
						<sequential-entrance animation="entranceFromTop" delay="25">
 | 
				
			||||||
 | 
							<template v-for="note in notes">
 | 
				
			||||||
 | 
								<mk-note-detail class="post" :note="note" :key="note.id"/>
 | 
				
			||||||
 | 
							</template>
 | 
				
			||||||
 | 
						</sequential-entrance>
 | 
				
			||||||
 | 
						<div class="more" v-if="more">
 | 
				
			||||||
 | 
							<ui-button inline @click="fetchMore()">{{ $t('@.load-more') }}</ui-button>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					import paging from '../../../common/scripts/paging';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mixins: [
 | 
				
			||||||
 | 
							paging({
 | 
				
			||||||
 | 
								captureWindowScroll: true,
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						props: {
 | 
				
			||||||
 | 
							pagination: {
 | 
				
			||||||
 | 
								required: true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							extract: {
 | 
				
			||||||
 | 
								required: false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						computed: {
 | 
				
			||||||
 | 
							notes() {
 | 
				
			||||||
 | 
								return this.extract ? this.extract(this.items) : this.items;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.ecsvsegy
 | 
				
			||||||
 | 
						margin 0 auto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> * > .post
 | 
				
			||||||
 | 
							margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> .more
 | 
				
			||||||
 | 
							margin 32px 16px 16px 16px
 | 
				
			||||||
 | 
							text-align center
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -4,9 +4,9 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
 | 
						<div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="empty" v-if="notes.length == 0 && !fetching && inited">{{ $t('@.no-notes') }}</div>
 | 
						<div class="empty" v-if="empty">{{ $t('@.no-notes') }}</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<mk-error v-if="!fetching && !inited" @retry="init()"/>
 | 
						<mk-error v-if="error" @retry="init()"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="placeholder" v-if="fetching">
 | 
						<div class="placeholder" v-if="fetching">
 | 
				
			||||||
		<template v-for="i in 10">
 | 
							<template v-for="i in 10">
 | 
				
			||||||
| 
						 | 
					@ -17,8 +17,8 @@
 | 
				
			||||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
						<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
				
			||||||
	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div" ref="notes">
 | 
						<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="notes transition" tag="div" ref="notes">
 | 
				
			||||||
		<template v-for="(note, i) in _notes">
 | 
							<template v-for="(note, i) in _notes">
 | 
				
			||||||
			<mk-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)" :compact="true" ref="note"/>
 | 
								<mk-note :note="note" :key="note.id" :compact="true" ref="note"/>
 | 
				
			||||||
			<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
 | 
								<p class="date" :key="note.id + '_date'" v-if="i != items.length - 1 && note._date != _notes[i + 1]._date">
 | 
				
			||||||
				<span><fa icon="angle-up"/>{{ note._datetext }}</span>
 | 
									<span><fa icon="angle-up"/>{{ note._datetext }}</span>
 | 
				
			||||||
				<span><fa icon="angle-down"/>{{ _notes[i + 1]._datetext }}</span>
 | 
									<span><fa icon="angle-down"/>{{ _notes[i + 1]._datetext }}</span>
 | 
				
			||||||
			</p>
 | 
								</p>
 | 
				
			||||||
| 
						 | 
					@ -39,152 +39,66 @@ import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
import * as config from '../../../config';
 | 
					import * as config from '../../../config';
 | 
				
			||||||
import shouldMuteNote from '../../../common/scripts/should-mute-note';
 | 
					import shouldMuteNote from '../../../common/scripts/should-mute-note';
 | 
				
			||||||
 | 
					import paging from '../../../common/scripts/paging';
 | 
				
			||||||
const displayLimit = 30;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n(),
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	props: {
 | 
						mixins: [
 | 
				
			||||||
		makePromise: {
 | 
							paging({
 | 
				
			||||||
			required: true
 | 
								captureWindowScroll: true,
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
								onQueueChanged: (self, x) => {
 | 
				
			||||||
		return {
 | 
									if (x.length > 0) {
 | 
				
			||||||
			notes: [],
 | 
										self.$store.commit('indicate', true);
 | 
				
			||||||
			queue: [],
 | 
									} else {
 | 
				
			||||||
			fetching: true,
 | 
										self.$store.commit('indicate', false);
 | 
				
			||||||
			moreFetching: false,
 | 
									}
 | 
				
			||||||
			inited: false,
 | 
								},
 | 
				
			||||||
			more: false
 | 
					
 | 
				
			||||||
		};
 | 
								onPrepend: (self, note, silent) => {
 | 
				
			||||||
 | 
									// 弾く
 | 
				
			||||||
 | 
									if (shouldMuteNote(self.$store.state.i, self.$store.state.settings, note)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
 | 
				
			||||||
 | 
									if (document.hidden || !self.isScrollTop()) {
 | 
				
			||||||
 | 
										self.$store.commit('pushBehindNote', note);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									if (self.isScrollTop()) {
 | 
				
			||||||
 | 
										// サウンドを再生する
 | 
				
			||||||
 | 
										if (self.$store.state.device.enableSounds && !silent) {
 | 
				
			||||||
 | 
											const sound = new Audio(`${config.url}/assets/post.mp3`);
 | 
				
			||||||
 | 
											sound.volume = self.$store.state.device.soundVolume;
 | 
				
			||||||
 | 
											sound.play();
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						props: {
 | 
				
			||||||
 | 
							pagination: {
 | 
				
			||||||
 | 
								required: true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
		_notes(): any[] {
 | 
							_notes(): any[] {
 | 
				
			||||||
			return (this.notes as any).map(note => {
 | 
								return (this.items as any).map(item => {
 | 
				
			||||||
				const date = new Date(note.createdAt).getDate();
 | 
									const date = new Date(item.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(note.createdAt).getMonth() + 1;
 | 
									const month = new Date(item.createdAt).getMonth() + 1;
 | 
				
			||||||
				note._date = date;
 | 
									item._date = date;
 | 
				
			||||||
				note._datetext = this.$t('@.month-and-day').replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
									item._datetext = this.$t('@.month-and-day').replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return note;
 | 
									return item;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		this.init();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mounted() {
 | 
					 | 
				
			||||||
		window.addEventListener('scroll', this.onScroll, { passive: true });
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	beforeDestroy() {
 | 
					 | 
				
			||||||
		window.removeEventListener('scroll', this.onScroll);
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		isScrollTop() {
 | 
					 | 
				
			||||||
			return window.scrollY <= 8;
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		focus() {
 | 
							focus() {
 | 
				
			||||||
			(this.$refs.notes as any).children[0].focus ? (this.$refs.notes as any).children[0].focus() : (this.$refs.notes as any).$el.children[0].focus();
 | 
								(this.$refs.notes as any).children[0].focus ? (this.$refs.notes as any).children[0].focus() : (this.$refs.notes as any).$el.children[0].focus();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					 | 
				
			||||||
		onNoteUpdated(i, note) {
 | 
					 | 
				
			||||||
			Vue.set((this as any).notes, i, note);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		reload() {
 | 
					 | 
				
			||||||
			this.queue = [];
 | 
					 | 
				
			||||||
			this.notes = [];
 | 
					 | 
				
			||||||
			this.init();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async init() {
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
			await (this.makePromise()).then(x => {
 | 
					 | 
				
			||||||
				if (Array.isArray(x)) {
 | 
					 | 
				
			||||||
					this.notes = x;
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					this.notes = x.notes;
 | 
					 | 
				
			||||||
					this.more = x.more;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				this.inited = true;
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
				this.$emit('inited');
 | 
					 | 
				
			||||||
			}, e => {
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async fetchMore() {
 | 
					 | 
				
			||||||
			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);
 | 
					 | 
				
			||||||
				this.more = x.more;
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			}, e => {
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		prepend(note, silent = false) {
 | 
					 | 
				
			||||||
			// 弾く
 | 
					 | 
				
			||||||
			if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
 | 
					 | 
				
			||||||
			if (document.hidden || !this.isScrollTop()) {
 | 
					 | 
				
			||||||
				this.$store.commit('pushBehindNote', note);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (this.isScrollTop()) {
 | 
					 | 
				
			||||||
				// Prepend the note
 | 
					 | 
				
			||||||
				this.notes.unshift(note);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// サウンドを再生する
 | 
					 | 
				
			||||||
				if (this.$store.state.device.enableSounds && !silent) {
 | 
					 | 
				
			||||||
					const sound = new Audio(`${config.url}/assets/post.mp3`);
 | 
					 | 
				
			||||||
					sound.volume = this.$store.state.device.soundVolume;
 | 
					 | 
				
			||||||
					sound.play();
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// オーバーフローしたら古い投稿は捨てる
 | 
					 | 
				
			||||||
				if (this.notes.length >= displayLimit) {
 | 
					 | 
				
			||||||
					this.notes = this.notes.slice(0, displayLimit);
 | 
					 | 
				
			||||||
					this.more = true;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				this.queue.push(note);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		append(note) {
 | 
					 | 
				
			||||||
			this.notes.push(note);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		releaseQueue() {
 | 
					 | 
				
			||||||
			for (const n of this.queue) {
 | 
					 | 
				
			||||||
				this.prepend(n, true);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			this.queue = [];
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		onScroll() {
 | 
					 | 
				
			||||||
			if (this.isScrollTop()) {
 | 
					 | 
				
			||||||
				this.releaseQueue();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (this.$store.state.settings.fetchOnScroll) {
 | 
					 | 
				
			||||||
				const current = window.scrollY + window.innerHeight;
 | 
					 | 
				
			||||||
				if (current > document.body.offsetHeight - 8) this.fetchMore();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<div>
 | 
				
			||||||
	<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
 | 
						<mk-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')">
 | 
				
			||||||
		<template #header>
 | 
							<template #header>
 | 
				
			||||||
			<slot></slot>
 | 
								<slot></slot>
 | 
				
			||||||
		</template>
 | 
							</template>
 | 
				
			||||||
| 
						 | 
					@ -11,36 +11,23 @@
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	props: ['list'],
 | 
						props: ['list'],
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			date: null,
 | 
								date: null,
 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/user-list-timeline', {
 | 
								pagination: {
 | 
				
			||||||
				listId: this.list.id,
 | 
									endpoint: 'notes/user-list-timeline',
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									limit: 10,
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									params: init => ({
 | 
				
			||||||
				untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
										listId: this.list.id,
 | 
				
			||||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
										untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
										includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
				
			||||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
										includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
				
			||||||
			}).then(notes => {
 | 
										includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
									})
 | 
				
			||||||
					notes.pop();
 | 
								}
 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	watch: {
 | 
						watch: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,83 +0,0 @@
 | 
				
			||||||
<template>
 | 
					 | 
				
			||||||
<div class="ecsvsegy" v-if="!fetching">
 | 
					 | 
				
			||||||
	<sequential-entrance animation="entranceFromTop" delay="25">
 | 
					 | 
				
			||||||
		<template v-for="favorite in favorites">
 | 
					 | 
				
			||||||
			<mk-note-detail class="post" :note="favorite.note" :key="favorite.note.id"/>
 | 
					 | 
				
			||||||
		</template>
 | 
					 | 
				
			||||||
	</sequential-entrance>
 | 
					 | 
				
			||||||
	<div class="more" v-if="existMore">
 | 
					 | 
				
			||||||
		<ui-button inline @click="fetchMore()">{{ $t('@.load-more') }}</ui-button>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue';
 | 
					 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	i18n: i18n('.vue'),
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			fetching: true,
 | 
					 | 
				
			||||||
			favorites: [],
 | 
					 | 
				
			||||||
			existMore: false,
 | 
					 | 
				
			||||||
			moreFetching: false
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		this.fetch();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		fetch() {
 | 
					 | 
				
			||||||
			Progress.start();
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.$root.api('i/favorites', {
 | 
					 | 
				
			||||||
				limit: 11
 | 
					 | 
				
			||||||
			}).then(favorites => {
 | 
					 | 
				
			||||||
				if (favorites.length == 11) {
 | 
					 | 
				
			||||||
					this.existMore = true;
 | 
					 | 
				
			||||||
					favorites.pop();
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				this.favorites = favorites;
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				Progress.done();
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		fetchMore() {
 | 
					 | 
				
			||||||
			this.moreFetching = true;
 | 
					 | 
				
			||||||
			this.$root.api('i/favorites', {
 | 
					 | 
				
			||||||
				limit: 11,
 | 
					 | 
				
			||||||
				untilId: this.favorites[this.favorites.length - 1].id
 | 
					 | 
				
			||||||
			}).then(favorites => {
 | 
					 | 
				
			||||||
				if (favorites.length == 11) {
 | 
					 | 
				
			||||||
					this.existMore = true;
 | 
					 | 
				
			||||||
					favorites.pop();
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					this.existMore = false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				this.favorites = this.favorites.concat(favorites);
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
.ecsvsegy
 | 
					 | 
				
			||||||
	margin 0 auto
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> * > .post
 | 
					 | 
				
			||||||
		margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> .more
 | 
					 | 
				
			||||||
		margin 32px 16px 16px 16px
 | 
					 | 
				
			||||||
		text-align center
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,55 +0,0 @@
 | 
				
			||||||
<template>
 | 
					 | 
				
			||||||
<div class="glowckho" v-if="!fetching">
 | 
					 | 
				
			||||||
	<sequential-entrance animation="entranceFromTop" delay="25">
 | 
					 | 
				
			||||||
		<template v-for="note in notes">
 | 
					 | 
				
			||||||
			<mk-note-detail class="post" :note="note" :key="note.id"/>
 | 
					 | 
				
			||||||
		</template>
 | 
					 | 
				
			||||||
	</sequential-entrance>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue';
 | 
					 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			fetching: true,
 | 
					 | 
				
			||||||
			notes: [],
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		this.fetch();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		fetch() {
 | 
					 | 
				
			||||||
			Progress.start();
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.$root.api('notes/featured', {
 | 
					 | 
				
			||||||
				limit: 30
 | 
					 | 
				
			||||||
			}).then(notes => {
 | 
					 | 
				
			||||||
				notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
 | 
					 | 
				
			||||||
				this.notes = notes;
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				Progress.done();
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
.glowckho
 | 
					 | 
				
			||||||
	margin 0 auto
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> * > .post
 | 
					 | 
				
			||||||
		margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	> .more
 | 
					 | 
				
			||||||
		margin 32px 16px 16px 16px
 | 
					 | 
				
			||||||
		text-align center
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<div>
 | 
				
			||||||
	<mk-notes ref="timeline" :make-promise="makePromise" @inited="inited">
 | 
						<mk-notes ref="timeline" :pagination="pagination" @inited="inited">
 | 
				
			||||||
		<template #header>
 | 
							<template #header>
 | 
				
			||||||
			<header class="oxgbmvii">
 | 
								<header class="oxgbmvii">
 | 
				
			||||||
				<span><fa icon="search"/> {{ q }}</span>
 | 
									<span><fa icon="search"/> {{ q }}</span>
 | 
				
			||||||
| 
						 | 
					@ -16,30 +16,15 @@ import i18n from '../../../i18n';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
import { genSearchQuery } from '../../../common/scripts/gen-search-query';
 | 
					import { genSearchQuery } from '../../../common/scripts/gen-search-query';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const limit = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('desktop/views/pages/search.vue'),
 | 
						i18n: i18n('desktop/views/pages/search.vue'),
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			makePromise: async cursor => this.$root.api('notes/search', {
 | 
								pagination: {
 | 
				
			||||||
				limit: limit + 1,
 | 
									endpoint: 'notes/search',
 | 
				
			||||||
				offset: cursor ? cursor : undefined,
 | 
									limit: 20,
 | 
				
			||||||
				...(await genSearchQuery(this, this.q))
 | 
									params: () => genSearchQuery(this, this.q)
 | 
				
			||||||
			}).then(notes => {
 | 
								}
 | 
				
			||||||
				if (notes.length == limit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						cursor: cursor ? cursor + limit : limit
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<div>
 | 
				
			||||||
	<mk-notes ref="timeline" :make-promise="makePromise" @inited="inited">
 | 
						<mk-notes ref="timeline" :pagination="pagination" @inited="inited">
 | 
				
			||||||
		<template #header>
 | 
							<template #header>
 | 
				
			||||||
			<header class="wqraeznr">
 | 
								<header class="wqraeznr">
 | 
				
			||||||
				<span><fa icon="hashtag"/> {{ $route.params.tag }}</span>
 | 
									<span><fa icon="hashtag"/> {{ $route.params.tag }}</span>
 | 
				
			||||||
| 
						 | 
					@ -15,30 +15,17 @@ import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const limit = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('desktop/views/pages/tag.vue'),
 | 
						i18n: i18n('desktop/views/pages/tag.vue'),
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/search-by-tag', {
 | 
								pagination: {
 | 
				
			||||||
				limit: limit + 1,
 | 
									endpoint: 'notes/search-by-tag',
 | 
				
			||||||
				offset: cursor ? cursor : undefined,
 | 
									limit: 20,
 | 
				
			||||||
				tag: this.$route.params.tag
 | 
									params: {
 | 
				
			||||||
			}).then(notes => {
 | 
										tag: this.$route.params.tag
 | 
				
			||||||
				if (notes.length == limit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						cursor: cursor ? cursor + limit : limit
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			})
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	watch: {
 | 
						watch: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<div>
 | 
				
			||||||
	<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
 | 
						<mk-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')">
 | 
				
			||||||
		<template #header>
 | 
							<template #header>
 | 
				
			||||||
			<slot></slot>
 | 
								<slot></slot>
 | 
				
			||||||
			<div v-if="src == 'home' && alone" class="ibpylqas">
 | 
								<div v-if="src == 'home' && alone" class="ibpylqas">
 | 
				
			||||||
| 
						 | 
					@ -16,8 +16,6 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('desktop/views/components/timeline.core.vue'),
 | 
						i18n: i18n('desktop/views/components/timeline.core.vue'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,7 +40,7 @@ export default Vue.extend({
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			query: {},
 | 
								query: {},
 | 
				
			||||||
			endpoint: null,
 | 
								endpoint: null,
 | 
				
			||||||
			makePromise: null
 | 
								pagination: null
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,25 +107,14 @@ export default Vue.extend({
 | 
				
			||||||
			this.connection.on('mention', onNote);
 | 
								this.connection.on('mention', onNote);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.makePromise = cursor => this.$root.api(this.endpoint, {
 | 
							this.pagination = {
 | 
				
			||||||
			limit: fetchLimit + 1,
 | 
								endpoint: this.endpoint,
 | 
				
			||||||
			untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
								limit: 10,
 | 
				
			||||||
			untilId: cursor ? cursor : undefined,
 | 
								params: init => ({
 | 
				
			||||||
			...this.baseQuery, ...this.query
 | 
									untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
		}).then(notes => {
 | 
									...this.baseQuery, ...this.query
 | 
				
			||||||
			if (notes.length == fetchLimit + 1) {
 | 
								})
 | 
				
			||||||
				notes.pop();
 | 
							};
 | 
				
			||||||
				return {
 | 
					 | 
				
			||||||
					notes: notes,
 | 
					 | 
				
			||||||
					more: true
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				return {
 | 
					 | 
				
			||||||
					notes: notes,
 | 
					 | 
				
			||||||
					more: false
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<div>
 | 
				
			||||||
	<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')">
 | 
						<mk-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')">
 | 
				
			||||||
		<template #header>
 | 
							<template #header>
 | 
				
			||||||
			<header class="oh5y2r7l5lx8j6jj791ykeiwgihheguk">
 | 
								<header class="kugajpep">
 | 
				
			||||||
				<span :data-active="mode == 'default'" @click="mode = 'default'"><fa :icon="['far', 'comment-alt']"/> {{ $t('default') }}</span>
 | 
									<span :data-active="mode == 'default'" @click="mode = 'default'"><fa :icon="['far', 'comment-alt']"/> {{ $t('default') }}</span>
 | 
				
			||||||
				<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'"><fa icon="comments"/> {{ $t('with-replies') }}</span>
 | 
									<span :data-active="mode == 'with-replies'" @click="mode = 'with-replies'"><fa icon="comments"/> {{ $t('with-replies') }}</span>
 | 
				
			||||||
				<span :data-active="mode == 'with-media'" @click="mode = 'with-media'"><fa :icon="['far', 'images']"/> {{ $t('with-media') }}</span>
 | 
									<span :data-active="mode == 'with-media'" @click="mode = 'with-media'"><fa :icon="['far', 'images']"/> {{ $t('with-media') }}</span>
 | 
				
			||||||
| 
						 | 
					@ -17,8 +17,6 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../../i18n';
 | 
					import i18n from '../../../../i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('desktop/views/pages/user/user.timeline.vue'),
 | 
						i18n: i18n('desktop/views/pages/user/user.timeline.vue'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,28 +28,17 @@ export default Vue.extend({
 | 
				
			||||||
			mode: 'default',
 | 
								mode: 'default',
 | 
				
			||||||
			unreadCount: 0,
 | 
								unreadCount: 0,
 | 
				
			||||||
			date: null,
 | 
								date: null,
 | 
				
			||||||
			makePromise: cursor => this.$root.api('users/notes', {
 | 
								pagination: {
 | 
				
			||||||
				userId: this.user.id,
 | 
									endpoint: 'users/notes',
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									limit: 10,
 | 
				
			||||||
				includeReplies: this.mode == 'with-replies',
 | 
									params: init => ({
 | 
				
			||||||
				includeMyRenotes: this.mode != 'my-posts',
 | 
										userId: this.user.id,
 | 
				
			||||||
				withFiles: this.mode == 'with-media',
 | 
										untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
				untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
										includeReplies: this.mode == 'with-replies',
 | 
				
			||||||
				untilId: cursor ? cursor : undefined
 | 
										includeMyRenotes: this.mode != 'my-posts',
 | 
				
			||||||
			}).then(notes => {
 | 
										withFiles: this.mode == 'with-media',
 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
									})
 | 
				
			||||||
					notes.pop();
 | 
								}
 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -88,7 +75,7 @@ export default Vue.extend({
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
.oh5y2r7l5lx8j6jj791ykeiwgihheguk
 | 
					.kugajpep
 | 
				
			||||||
	padding 0 8px
 | 
						padding 0 8px
 | 
				
			||||||
	z-index 10
 | 
						z-index 10
 | 
				
			||||||
	background var(--faceHeader)
 | 
						background var(--faceHeader)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -123,10 +123,10 @@ init((launch, os) => {
 | 
				
			||||||
					{ path: '/notes/:note', name: 'note', component: () => import('../common/views/deck/deck.note-column.vue').then(m => m.default) },
 | 
										{ path: '/notes/:note', name: 'note', component: () => import('../common/views/deck/deck.note-column.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/search', component: () => import('../common/views/deck/deck.search-column.vue').then(m => m.default) },
 | 
										{ path: '/search', component: () => import('../common/views/deck/deck.search-column.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/tags/:tag', name: 'tag', component: () => import('../common/views/deck/deck.hashtag-column.vue').then(m => m.default) },
 | 
										{ path: '/tags/:tag', name: 'tag', component: () => import('../common/views/deck/deck.hashtag-column.vue').then(m => m.default) },
 | 
				
			||||||
					{ path: '/featured', name: 'featured', component: () => import('../common/views/deck/deck.featured-column.vue').then(m => m.default) },
 | 
										{ path: '/featured', name: 'featured', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/featured.vue').then(m => m.default), platform: 'deck' }) },
 | 
				
			||||||
					{ path: '/explore', name: 'explore', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default) }) },
 | 
										{ path: '/explore', name: 'explore', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default) }) },
 | 
				
			||||||
					{ path: '/explore/tags/:tag', name: 'explore-tag', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default), tag: route.params.tag }) },
 | 
										{ path: '/explore/tags/:tag', name: 'explore-tag', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default), tag: route.params.tag }) },
 | 
				
			||||||
					{ path: '/i/favorites', component: () => import('../common/views/deck/deck.favorites-column.vue').then(m => m.default) },
 | 
										{ path: '/i/favorites', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/favorites.vue').then(m => m.default), platform: 'deck' }) },
 | 
				
			||||||
					{ path: '/i/pages', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/pages.vue').then(m => m.default) }) },
 | 
										{ path: '/i/pages', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/pages.vue').then(m => m.default) }) },
 | 
				
			||||||
					{ path: '/i/lists', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) }) },
 | 
										{ path: '/i/lists', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) }) },
 | 
				
			||||||
					{ path: '/i/lists/:listId', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-list-editor.vue').then(m => m.default), listId: route.params.listId }) },
 | 
										{ path: '/i/lists/:listId', component: DeckColumn, props: route => ({ component: () => import('../common/views/pages/user-list-editor.vue').then(m => m.default), listId: route.params.listId }) },
 | 
				
			||||||
| 
						 | 
					@ -138,7 +138,7 @@ init((launch, os) => {
 | 
				
			||||||
		]),
 | 
							]),
 | 
				
			||||||
			{ path: '/signup', name: 'signup', component: MkSignup },
 | 
								{ path: '/signup', name: 'signup', component: MkSignup },
 | 
				
			||||||
			{ path: '/i/settings', name: 'settings', component: () => import('./views/pages/settings.vue').then(m => m.default) },
 | 
								{ path: '/i/settings', name: 'settings', component: () => import('./views/pages/settings.vue').then(m => m.default) },
 | 
				
			||||||
			{ path: '/i/favorites', name: 'favorites', component: MkFavorites },
 | 
								{ path: '/i/favorites', name: 'favorites', component: UI, props: route => ({ component: () => import('../common/views/pages/favorites.vue').then(m => m.default), platform: 'mobile' }) },
 | 
				
			||||||
			{ path: '/i/pages', name: 'pages', component: UI, props: route => ({ component: () => import('../common/views/pages/pages.vue').then(m => m.default) }) },
 | 
								{ path: '/i/pages', name: 'pages', component: UI, props: route => ({ component: () => import('../common/views/pages/pages.vue').then(m => m.default) }) },
 | 
				
			||||||
			{ path: '/i/lists', name: 'user-lists', component: UI, props: route => ({ component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) }) },
 | 
								{ path: '/i/lists', name: 'user-lists', component: UI, props: route => ({ component: () => import('../common/views/pages/user-lists.vue').then(m => m.default) }) },
 | 
				
			||||||
			{ path: '/i/lists/:list', component: UI, props: route => ({ component: () => import('../common/views/pages/user-list-editor.vue').then(m => m.default), listId: route.params.list }) },
 | 
								{ path: '/i/lists/:list', component: UI, props: route => ({ component: () => import('../common/views/pages/user-list-editor.vue').then(m => m.default), listId: route.params.list }) },
 | 
				
			||||||
| 
						 | 
					@ -157,7 +157,7 @@ init((launch, os) => {
 | 
				
			||||||
			{ path: '/selectdrive', component: MkSelectDrive },
 | 
								{ path: '/selectdrive', component: MkSelectDrive },
 | 
				
			||||||
			{ path: '/search', component: MkSearch },
 | 
								{ path: '/search', component: MkSearch },
 | 
				
			||||||
			{ path: '/tags/:tag', component: MkTag },
 | 
								{ path: '/tags/:tag', component: MkTag },
 | 
				
			||||||
			{ path: '/featured', name: 'featured', component: () => import('./views/pages/featured.vue').then(m => m.default) },
 | 
								{ path: '/featured', name: 'featured', component: UI, props: route => ({ component: () => import('../common/views/pages/featured.vue').then(m => m.default), platform: 'mobile' }) },
 | 
				
			||||||
			{ path: '/explore', name: 'explore', component: UI, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default) }) },
 | 
								{ path: '/explore', name: 'explore', component: UI, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default) }) },
 | 
				
			||||||
			{ path: '/explore/tags/:tag', name: 'explore-tag', component: UI, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default), tag: route.params.tag }) },
 | 
								{ path: '/explore/tags/:tag', name: 'explore-tag', component: UI, props: route => ({ component: () => import('../common/views/pages/explore.vue').then(m => m.default), tag: route.params.tag }) },
 | 
				
			||||||
			{ path: '/share', component: MkShare },
 | 
								{ path: '/share', component: MkShare },
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										52
									
								
								src/client/app/mobile/views/components/detail-notes.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/client/app/mobile/views/components/detail-notes.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,52 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div class="fdcvngpy">
 | 
				
			||||||
 | 
						<sequential-entrance animation="entranceFromTop" delay="25">
 | 
				
			||||||
 | 
							<template v-for="note in notes">
 | 
				
			||||||
 | 
								<mk-note-detail class="post" :note="note" :key="note.id"/>
 | 
				
			||||||
 | 
							</template>
 | 
				
			||||||
 | 
						</sequential-entrance>
 | 
				
			||||||
 | 
						<ui-button v-if="more" @click="fetchMore()">{{ $t('@.load-more') }}</ui-button>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					import paging from '../../../common/scripts/paging';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mixins: [
 | 
				
			||||||
 | 
							paging({
 | 
				
			||||||
 | 
								captureWindowScroll: true,
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						props: {
 | 
				
			||||||
 | 
							pagination: {
 | 
				
			||||||
 | 
								required: true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							extract: {
 | 
				
			||||||
 | 
								required: false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						computed: {
 | 
				
			||||||
 | 
							notes() {
 | 
				
			||||||
 | 
								return this.extract ? this.extract(this.items) : this.items;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					.fdcvngpy
 | 
				
			||||||
 | 
						> * > .post
 | 
				
			||||||
 | 
							margin-bottom 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@media (min-width 500px)
 | 
				
			||||||
 | 
							> * > .post
 | 
				
			||||||
 | 
								margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,8 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="ivaojijs" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
					<div class="ivaojijs" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
 | 
				
			||||||
	<div class="empty" v-if="notes.length == 0 && !fetching && inited">{{ $t('@.no-notes') }}</div>
 | 
						<div class="empty" v-if="empty">{{ $t('@.no-notes') }}</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<mk-error v-if="!fetching && !inited" @retry="init()"/>
 | 
						<mk-error v-if="error" @retry="init()"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="placeholder" v-if="fetching">
 | 
						<div class="placeholder" v-if="fetching">
 | 
				
			||||||
		<template v-for="i in 10">
 | 
							<template v-for="i in 10">
 | 
				
			||||||
| 
						 | 
					@ -13,8 +13,8 @@
 | 
				
			||||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
						<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
				
			||||||
	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="transition" tag="div">
 | 
						<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notes" class="transition" tag="div">
 | 
				
			||||||
		<template v-for="(note, i) in _notes">
 | 
							<template v-for="(note, i) in _notes">
 | 
				
			||||||
			<mk-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
 | 
								<mk-note :note="note" :key="note.id"/>
 | 
				
			||||||
			<p class="date" :key="note.id + '_date'" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
 | 
								<p class="date" :key="note.id + '_date'" v-if="i != items.length - 1 && note._date != _notes[i + 1]._date">
 | 
				
			||||||
				<span><fa icon="angle-up"/>{{ note._datetext }}</span>
 | 
									<span><fa icon="angle-up"/>{{ note._datetext }}</span>
 | 
				
			||||||
				<span><fa icon="angle-down"/>{{ _notes[i + 1]._datetext }}</span>
 | 
									<span><fa icon="angle-down"/>{{ _notes[i + 1]._datetext }}</span>
 | 
				
			||||||
			</p>
 | 
								</p>
 | 
				
			||||||
| 
						 | 
					@ -34,157 +34,52 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
import shouldMuteNote from '../../../common/scripts/should-mute-note';
 | 
					import shouldMuteNote from '../../../common/scripts/should-mute-note';
 | 
				
			||||||
 | 
					import paging from '../../../common/scripts/paging';
 | 
				
			||||||
const displayLimit = 30;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n(),
 | 
						i18n: i18n(),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	props: {
 | 
						mixins: [
 | 
				
			||||||
		makePromise: {
 | 
							paging({
 | 
				
			||||||
			required: true
 | 
								captureWindowScroll: true,
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
								onQueueChanged: (self, x) => {
 | 
				
			||||||
		return {
 | 
									if (x.length > 0) {
 | 
				
			||||||
			notes: [],
 | 
										self.$store.commit('indicate', true);
 | 
				
			||||||
			queue: [],
 | 
									} else {
 | 
				
			||||||
			fetching: true,
 | 
										self.$store.commit('indicate', false);
 | 
				
			||||||
			moreFetching: false,
 | 
									}
 | 
				
			||||||
			inited: false,
 | 
								},
 | 
				
			||||||
			more: false
 | 
					
 | 
				
			||||||
		};
 | 
								onPrepend: (self, note) => {
 | 
				
			||||||
 | 
									// 弾く
 | 
				
			||||||
 | 
									if (shouldMuteNote(self.$store.state.i, self.$store.state.settings, note)) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
 | 
				
			||||||
 | 
									if (document.hidden || !self.isScrollTop()) {
 | 
				
			||||||
 | 
										self.$store.commit('pushBehindNote', note);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}),
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						props: {
 | 
				
			||||||
 | 
							pagination: {
 | 
				
			||||||
 | 
								required: true
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
		_notes(): any[] {
 | 
							_notes(): any[] {
 | 
				
			||||||
			return (this.notes as any).map(note => {
 | 
								return (this.items as any).map(item => {
 | 
				
			||||||
				const date = new Date(note.createdAt).getDate();
 | 
									const date = new Date(item.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(note.createdAt).getMonth() + 1;
 | 
									const month = new Date(item.createdAt).getMonth() + 1;
 | 
				
			||||||
				note._date = date;
 | 
									item._date = date;
 | 
				
			||||||
				note._datetext = this.$t('@.month-and-day').replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
									item._datetext = this.$t('@.month-and-day').replace('{month}', month.toString()).replace('{day}', date.toString());
 | 
				
			||||||
				return note;
 | 
									return item;
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					 | 
				
			||||||
	watch: {
 | 
					 | 
				
			||||||
		queue(x) {
 | 
					 | 
				
			||||||
			if (x.length > 0) {
 | 
					 | 
				
			||||||
				this.$store.commit('indicate', true);
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				this.$store.commit('indicate', false);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		this.init();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mounted() {
 | 
					 | 
				
			||||||
		window.addEventListener('scroll', this.onScroll, { passive: true });
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	beforeDestroy() {
 | 
					 | 
				
			||||||
		window.removeEventListener('scroll', this.onScroll);
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		isScrollTop() {
 | 
					 | 
				
			||||||
			return window.scrollY <= 8;
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		onNoteUpdated(i, note) {
 | 
					 | 
				
			||||||
			Vue.set((this as any).notes, i, note);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		reload() {
 | 
					 | 
				
			||||||
			this.queue = [];
 | 
					 | 
				
			||||||
			this.notes = [];
 | 
					 | 
				
			||||||
			this.init();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async init() {
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
			await (this.makePromise()).then(x => {
 | 
					 | 
				
			||||||
				if (Array.isArray(x)) {
 | 
					 | 
				
			||||||
					this.notes = x;
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					this.notes = x.notes;
 | 
					 | 
				
			||||||
					this.more = x.more;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				this.inited = true;
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
				this.$emit('inited');
 | 
					 | 
				
			||||||
			}, e => {
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		async fetchMore() {
 | 
					 | 
				
			||||||
			if (!this.more || this.moreFetching || this.notes.length === 0) return;
 | 
					 | 
				
			||||||
			this.moreFetching = true;
 | 
					 | 
				
			||||||
			await (this.makePromise(this.notes[this.notes.length - 1].id)).then(x => {
 | 
					 | 
				
			||||||
				this.notes = this.notes.concat(x.notes);
 | 
					 | 
				
			||||||
				this.more = x.more;
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			}, e => {
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		prepend(note, silent = false) {
 | 
					 | 
				
			||||||
			// 弾く
 | 
					 | 
				
			||||||
			if (shouldMuteNote(this.$store.state.i, this.$store.state.settings, note)) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			// タブが非表示またはスクロール位置が最上部ではないならタイトルで通知
 | 
					 | 
				
			||||||
			if (document.hidden || !this.isScrollTop()) {
 | 
					 | 
				
			||||||
				this.$store.commit('pushBehindNote', note);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (this.isScrollTop()) {
 | 
					 | 
				
			||||||
				// Prepend the note
 | 
					 | 
				
			||||||
				this.notes.unshift(note);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// オーバーフローしたら古い投稿は捨てる
 | 
					 | 
				
			||||||
				if (this.notes.length >= displayLimit) {
 | 
					 | 
				
			||||||
					this.notes = this.notes.slice(0, displayLimit);
 | 
					 | 
				
			||||||
					this.more = true;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				this.queue.push(note);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		append(note) {
 | 
					 | 
				
			||||||
			this.notes.push(note);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		releaseQueue() {
 | 
					 | 
				
			||||||
			for (const n of this.queue) {
 | 
					 | 
				
			||||||
				this.prepend(n, true);
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			this.queue = [];
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		onScroll() {
 | 
					 | 
				
			||||||
			if (this.isScrollTop()) {
 | 
					 | 
				
			||||||
				this.releaseQueue();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (this.$store.state.settings.fetchOnScroll) {
 | 
					 | 
				
			||||||
				// 親要素が display none だったら弾く
 | 
					 | 
				
			||||||
				// https://github.com/syuilo/misskey/issues/1569
 | 
					 | 
				
			||||||
				// http://d.hatena.ne.jp/favril/20091105/1257403319
 | 
					 | 
				
			||||||
				if (this.$el.offsetHeight == 0) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				const current = window.scrollY + window.innerHeight;
 | 
					 | 
				
			||||||
				if (current > document.body.offsetHeight - 8) this.fetchMore();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,15 +71,15 @@
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template v-if="notification.type == 'quote'">
 | 
						<template v-if="notification.type == 'quote'">
 | 
				
			||||||
		<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
 | 
							<mk-note :note="notification.note"/>
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template v-if="notification.type == 'reply'">
 | 
						<template v-if="notification.type == 'reply'">
 | 
				
			||||||
		<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
 | 
							<mk-note :note="notification.note"/>
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template v-if="notification.type == 'mention'">
 | 
						<template v-if="notification.type == 'mention'">
 | 
				
			||||||
		<mk-note :note="notification.note" @update:note="onNoteUpdated"/>
 | 
							<mk-note :note="notification.note"/>
 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					@ -95,17 +95,6 @@ export default Vue.extend({
 | 
				
			||||||
			getNoteSummary
 | 
								getNoteSummary
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		onNoteUpdated(note) {
 | 
					 | 
				
			||||||
			switch (this.notification.type) {
 | 
					 | 
				
			||||||
				case 'quote':
 | 
					 | 
				
			||||||
				case 'reply':
 | 
					 | 
				
			||||||
				case 'mention':
 | 
					 | 
				
			||||||
					Vue.set(this.notification, 'note', note);
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,41 +10,49 @@
 | 
				
			||||||
	<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications" tag="div">
 | 
						<component :is="!$store.state.device.reduceMotion ? 'transition-group' : 'div'" name="mk-notifications" class="transition notifications" tag="div">
 | 
				
			||||||
		<template v-for="(notification, i) in _notifications">
 | 
							<template v-for="(notification, i) in _notifications">
 | 
				
			||||||
			<mk-notification :notification="notification" :key="notification.id"/>
 | 
								<mk-notification :notification="notification" :key="notification.id"/>
 | 
				
			||||||
			<p class="date" :key="notification.id + '_date'" v-if="i != notifications.length - 1 && notification._date != _notifications[i + 1]._date">
 | 
								<p class="date" :key="notification.id + '_date'" v-if="i != items.length - 1 && notification._date != _notifications[i + 1]._date">
 | 
				
			||||||
				<span><fa icon="angle-up"/>{{ notification._datetext }}</span>
 | 
									<span><fa icon="angle-up"/>{{ notification._datetext }}</span>
 | 
				
			||||||
				<span><fa icon="angle-down"/>{{ _notifications[i + 1]._datetext }}</span>
 | 
									<span><fa icon="angle-down"/>{{ _notifications[i + 1]._datetext }}</span>
 | 
				
			||||||
			</p>
 | 
								</p>
 | 
				
			||||||
		</template>
 | 
							</template>
 | 
				
			||||||
	</component>
 | 
						</component>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<button class="more" v-if="moreNotifications" @click="fetchMoreNotifications" :disabled="fetchingMoreNotifications">
 | 
						<button class="more" v-if="more" @click="fetchMore" :disabled="moreFetching">
 | 
				
			||||||
		<template v-if="fetchingMoreNotifications"><fa icon="spinner" pulse fixed-width/></template>
 | 
							<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>
 | 
				
			||||||
		{{ fetchingMoreNotifications ? $t('@.loading') : $t('@.load-more') }}
 | 
							{{ moreFetching ? $t('@.loading') : $t('@.load-more') }}
 | 
				
			||||||
	</button>
 | 
						</button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<p class="empty" v-if="notifications.length == 0 && !fetching">{{ $t('empty') }}</p>
 | 
						<p class="empty" v-if="empty">{{ $t('empty') }}</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<mk-error v-if="error" @retry="init()"/>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					import paging from '../../../common/scripts/paging';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('mobile/views/components/notifications.vue'),
 | 
						i18n: i18n('mobile/views/components/notifications.vue'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mixins: [
 | 
				
			||||||
 | 
							paging({}),
 | 
				
			||||||
 | 
						],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			fetching: true,
 | 
								connection: null,
 | 
				
			||||||
			fetchingMoreNotifications: false,
 | 
								pagination: {
 | 
				
			||||||
			notifications: [],
 | 
									endpoint: 'i/notifications',
 | 
				
			||||||
			moreNotifications: false,
 | 
									limit: 15,
 | 
				
			||||||
			connection: null
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
		_notifications(): any[] {
 | 
							_notifications(): any[] {
 | 
				
			||||||
			return (this.notifications as any).map(notification => {
 | 
								return (this.items as any).map(notification => {
 | 
				
			||||||
				const date = new Date(notification.createdAt).getDate();
 | 
									const date = new Date(notification.createdAt).getDate();
 | 
				
			||||||
				const month = new Date(notification.createdAt).getMonth() + 1;
 | 
									const month = new Date(notification.createdAt).getMonth() + 1;
 | 
				
			||||||
				notification._date = date;
 | 
									notification._date = date;
 | 
				
			||||||
| 
						 | 
					@ -55,76 +63,23 @@ export default Vue.extend({
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		window.addEventListener('scroll', this.onScroll, { passive: true });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.connection = this.$root.stream.useSharedConnection('main');
 | 
							this.connection = this.$root.stream.useSharedConnection('main');
 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.connection.on('notification', this.onNotification);
 | 
							this.connection.on('notification', this.onNotification);
 | 
				
			||||||
 | 
					 | 
				
			||||||
		const max = 15;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.$root.api('i/notifications', {
 | 
					 | 
				
			||||||
			limit: max + 1
 | 
					 | 
				
			||||||
		}).then(notifications => {
 | 
					 | 
				
			||||||
			if (notifications.length == max + 1) {
 | 
					 | 
				
			||||||
				this.moreNotifications = true;
 | 
					 | 
				
			||||||
				notifications.pop();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.notifications = notifications;
 | 
					 | 
				
			||||||
			this.fetching = false;
 | 
					 | 
				
			||||||
			this.$emit('fetched');
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	beforeDestroy() {
 | 
						beforeDestroy() {
 | 
				
			||||||
		window.removeEventListener('scroll', this.onScroll);
 | 
					 | 
				
			||||||
		this.connection.dispose();
 | 
							this.connection.dispose();
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		fetchMoreNotifications() {
 | 
					 | 
				
			||||||
			if (this.fetchingMoreNotifications) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.fetchingMoreNotifications = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const max = 30;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.$root.api('i/notifications', {
 | 
					 | 
				
			||||||
				limit: max + 1,
 | 
					 | 
				
			||||||
				untilId: this.notifications[this.notifications.length - 1].id
 | 
					 | 
				
			||||||
			}).then(notifications => {
 | 
					 | 
				
			||||||
				if (notifications.length == max + 1) {
 | 
					 | 
				
			||||||
					this.moreNotifications = true;
 | 
					 | 
				
			||||||
					notifications.pop();
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					this.moreNotifications = false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				this.notifications = this.notifications.concat(notifications);
 | 
					 | 
				
			||||||
				this.fetchingMoreNotifications = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		onNotification(notification) {
 | 
							onNotification(notification) {
 | 
				
			||||||
			// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
 | 
								// TODO: ユーザーが画面を見てないと思われるとき(ブラウザやタブがアクティブじゃないなど)は送信しない
 | 
				
			||||||
			this.$root.stream.send('readNotification', {
 | 
								this.$root.stream.send('readNotification', {
 | 
				
			||||||
				id: notification.id
 | 
									id: notification.id
 | 
				
			||||||
			});
 | 
								});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			this.notifications.unshift(notification);
 | 
								this.prepend(notification);
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					 | 
				
			||||||
		onScroll() {
 | 
					 | 
				
			||||||
			if (this.$store.state.settings.fetchOnScroll) {
 | 
					 | 
				
			||||||
				// 親要素が display none だったら弾く
 | 
					 | 
				
			||||||
				// https://github.com/syuilo/misskey/issues/1569
 | 
					 | 
				
			||||||
				// http://d.hatena.ne.jp/favril/20091105/1257403319
 | 
					 | 
				
			||||||
				if (this.$el.offsetHeight == 0) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				const current = window.scrollY + window.innerHeight;
 | 
					 | 
				
			||||||
				if (current > document.body.offsetHeight - 8) this.fetchMoreNotifications();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,10 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div>
 | 
					<mk-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
	<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	props: ['list'],
 | 
						props: ['list'],
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,28 +12,17 @@ export default Vue.extend({
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			date: null,
 | 
								date: null,
 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/user-list-timeline', {
 | 
								pagination: {
 | 
				
			||||||
				listId: this.list.id,
 | 
									endpoint: 'notes/user-list-timeline',
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									limit: 10,
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									params: init => ({
 | 
				
			||||||
				untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
										listId: this.list.id,
 | 
				
			||||||
				includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
										untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
				includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
										includeMyRenotes: this.$store.state.settings.showMyRenotes,
 | 
				
			||||||
				includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
										includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
 | 
				
			||||||
			}).then(notes => {
 | 
										includeLocalRenotes: this.$store.state.settings.showLocalRenotes
 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
									})
 | 
				
			||||||
					notes.pop();
 | 
								}
 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,15 +1,11 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="mk-user-timeline">
 | 
					<mk-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
	<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('mobile/views/components/user-timeline.vue'),
 | 
						i18n: i18n('mobile/views/components/user-timeline.vue'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -18,26 +14,15 @@ export default Vue.extend({
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			date: null,
 | 
								date: null,
 | 
				
			||||||
			makePromise: cursor => this.$root.api('users/notes', {
 | 
								pagination: {
 | 
				
			||||||
				userId: this.user.id,
 | 
									endpoint: 'users/notes',
 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
									limit: 10,
 | 
				
			||||||
				withFiles: this.withMedia,
 | 
									params: init => ({
 | 
				
			||||||
				untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
										userId: this.user.id,
 | 
				
			||||||
				untilId: cursor ? cursor : undefined
 | 
										withFiles: this.withMedia,
 | 
				
			||||||
			}).then(notes => {
 | 
										untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
									})
 | 
				
			||||||
					notes.pop();
 | 
								}
 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,86 +0,0 @@
 | 
				
			||||||
<template>
 | 
					 | 
				
			||||||
<mk-ui>
 | 
					 | 
				
			||||||
	<template #header><span style="margin-right:4px;"><fa icon="star"/></span>{{ $t('@.favorites') }}</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<main>
 | 
					 | 
				
			||||||
		<sequential-entrance animation="entranceFromTop" delay="25">
 | 
					 | 
				
			||||||
			<template v-for="favorite in favorites">
 | 
					 | 
				
			||||||
				<mk-note-detail class="post" :note="favorite.note" :key="favorite.note.id"/>
 | 
					 | 
				
			||||||
			</template>
 | 
					 | 
				
			||||||
		</sequential-entrance>
 | 
					 | 
				
			||||||
		<ui-button v-if="existMore" @click="fetchMore()">{{ $t('@.load-more') }}</ui-button>
 | 
					 | 
				
			||||||
	</main>
 | 
					 | 
				
			||||||
</mk-ui>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue';
 | 
					 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	i18n: i18n(),
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			fetching: true,
 | 
					 | 
				
			||||||
			favorites: [],
 | 
					 | 
				
			||||||
			existMore: false,
 | 
					 | 
				
			||||||
			moreFetching: false
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		this.fetch();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	mounted() {
 | 
					 | 
				
			||||||
		document.title = `${this.$root.instanceName} | ${this.$t('@.favorites')}`;
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		fetch() {
 | 
					 | 
				
			||||||
			Progress.start();
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.$root.api('i/favorites', {
 | 
					 | 
				
			||||||
				limit: 11
 | 
					 | 
				
			||||||
			}).then(favorites => {
 | 
					 | 
				
			||||||
				if (favorites.length == 11) {
 | 
					 | 
				
			||||||
					this.existMore = true;
 | 
					 | 
				
			||||||
					favorites.pop();
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				this.favorites = favorites;
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				Progress.done();
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		fetchMore() {
 | 
					 | 
				
			||||||
			this.moreFetching = true;
 | 
					 | 
				
			||||||
			this.$root.api('i/favorites', {
 | 
					 | 
				
			||||||
				limit: 11,
 | 
					 | 
				
			||||||
				untilId: this.favorites[this.favorites.length - 1].id
 | 
					 | 
				
			||||||
			}).then(favorites => {
 | 
					 | 
				
			||||||
				if (favorites.length == 11) {
 | 
					 | 
				
			||||||
					this.existMore = true;
 | 
					 | 
				
			||||||
					favorites.pop();
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					this.existMore = false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				this.favorites = this.favorites.concat(favorites);
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
main
 | 
					 | 
				
			||||||
	> * > .post
 | 
					 | 
				
			||||||
		margin-bottom 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@media (min-width 500px)
 | 
					 | 
				
			||||||
		> * > .post
 | 
					 | 
				
			||||||
			margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,61 +0,0 @@
 | 
				
			||||||
<template>
 | 
					 | 
				
			||||||
<mk-ui>
 | 
					 | 
				
			||||||
	<template #header><span style="margin-right:4px;"><fa :icon="faNewspaper"/></span>{{ $t('@.featured-notes') }}</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<main>
 | 
					 | 
				
			||||||
		<sequential-entrance animation="entranceFromTop" delay="25">
 | 
					 | 
				
			||||||
			<template v-for="note in notes">
 | 
					 | 
				
			||||||
				<mk-note-detail class="post" :note="note" :key="note.id"/>
 | 
					 | 
				
			||||||
			</template>
 | 
					 | 
				
			||||||
		</sequential-entrance>
 | 
					 | 
				
			||||||
	</main>
 | 
					 | 
				
			||||||
</mk-ui>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue';
 | 
					 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					 | 
				
			||||||
import { faNewspaper } from '@fortawesome/free-solid-svg-icons';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	i18n: i18n(''),
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			fetching: true,
 | 
					 | 
				
			||||||
			notes: [],
 | 
					 | 
				
			||||||
			faNewspaper
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		this.fetch();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		fetch() {
 | 
					 | 
				
			||||||
			Progress.start();
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.$root.api('notes/featured', {
 | 
					 | 
				
			||||||
				limit: 30
 | 
					 | 
				
			||||||
			}).then(notes => {
 | 
					 | 
				
			||||||
				notes.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
 | 
					 | 
				
			||||||
				this.notes = notes;
 | 
					 | 
				
			||||||
				this.fetching = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				Progress.done();
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
main
 | 
					 | 
				
			||||||
	> * > .post
 | 
					 | 
				
			||||||
		margin-bottom 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	@media (min-width 500px)
 | 
					 | 
				
			||||||
		> * > .post
 | 
					 | 
				
			||||||
			margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
| 
						 | 
					@ -7,7 +7,7 @@
 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</ui-container>
 | 
						</ui-container>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<mk-notes ref="timeline" :make-promise="makePromise" @inited="() => $emit('loaded')"/>
 | 
						<mk-notes ref="timeline" :pagination="pagination" @inited="() => $emit('loaded')"/>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,8 +15,6 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('mobile/views/pages/home.timeline.vue'),
 | 
						i18n: i18n('mobile/views/pages/home.timeline.vue'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -43,7 +41,7 @@ export default Vue.extend({
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			query: {},
 | 
								query: {},
 | 
				
			||||||
			endpoint: null,
 | 
								endpoint: null,
 | 
				
			||||||
			makePromise: null
 | 
								pagination: null
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -110,25 +108,14 @@ export default Vue.extend({
 | 
				
			||||||
			this.connection.on('mention', onNote);
 | 
								this.connection.on('mention', onNote);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.makePromise = cursor => this.$root.api(this.endpoint, {
 | 
							this.pagination = {
 | 
				
			||||||
			limit: fetchLimit + 1,
 | 
								endpoint: this.endpoint,
 | 
				
			||||||
			untilDate: cursor ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
								limit: 10,
 | 
				
			||||||
			untilId: cursor ? cursor : undefined,
 | 
								params: init => ({
 | 
				
			||||||
			...this.baseQuery, ...this.query
 | 
									untilDate: init ? undefined : (this.date ? this.date.getTime() : undefined),
 | 
				
			||||||
		}).then(notes => {
 | 
									...this.baseQuery, ...this.query
 | 
				
			||||||
			if (notes.length == fetchLimit + 1) {
 | 
								})
 | 
				
			||||||
				notes.pop();
 | 
							};
 | 
				
			||||||
				return {
 | 
					 | 
				
			||||||
					notes: notes,
 | 
					 | 
				
			||||||
					more: true
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				return {
 | 
					 | 
				
			||||||
					notes: notes,
 | 
					 | 
				
			||||||
					more: false
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
	<template #header><fa icon="search"/> {{ q }}</template>
 | 
						<template #header><fa icon="search"/> {{ q }}</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<main>
 | 
						<main>
 | 
				
			||||||
		<mk-notes ref="timeline" :make-promise="makePromise" @inited="inited"/>
 | 
							<mk-notes ref="timeline" :pagination="pagination" @inited="inited"/>
 | 
				
			||||||
	</main>
 | 
						</main>
 | 
				
			||||||
</mk-ui>
 | 
					</mk-ui>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					@ -14,42 +14,27 @@ import i18n from '../../../i18n';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
import { genSearchQuery } from '../../../common/scripts/gen-search-query';
 | 
					import { genSearchQuery } from '../../../common/scripts/gen-search-query';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const limit = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('mobile/views/pages/search.vue'),
 | 
						i18n: i18n('mobile/views/pages/search.vue'),
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			makePromise: async cursor => this.$root.api('notes/search', {
 | 
								pagination: {
 | 
				
			||||||
				limit: limit + 1,
 | 
									endpoint: 'notes/search',
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									limit: 20,
 | 
				
			||||||
				...(await genSearchQuery(this, this.q))
 | 
									params: () => genSearchQuery(this, this.q)
 | 
				
			||||||
			}).then(notes => {
 | 
								}
 | 
				
			||||||
				if (notes.length == limit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			})
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	watch: {
 | 
					 | 
				
			||||||
		$route() {
 | 
					 | 
				
			||||||
			this.$refs.timeline.reload();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	computed: {
 | 
						computed: {
 | 
				
			||||||
		q(): string {
 | 
							q(): string {
 | 
				
			||||||
			return this.$route.query.q;
 | 
								return this.$route.query.q;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
						watch: {
 | 
				
			||||||
 | 
							$route() {
 | 
				
			||||||
 | 
								this.$refs.timeline.reload();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = `${this.$t('search')}: ${this.q} | ${this.$root.instanceName}`;
 | 
							document.title = `${this.$t('search')}: ${this.q} | ${this.$root.instanceName}`;
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
	<template #header><span style="margin-right:4px;"><fa icon="hashtag"/></span>{{ $route.params.tag }}</template>
 | 
						<template #header><span style="margin-right:4px;"><fa icon="hashtag"/></span>{{ $route.params.tag }}</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<main>
 | 
						<main>
 | 
				
			||||||
		<mk-notes ref="timeline" :make-promise="makePromise" @inited="inited"/>
 | 
							<mk-notes ref="timeline" :pagination="pagination" @inited="inited"/>
 | 
				
			||||||
	</main>
 | 
						</main>
 | 
				
			||||||
</mk-ui>
 | 
					</mk-ui>
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
| 
						 | 
					@ -13,30 +13,17 @@ import Vue from 'vue';
 | 
				
			||||||
import i18n from '../../../i18n';
 | 
					import i18n from '../../../i18n';
 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const limit = 20;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	i18n: i18n('mobile/views/pages/tag.vue'),
 | 
						i18n: i18n('mobile/views/pages/tag.vue'),
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			makePromise: cursor => this.$root.api('notes/search-by-tag', {
 | 
								pagination: {
 | 
				
			||||||
				limit: limit + 1,
 | 
									endpoint: 'notes/search-by-tag',
 | 
				
			||||||
				untilId: cursor ? cursor : undefined,
 | 
									limit: 20,
 | 
				
			||||||
				tag: this.$route.params.tag
 | 
									params: {
 | 
				
			||||||
			}).then(notes => {
 | 
										tag: this.$route.params.tag
 | 
				
			||||||
				if (notes.length == limit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: true
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					return {
 | 
					 | 
				
			||||||
						notes: notes,
 | 
					 | 
				
			||||||
						more: false
 | 
					 | 
				
			||||||
					};
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			})
 | 
								}
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	watch: {
 | 
						watch: {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue