Skip to content
Snippets Groups Projects
Commit c7f80182 authored by syuilo's avatar syuilo
Browse files

wip

parent 1ba5dfd7
No related branches found
No related tags found
No related merge requests found
......@@ -14,6 +14,7 @@
"vue/no-unused-vars": false,
"vue/attributes-order": false,
"vue/require-prop-types": false,
"vue/require-default-prop": false,
"no-console": 0,
"no-unused-vars": 0,
"no-empty": 0
......
<template>
<mk-notes ref="timeline" :more="more"/>
</template>
<script lang="ts">
import Vue from 'vue';
const fetchLimit = 10;
export default Vue.extend({
props: ['list'],
data() {
return {
fetching: true,
moreFetching: false,
existMore: false,
connection: null
};
},
watch: {
$route: 'fetch'
},
mounted() {
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();
},
beforeDestroy() {
this.connection.off('note', this.onNote);
this.connection.off('userAdded', this.onUserAdded);
this.connection.off('userRemoved', this.onUserRemoved);
this.connection.close();
},
methods: {
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');
});
},
more() {
this.moreFetching = true;
(this as any).api('notes/list-timeline', {
limit: fetchLimit + 1,
untilId: (this.$refs.timeline as any).tail().id,
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();
} else {
this.existMore = false;
}
notes.forEach(n => (this.$refs.timeline as any).append(n));
this.moreFetching = false;
});
}
}
});
</script>
......@@ -2,10 +2,8 @@
<mk-window ref="window" is-modal width="500px" height="550px" @closed="$destroy">
<span slot="header" :class="$style.header">%fa:list% リスト</span>
<button class="ui">リストを作成</button>
<a v-for="list in lists" :key="list.id">
</a>
<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>
</mk-window>
</template>
......@@ -25,6 +23,17 @@ export default Vue.extend({
});
},
methods: {
add() {
(this as any).apis.input({
title: 'リスト名',
}).then(async title => {
const list = await (this as any).api('users/lists/create', {
title
});
this.$router.push(`i/lists/${ list.id }`);
});
},
close() {
(this as any).$refs.window.close();
}
......
......@@ -9,8 +9,11 @@
</p>
</template>
</transition-group>
<footer>
<slot name="footer"></slot>
<footer v-if="loadMore">
<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>
</button>
</footer>
</div>
</template>
......@@ -19,16 +22,29 @@
import Vue from 'vue';
import XNote from './notes.note.vue';
const displayLimit = 30;
export default Vue.extend({
components: {
XNote
},
props: {
notes: {
type: Array,
default: () => []
more: {
type: Function,
required: false
}
},
data() {
return {
notes: [],
queue: [],
fetching: false,
moreFetching: false
};
},
computed: {
_notes(): any[] {
return (this.notes as any).map(note => {
......@@ -40,12 +56,74 @@ export default Vue.extend({
});
}
},
mounted() {
window.addEventListener('scroll', this.onScroll);
},
beforeDestroy() {
window.removeEventListener('scroll', this.onScroll);
},
methods: {
isScrollTop() {
return window.scrollY <= 8;
},
focus() {
(this.$el as any).children[0].focus();
},
onNoteUpdated(i, note) {
Vue.set((this as any).notes, i, note);
},
init(notes) {
this.queue = [];
this.notes = notes;
},
prepend(note) {
if (this.isScrollTop()) {
this.notes.unshift(note);
// オーバーフローしたら古い投稿は捨てる
if (this.notes.length >= displayLimit) {
this.notes = this.notes.slice(0, displayLimit);
}
} else {
this.queue.unshift(note);
}
},
append(note) {
this.notes.push(note);
},
tail() {
return this.notes[this.notes.length - 1];
},
releaseQueue() {
this.queue.forEach(n => this.prepend(n));
this.queue = [];
},
async loadMore() {
this.moreFetching = true;
await this.more();
this.moreFetching = false;
},
onScroll() {
if (this.isScrollTop()) {
this.releaseQueue();
}
if ((this as any).os.i.clientSettings.fetchOnScroll !== false) {
const current = window.scrollY + window.innerHeight;
if (current > document.body.offsetHeight - 8) this.loadMore();
}
}
}
});
......
<template>
<div class="mk-home-timeline">
<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">
......@@ -8,12 +8,7 @@
<p class="empty" v-if="notes.length == 0 && !fetching">
%fa:R comments%%i18n:@empty%
</p>
<mk-notes :notes="notes" ref="timeline">
<button slot="footer" @click="more" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
<template v-if="!moreFetching">%i18n:@load-more%</template>
<template v-if="moreFetching">%fa:spinner .pulse .fw%</template>
</button>
</mk-notes>
<mk-notes :notes="notes" ref="timeline" :more="canFetchMore ? more : null"/>
</div>
</template>
......@@ -22,7 +17,6 @@ import Vue from 'vue';
import { url } from '../../../config';
const fetchLimit = 10;
const displayLimit = 30;
export default Vue.extend({
props: {
......@@ -37,8 +31,6 @@ export default Vue.extend({
fetching: true,
moreFetching: false,
existMore: false,
notes: [],
queue: [],
connection: null,
connectionId: null,
date: null
......@@ -67,7 +59,7 @@ export default Vue.extend({
},
canFetchMore(): boolean {
return !this.moreFetching && !this.fetching && this.notes.length > 0 && this.existMore;
return !this.moreFetching && !this.fetching && this.existMore;
}
},
......@@ -82,7 +74,6 @@ export default Vue.extend({
}
document.addEventListener('keydown', this.onKeydown);
window.addEventListener('scroll', this.onScroll);
this.fetch();
},
......@@ -96,7 +87,6 @@ export default Vue.extend({
this.stream.dispose(this.connectionId);
document.removeEventListener('keydown', this.onKeydown);
window.removeEventListener('scroll', this.onScroll);
},
methods: {
......@@ -105,7 +95,6 @@ export default Vue.extend({
},
fetch(cb?) {
this.queue = [];
this.fetching = true;
(this as any).api(this.endpoint, {
......@@ -118,7 +107,7 @@ export default Vue.extend({
notes.pop();
this.existMore = true;
}
this.notes = notes;
(this.$refs.timeline as any).init(notes);
this.fetching = false;
this.$emit('loaded');
if (cb) cb();
......@@ -132,7 +121,7 @@ export default Vue.extend({
(this as any).api(this.endpoint, {
limit: fetchLimit + 1,
untilId: this.notes[this.notes.length - 1].id,
untilId: (this.$refs.timeline as any).tail().id,
includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
}).then(notes => {
......@@ -141,33 +130,11 @@ export default Vue.extend({
} else {
this.existMore = false;
}
this.notes = this.notes.concat(notes);
notes.forEach(n => (this.$refs.timeline as any).append(n));
this.moreFetching = false;
});
},
prependNote(note, silent = false) {
// サウンドを再生する
if ((this as any).os.isEnableSounds && !silent) {
const sound = new Audio(`${url}/assets/post.mp3`);
sound.volume = localStorage.getItem('soundVolume') ? parseInt(localStorage.getItem('soundVolume'), 10) / 100 : 0.5;
sound.play();
}
// Prepent a note
this.notes.unshift(note);
// オーバーフローしたら古い投稿は捨てる
if (this.notes.length >= displayLimit) {
this.notes = this.notes.slice(0, displayLimit);
}
},
releaseQueue() {
this.queue.forEach(n => this.prependNote(n, true));
this.queue = [];
},
onNote(note) {
//#region 弾く
const isMyNote = note.userId == (this as any).os.i.id;
......@@ -186,11 +153,15 @@ export default Vue.extend({
}
//#endregion
if (this.isScrollTop()) {
this.prependNote(note);
} else {
this.queue.unshift(note);
// サウンドを再生する
if ((this as any).os.isEnableSounds) {
const sound = new Audio(`${url}/assets/post.mp3`);
sound.volume = localStorage.getItem('soundVolume') ? parseInt(localStorage.getItem('soundVolume'), 10) / 100 : 0.5;
sound.play();
}
// Prepend a note
(this.$refs.timeline as any).prepend(note);
},
onChangeFollowing() {
......@@ -206,17 +177,6 @@ export default Vue.extend({
this.fetch();
},
onScroll() {
if ((this as any).os.i.clientSettings.fetchOnScroll !== false) {
const current = window.scrollY + window.innerHeight;
if (current > document.body.offsetHeight - 8) this.more();
}
if (this.isScrollTop()) {
this.releaseQueue();
}
},
onKeydown(e) {
if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
if (e.which == 84) { // t
......@@ -231,7 +191,7 @@ export default Vue.extend({
<style lang="stylus" scoped>
@import '~const.styl'
.mk-home-timeline
.mk-timeline-core
> .newer-indicator
position -webkit-sticky
position sticky
......
......@@ -45,6 +45,7 @@
<script lang="ts">
import Vue from 'vue';
import MkListsWindow from './lists-window.vue';
import MkSettingsWindow from './settings-window.vue';
import MkDriveWindow from './drive-window.vue';
import contains from '../../../common/scripts/contains';
......@@ -83,6 +84,10 @@ export default Vue.extend({
this.close();
(this as any).os.new(MkDriveWindow);
},
list() {
this.close();
(this as any).os.new(MkListsWindow);
},
settings() {
this.close();
(this as any).os.new(MkSettingsWindow);
......
<template>
<mk-ui>
<header :class="$style.header">
<h1>{{ list.title }}</h1>
</header>
<mk-list-timeline :list="list"/>
</mk-ui>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
data() {
return {
fetching: true,
list: null
};
},
watch: {
$route: 'fetch'
},
mounted() {
this.fetch();
},
methods: {
fetch() {
this.fetching = true;
(this as any).api('users/lists/show', {
id: this.$route.params.list
}).then(list => {
this.list = list;
this.fetching = false;
});
}
}
});
</script>
<style lang="stylus" module>
.header
width 100%
max-width 600px
margin 0 auto
color #555
.notes
max-width 600px
margin 0 auto
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
overflow hidden
.loading
padding 64px 0
.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>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment