This commit is contained in:
syuilo
2018-04-25 19:53:16 +09:00
parent 3d9ac6387e
commit c2e053a208
11 changed files with 363 additions and 79 deletions

View File

@ -28,6 +28,7 @@ import MkUser from './views/pages/user/user.vue';
import MkFavorites from './views/pages/favorites.vue';
import MkSelectDrive from './views/pages/selectdrive.vue';
import MkDrive from './views/pages/drive.vue';
import MkUserList from './views/pages/user-list.vue';
import MkHomeCustomize from './views/pages/home-customize.vue';
import MkMessagingRoom from './views/pages/messaging-room.vue';
import MkNote from './views/pages/note.vue';
@ -55,6 +56,7 @@ init(async (launch) => {
{ path: '/i/messaging/:user', component: MkMessagingRoom },
{ path: '/i/drive', component: MkDrive },
{ path: '/i/drive/folder/:folder', component: MkDrive },
{ path: '/i/lists/:list', component: MkUserList },
{ path: '/selectdrive', component: MkSelectDrive },
{ path: '/search', component: MkSearch },
{ path: '/othello', component: MkOthello },

View File

@ -28,6 +28,7 @@ import friendsMaker from './friends-maker.vue';
import followers from './followers.vue';
import following from './following.vue';
import usersList from './users-list.vue';
import userListTimeline from './user-list-timeline.vue';
import widgetContainer from './widget-container.vue';
Vue.component('mk-ui', ui);
@ -58,4 +59,5 @@ Vue.component('mk-friends-maker', friendsMaker);
Vue.component('mk-followers', followers);
Vue.component('mk-following', following);
Vue.component('mk-users-list', usersList);
Vue.component('mk-user-list-timeline', userListTimeline);
Vue.component('mk-widget-container', widgetContainer);

View File

@ -1,6 +1,6 @@
<template>
<mk-window ref="window" is-modal width="500px" height="550px" @closed="$destroy">
<span slot="header" :class="$style.header">%fa:list% リスト</span>
<span slot="header">%fa:list% リスト</span>
<button class="ui" @click="add">リストを作成</button>
<router-link v-for="list in lists" :key="list.id" :to="`/i/lists/${list.id}`">{{ list.title }}</router-link>

View File

@ -1,5 +1,14 @@
<template>
<div class="mk-notes">
<div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
<slot name="empty" v-if="notes.length == 0 && !fetching && requestInitPromise == null"></slot>
<div v-if="!fetching && requestInitPromise != null">
<p>読み込みに失敗しました</p>
<button @click="resolveInitPromise">リトライ</button>
</div>
<transition-group name="mk-notes" class="transition">
<template v-for="(note, i) in _notes">
<x-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
@ -9,7 +18,8 @@
</p>
</template>
</transition-group>
<footer v-if="loadMore">
<footer v-if="more">
<button @click="loadMore" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">%i18n:@load-more%</template>
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
@ -40,9 +50,10 @@ export default Vue.extend({
data() {
return {
requestInitPromise: null as () => Promise<any[]>,
notes: [],
queue: [],
fetching: false,
fetching: true,
moreFetching: false
};
},
@ -80,9 +91,25 @@ export default Vue.extend({
Vue.set((this as any).notes, i, note);
},
init(notes) {
init(promiseGenerator: () => Promise<any[]>) {
this.requestInitPromise = promiseGenerator;
this.resolveInitPromise();
},
resolveInitPromise() {
this.queue = [];
this.notes = notes;
this.notes = [];
this.fetching = true;
const promise = this.requestInitPromise();
promise.then(notes => {
this.notes = notes;
this.requestInitPromise = null;
this.fetching = false;
}, e => {
this.fetching = false;
});
},
prepend(note, silent = false) {
@ -137,6 +164,9 @@ export default Vue.extend({
},
async loadMore() {
if (this.more == null) return;
if (this.moreFetching) return;
this.moreFetching = true;
await this.more();
this.moreFetching = false;
@ -157,6 +187,8 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
@import '~const.styl'
root(isDark)
.transition
.mk-notes-enter
@ -183,6 +215,13 @@ root(isDark)
[data-fa]
margin-right 8px
> .newer-indicator
position -webkit-sticky
position sticky
z-index 100
height 3px
background $theme-color
> footer
> *
display block
@ -191,16 +230,16 @@ root(isDark)
width 100%
text-align center
color #ccc
border-top solid 1px #eaeaea
border-top solid 1px isDark ? #1c2023 : #eaeaea
border-bottom-left-radius 4px
border-bottom-right-radius 4px
> button
&:hover
background #f5f5f5
background isDark ? #2e3440 : #f5f5f5
&:active
background #eee
background isDark ? #21242b : #eee
.mk-notes[data-darkmode]
root(true)

View File

@ -1,14 +1,15 @@
<template>
<div class="mk-timeline-core">
<div class="newer-indicator" :style="{ top: $store.state.uiHeaderHeight + 'px' }" v-show="queue.length > 0"></div>
<mk-friends-maker v-if="src == 'home' && alone"/>
<div class="fetching" v-if="fetching">
<mk-ellipsis-icon/>
</div>
<p class="empty" v-if="notes.length == 0 && !fetching">
%fa:R comments%%i18n:@empty%
</p>
<mk-notes :notes="notes" ref="timeline" :more="canFetchMore ? more : null"/>
<mk-notes ref="timeline" :more="canFetchMore ? more : null">
<p :class="$style.empty" slot="empty">
%fa:R comments%%i18n:@empty%
</p>
</mk-notes>
</div>
</template>
@ -89,28 +90,26 @@ export default Vue.extend({
},
methods: {
isScrollTop() {
return window.scrollY <= 8;
},
fetch(cb?) {
this.fetching = true;
(this as any).api(this.endpoint, {
limit: fetchLimit + 1,
untilDate: this.date ? this.date.getTime() : undefined,
includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
}).then(notes => {
if (notes.length == fetchLimit + 1) {
notes.pop();
this.existMore = true;
}
(this.$refs.timeline as any).init(notes);
this.fetching = false;
this.$emit('loaded');
if (cb) cb();
});
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
(this as any).api(this.endpoint, {
limit: fetchLimit + 1,
untilDate: this.date ? this.date.getTime() : undefined,
includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
}).then(notes => {
if (notes.length == fetchLimit + 1) {
notes.pop();
this.existMore = true;
}
res(notes);
this.fetching = false;
this.$emit('loaded');
if (cb) cb();
}, rej);
}));
},
more() {
@ -167,31 +166,27 @@ export default Vue.extend({
@import '~const.styl'
.mk-timeline-core
> .newer-indicator
position -webkit-sticky
position sticky
z-index 100
height 3px
background $theme-color
> .mk-friends-maker
border-bottom solid 1px #eee
> .fetching
padding 64px 0
> .empty
display block
margin 0 auto
padding 32px
max-width 400px
text-align center
color #999
</style>
> [data-fa]
display block
margin-bottom 16px
font-size 3em
color #ccc
<style lang="stylus" module>
.empty
display block
margin 0 auto
padding 32px
max-width 400px
text-align center
color #999
> [data-fa]
display block
margin-bottom 16px
font-size 3em
color #ccc
</style>

View File

@ -1,5 +1,5 @@
<template>
<mk-notes ref="timeline" :more="more"/>
<mk-notes ref="timeline" :more="existMore ? more : null"/>
</template>
<script lang="ts">
@ -19,42 +19,49 @@ export default Vue.extend({
};
},
watch: {
$route: 'fetch'
$route: 'init'
},
mounted() {
this.fetch();
this.init();
},
beforeDestroy() {
this.connection.close();
},
methods: {
fetch() {
init() {
if (this.connection) this.connection.close();
this.connection = new UserListStream((this as any).os, (this as any).os.i, this.list.id);
this.connection.on('note', this.onNote);
this.connection.on('userAdded', this.onUserAdded);
this.connection.on('userRemoved', this.onUserRemoved);
this.fetch();
},
fetch() {
this.fetching = true;
(this as any).api('notes/list-timeline', {
limit: fetchLimit + 1,
includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
}).then(notes => {
if (notes.length == fetchLimit + 1) {
notes.pop();
this.existMore = true;
}
(this.$refs.timeline as any).init(notes);
this.fetching = false;
this.$emit('loaded');
});
(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
(this as any).api('notes/user-list-timeline', {
listId: this.list.id,
limit: fetchLimit + 1,
includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
}).then(notes => {
if (notes.length == fetchLimit + 1) {
notes.pop();
this.existMore = true;
}
res(notes);
this.fetching = false;
this.$emit('loaded');
}, rej);
}));
},
more() {
this.moreFetching = true;
(this as any).api('notes/list-timeline', {
listId: this.list.id,
limit: fetchLimit + 1,
untilId: (this.$refs.timeline as any).tail().id,
includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
@ -68,7 +75,17 @@ export default Vue.extend({
notes.forEach(n => (this.$refs.timeline as any).append(n));
this.moreFetching = false;
});
}
},
onNote(note) {
// Prepend a note
(this.$refs.timeline as any).prepend(note);
},
onUserAdded() {
this.fetch();
},
onUserRemoved() {
this.fetch();
},
}
});
</script>

View File

@ -1,9 +1,11 @@
<template>
<mk-ui>
<header :class="$style.header">
<h1>{{ list.title }}</h1>
</header>
<mk-list-timeline :list="list"/>
<template v-if="!fetching">
<header :class="$style.header">
<h1>{{ list.title }}</h1>
</header>
<mk-user-list-timeline :list="list"/>
</template>
</mk-ui>
</template>
@ -28,7 +30,7 @@ export default Vue.extend({
this.fetching = true;
(this as any).api('users/lists/show', {
id: this.$route.params.list
listId: this.$route.params.list
}).then(list => {
this.list = list;
this.fetching = false;