1
1
mirror of https://github.com/kokonect-link/cherrypick synced 2024-12-01 08:18:45 +09:00

feat(client): 自動でもっと見るオプション (#6403)

* wip

* ugokanai

* wip

* implement setting subscribing

* fix lint

* ✌️

* Update notifications.vue

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
This commit is contained in:
tamaina 2020-05-31 12:53:06 +09:00 committed by GitHub
parent eda7d60c26
commit a1e0c866aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 96 additions and 17 deletions

View File

@ -509,6 +509,7 @@ addedRelays: "追加済みのリレー"
serviceworkerInfo: "プッシュ通知を行うには有効する必要があります。" serviceworkerInfo: "プッシュ通知を行うには有効する必要があります。"
deletedNote: "削除された投稿" deletedNote: "削除された投稿"
invisibleNote: "非公開の投稿" invisibleNote: "非公開の投稿"
enableInfiniteScroll: "自動でもっと見る"
_theme: _theme:
explore: "テーマを探す" explore: "テーマを探す"

View File

@ -19,17 +19,17 @@
@drop.prevent.stop="onDrop" @drop.prevent.stop="onDrop"
> >
<div class="contents" ref="contents"> <div class="contents" ref="contents">
<div class="folders" ref="foldersContainer" v-if="folders.length > 0"> <div class="folders" ref="foldersContainer" v-show="folders.length > 0">
<x-folder v-for="f in folders" :key="f.id" class="folder" :folder="f" :select-mode="select === 'folder'" :is-selected="selectedFolders.some(x => x.id === f.id)" @chosen="chooseFolder"/> <x-folder v-for="f in folders" :key="f.id" class="folder" :folder="f" :select-mode="select === 'folder'" :is-selected="selectedFolders.some(x => x.id === f.id)" @chosen="chooseFolder"/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" v-for="(n, i) in 16" :key="i"></div> <div class="padding" v-for="(n, i) in 16" :key="i"></div>
<mk-button v-if="moreFolders">{{ $t('loadMore') }}</mk-button> <mk-button ref="moreFolders" v-if="moreFolders">{{ $t('loadMore') }}</mk-button>
</div> </div>
<div class="files" ref="filesContainer" v-if="files.length > 0"> <div class="files" ref="filesContainer" v-show="files.length > 0">
<x-file v-for="file in files" :key="file.id" class="file" :file="file" :select-mode="select === 'file'" :is-selected="selectedFiles.some(x => x.id === file.id)" @chosen="chooseFile"/> <x-file v-for="file in files" :key="file.id" class="file" :file="file" :select-mode="select === 'file'" :is-selected="selectedFiles.some(x => x.id === file.id)" @chosen="chooseFile"/>
<!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid --> <!-- SEE: https://stackoverflow.com/questions/18744164/flex-box-align-last-row-to-grid -->
<div class="padding" v-for="(n, i) in 16" :key="i"></div> <div class="padding" v-for="(n, i) in 16" :key="i"></div>
<mk-button v-if="moreFiles" @click="fetchMoreFiles">{{ $t('loadMore') }}</mk-button> <mk-button ref="loadMoreFiles" @click="fetchMoreFiles" v-show="moreFiles">{{ $t('loadMore') }}</mk-button>
</div> </div>
<div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching"> <div class="empty" v-if="files.length == 0 && folders.length == 0 && !fetching">
<p v-if="draghover">{{ $t('empty-draghover') }}</p> <p v-if="draghover">{{ $t('empty-draghover') }}</p>
@ -116,6 +116,13 @@ export default Vue.extend({
fetching: true, fetching: true,
ilFilesObserver: new IntersectionObserver(
(entries) => entries.some((entry) => entry.isIntersecting)
&& !this.fetching && this.moreFiles &&
this.fetchMoreFiles()
),
moreFilesElement: null as Element,
faAngleRight faAngleRight
}; };
}, },
@ -127,6 +134,12 @@ export default Vue.extend({
}, },
mounted() { mounted() {
if (this.$store.state.device.enableInfiniteScroll && this.$refs.loadMoreFiles) {
this.$nextTick(() => {
this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el)
});
}
this.connection = this.$root.stream.useSharedConnection('drive'); this.connection = this.$root.stream.useSharedConnection('drive');
this.connection.on('fileCreated', this.onStreamDriveFileCreated); this.connection.on('fileCreated', this.onStreamDriveFileCreated);
@ -143,8 +156,17 @@ export default Vue.extend({
} }
}, },
activated() {
if (this.$store.state.device.enableInfiniteScroll) {
this.$nextTick(() => {
this.ilFilesObserver.observe((this.$refs.loadMoreFiles as Vue).$el)
});
}
},
beforeDestroy() { beforeDestroy() {
this.connection.dispose(); this.connection.dispose();
this.ilFilesObserver.disconnect();
}, },
methods: { methods: {

View File

@ -7,8 +7,8 @@
<mk-error v-if="error" @retry="init()"/> <mk-error v-if="error" @retry="init()"/>
<div v-if="more && reversed" style="margin-bottom: var(--margin);"> <div v-show="more && reversed" style="margin-bottom: var(--margin);">
<button class="_panel _button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()"> <button class="_panel _button" ref="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template> <template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><mk-loading inline/></template> <template v-if="moreFetching"><mk-loading inline/></template>
</button> </button>
@ -18,8 +18,8 @@
<x-note :note="note" :detail="detail" :key="note._featuredId_ || note._prId_ || note.id"/> <x-note :note="note" :detail="detail" :key="note._featuredId_ || note._prId_ || note.id"/>
</x-list> </x-list>
<div v-if="more && !reversed" style="margin-top: var(--margin);"> <div v-show="more && !reversed" style="margin-top: var(--margin);">
<button class="_panel _button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()"> <button class="_panel _button" ref="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template> <template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><mk-loading inline/></template> <template v-if="moreFetching"><mk-loading inline/></template>
</button> </button>

View File

@ -5,7 +5,7 @@
<x-notification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/> <x-notification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/>
</x-list> </x-list>
<button class="_panel _button" v-if="more" @click="fetchMore" :disabled="moreFetching"> <button class="_panel _button" ref="loadMore" v-show="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template> <template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><mk-loading inline/></template> <template v-if="moreFetching"><mk-loading inline/></template>
</button> </button>

View File

@ -4,8 +4,8 @@
<div class="empty" v-if="empty" key="_empty_"> <div class="empty" v-if="empty" key="_empty_">
<slot name="empty"></slot> <slot name="empty"></slot>
</div> </div>
<div class="more" v-if="more" key="_more_"> <div class="more" v-show="more" key="_more_">
<mk-button class="button" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" @click="fetchMore()" primary> <mk-button class="button" ref="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }" primary>
<template v-if="!moreFetching">{{ $t('loadMore') }}</template> <template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><mk-loading inline/></template> <template v-if="moreFetching"><mk-loading inline/></template>
</mk-button> </mk-button>

View File

@ -22,7 +22,7 @@
</div> </div>
<mk-follow-button class="koudoku-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/> <mk-follow-button class="koudoku-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/>
</div> </div>
<button class="more" :class="{ fetching: moreFetching }" v-if="more" @click="fetchMore()" :disabled="moreFetching"> <button class="more" ref="loadMore" :class="{ fetching: moreFetching }" v-show="more" :disabled="moreFetching">
<template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('loading') : $t('loadMore') }} <template v-if="moreFetching"><fa icon="spinner" pulse fixed-width/></template>{{ moreFetching ? $t('loading') : $t('loadMore') }}
</button> </button>
</div> </div>

View File

@ -15,8 +15,7 @@
</div> </div>
</router-link> </router-link>
</div> </div>
<button class="more _button" ref="loadMore" v-show="more" @click="fetchMore" :disabled="moreFetching">
<button class="more _button" v-if="more" @click="fetchMore" :disabled="moreFetching">
<template v-if="!moreFetching">{{ $t('loadMore') }}</template> <template v-if="!moreFetching">{{ $t('loadMore') }}</template>
<template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template> <template v-if="moreFetching"><fa :icon="faSpinner" pulse fixed-width/></template>
</button> </button>

View File

@ -16,7 +16,7 @@
<mk-loading v-if="fetching"/> <mk-loading v-if="fetching"/>
<p class="empty" v-if="!fetching && messages.length == 0"><fa :icon="faInfoCircle"/>{{ $t('noMessagesYet') }}</p> <p class="empty" v-if="!fetching && messages.length == 0"><fa :icon="faInfoCircle"/>{{ $t('noMessagesYet') }}</p>
<p class="no-history" v-if="!fetching && messages.length > 0 && !existMoreMessages"><fa :icon="faFlag"/>{{ $t('noMoreHistory') }}</p> <p class="no-history" v-if="!fetching && messages.length > 0 && !existMoreMessages"><fa :icon="faFlag"/>{{ $t('noMoreHistory') }}</p>
<button class="more _button" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages"> <button class="more _button" ref="loadMore" :class="{ fetching: fetchingMoreMessages }" v-show="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
<template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('loading') : $t('loadMore') }} <template v-if="fetchingMoreMessages"><fa icon="spinner" pulse fixed-width/></template>{{ fetchingMoreMessages ? $t('loading') : $t('loadMore') }}
</button> </button>
<x-list class="messages" :items="messages" v-slot="{ item: message }" direction="up" reversed> <x-list class="messages" :items="messages" v-slot="{ item: message }" direction="up" reversed>
@ -40,7 +40,6 @@ import { faArrowCircleDown, faFlag, faUsers, faInfoCircle } from '@fortawesome/f
import XList from '../../components/date-separated-list.vue'; import XList from '../../components/date-separated-list.vue';
import XMessage from './messaging-room.message.vue'; import XMessage from './messaging-room.message.vue';
import XForm from './messaging-room.form.vue'; import XForm from './messaging-room.form.vue';
import { url } from '../../config';
import parseAcct from '../../../misc/acct/parse'; import parseAcct from '../../../misc/acct/parse';
export default Vue.extend({ export default Vue.extend({
@ -61,6 +60,13 @@ export default Vue.extend({
connection: null, connection: null,
showIndicator: false, showIndicator: false,
timer: null, timer: null,
ilObserver: new IntersectionObserver(
(entries) => entries.some((entry) => entry.isIntersecting)
&& !this.fetching
&& !this.fetchingMoreMessages
&& this.existMoreMessages
&& this.fetchMoreMessages()
),
faArrowCircleDown, faFlag, faUsers, faInfoCircle faArrowCircleDown, faFlag, faUsers, faInfoCircle
}; };
}, },
@ -77,6 +83,9 @@ export default Vue.extend({
mounted() { mounted() {
this.fetch(); this.fetch();
if (this.$store.state.device.enableInfiniteScroll) {
this.$nextTick(() => this.ilObserver.observe(this.$refs.loadMore as Element));
}
}, },
beforeDestroy() { beforeDestroy() {
@ -85,6 +94,8 @@ export default Vue.extend({
window.removeEventListener('scroll', this.onScroll); window.removeEventListener('scroll', this.onScroll);
document.removeEventListener('visibilitychange', this.onVisibilitychange); document.removeEventListener('visibilitychange', this.onVisibilitychange);
this.ilObserver.disconnect();
}, },
methods: { methods: {
@ -112,8 +123,12 @@ export default Vue.extend({
document.addEventListener('visibilitychange', this.onVisibilitychange); document.addEventListener('visibilitychange', this.onVisibilitychange);
this.fetchMessages().then(() => { this.fetchMessages().then(() => {
this.fetching = false;
this.scrollToBottom(); this.scrollToBottom();
// fetch
// false
// scrollendsetTimeout
setTimeout(() => this.fetching = false, 300);
}); });
}, },

View File

@ -67,6 +67,7 @@
<template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template> <template #desc><mfm text="🍮🍦🍭🍩🍰🍫🍬🥞🍪"/></template>
</mk-switch> </mk-switch>
<mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch> <mk-switch v-model="showFixedPostForm">{{ $t('showFixedPostForm') }}</mk-switch>
<mk-switch v-model="enableInfiniteScroll">{{ $t('enableInfiniteScroll') }}</mk-switch>
<mk-switch v-model="disablePagesScript">{{ $t('disablePagesScript') }}</mk-switch> <mk-switch v-model="disablePagesScript">{{ $t('disablePagesScript') }}</mk-switch>
</div> </div>
<div class="_content"> <div class="_content">
@ -182,6 +183,11 @@ export default Vue.extend({
set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); } set(value) { this.$store.commit('device/set', { key: 'showFixedPostForm', value }); }
}, },
enableInfiniteScroll: {
get() { return this.$store.state.device.enableInfiniteScroll; },
set(value) { this.$store.commit('device/setInfiniteScrollEnabling', value); }
},
sfxVolume: { sfxVolume: {
get() { return this.$store.state.device.sfxVolume; }, get() { return this.$store.state.device.sfxVolume; },
set(value) { this.$store.commit('device/set', { key: 'sfxVolume', value: parseFloat(value, 10) }); } set(value) { this.$store.commit('device/set', { key: 'sfxVolume', value: parseFloat(value, 10) }); }

View File

@ -15,6 +15,14 @@ export default (opts) => ({
more: false, more: false,
backed: false, backed: false,
isBackTop: false, isBackTop: false,
ilObserver: new IntersectionObserver(
(entries) => entries.some((entry) => entry.isIntersecting)
&& !this.moreFetching
&& !this.fetching
&& this.fetchMore()
),
loadMoreElement: null as Element,
unsubscribeInfiniteScrollMutation: null as any,
}; };
}, },
@ -51,6 +59,29 @@ export default (opts) => ({
}); });
}, },
mounted() {
this.$nextTick(() => {
if (this.$refs.loadMore) {
this.loadMoreElement = this.$refs.loadMore instanceof Element ? this.$refs.loadMore : this.$refs.loadMore.$el;
if (this.$store.state.device.enableInfiniteScroll) this.ilObserver.observe(this.loadMoreElement);
this.loadMoreElement.addEventListener('click', this.fetchMore);
this.unsubscribeInfiniteScrollMutation = this.$store.subscribe(mutation => {
if (mutation.type !== 'device/setInfiniteScrollEnabling') return;
if (mutation.payload) return this.ilObserver.observe(this.loadMoreElement);
return this.ilObserver.unobserve(this.loadMoreElement);
});
}
});
},
beforeDestroy() {
this.ilObserver.disconnect();
if (this.$refs.loadMore) this.loadMoreElement.removeEventListener('click', this.fetchMore);
if (this.unsubscribeInfiniteScrollMutation) this.unsubscribeInfiniteScrollMutation();
},
methods: { methods: {
updateItem(i, item) { updateItem(i, item) {
Vue.set((this as any).items, i, item); Vue.set((this as any).items, i, item);

View File

@ -56,6 +56,7 @@ export const defaultDeviceSettings = {
imageNewTab: false, imageNewTab: false,
showFixedPostForm: false, showFixedPostForm: false,
disablePagesScript: true, disablePagesScript: true,
enableInfiniteScroll: true,
roomGraphicsQuality: 'medium', roomGraphicsQuality: 'medium',
roomUseOrthographicCamera: true, roomUseOrthographicCamera: true,
sfxVolume: 0.3, sfxVolume: 0.3,
@ -333,6 +334,10 @@ export default () => new Vuex.Store({
setUserData(state, x: { userId: string; data: any }) { setUserData(state, x: { userId: string; data: any }) {
state.userData[x.userId] = copy(x.data); state.userData[x.userId] = copy(x.data);
}, },
setInfiniteScrollEnabling(state, x: boolean) {
state.enableInfiniteScroll = x;
},
} }
}, },