From 72e09b86b616cf2272efc741e951d253b49ab896 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E3=81=93=E3=81=B4=E3=81=AA=E3=81=9F=E3=81=BF=E3=81=BD?=
 <syuilotan@yahoo.co.jp>
Date: Wed, 14 Feb 2018 12:24:49 +0900
Subject: [PATCH] wip

---
 src/web/app/mobile/tags/home-timeline.tag |  69 -----------
 src/web/app/mobile/tags/timeline.tag      | 137 ----------------------
 src/web/app/mobile/views/posts.vue        |  97 +++++++++++++++
 src/web/app/mobile/views/timeline.vue     |  89 ++++++++++++++
 4 files changed, 186 insertions(+), 206 deletions(-)
 delete mode 100644 src/web/app/mobile/tags/home-timeline.tag
 create mode 100644 src/web/app/mobile/views/posts.vue
 create mode 100644 src/web/app/mobile/views/timeline.vue

diff --git a/src/web/app/mobile/tags/home-timeline.tag b/src/web/app/mobile/tags/home-timeline.tag
deleted file mode 100644
index 88e26bc785..0000000000
--- a/src/web/app/mobile/tags/home-timeline.tag
+++ /dev/null
@@ -1,69 +0,0 @@
-<mk-home-timeline>
-	<mk-init-following v-if="noFollowing" />
-	<mk-timeline ref="timeline" init={ init } more={ more } empty={ '%i18n:mobile.tags.mk-home-timeline.empty-timeline%' }/>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-
-			> mk-init-following
-				margin-bottom 8px
-
-	</style>
-	<script lang="typescript">
-		this.mixin('i');
-		this.mixin('api');
-
-		this.mixin('stream');
-		this.connection = this.stream.getConnection();
-		this.connectionId = this.stream.use();
-
-		this.noFollowing = this.I.following_count == 0;
-
-		this.init = new Promise((res, rej) => {
-			this.api('posts/timeline').then(posts => {
-				res(posts);
-				this.$emit('loaded');
-			});
-		});
-
-		this.fetch = () => {
-			this.api('posts/timeline').then(posts => {
-				this.$refs.timeline.setPosts(posts);
-			});
-		};
-
-		this.on('mount', () => {
-			this.connection.on('post', this.onStreamPost);
-			this.connection.on('follow', this.onStreamFollow);
-			this.connection.on('unfollow', this.onStreamUnfollow);
-		});
-
-		this.on('unmount', () => {
-			this.connection.off('post', this.onStreamPost);
-			this.connection.off('follow', this.onStreamFollow);
-			this.connection.off('unfollow', this.onStreamUnfollow);
-			this.stream.dispose(this.connectionId);
-		});
-
-		this.more = () => {
-			return this.api('posts/timeline', {
-				until_id: this.$refs.timeline.tail().id
-			});
-		};
-
-		this.onStreamPost = post => {
-			this.update({
-				isEmpty: false
-			});
-			this.$refs.timeline.addPost(post);
-		};
-
-		this.onStreamFollow = () => {
-			this.fetch();
-		};
-
-		this.onStreamUnfollow = () => {
-			this.fetch();
-		};
-	</script>
-</mk-home-timeline>
diff --git a/src/web/app/mobile/tags/timeline.tag b/src/web/app/mobile/tags/timeline.tag
index ed3f88c043..8a4d72b677 100644
--- a/src/web/app/mobile/tags/timeline.tag
+++ b/src/web/app/mobile/tags/timeline.tag
@@ -1,140 +1,3 @@
-<mk-timeline>
-	<div class="init" v-if="init">
-		%fa:spinner .pulse%%i18n:common.loading%
-	</div>
-	<div class="empty" v-if="!init && posts.length == 0">
-		%fa:R comments%{ opts.empty || '%i18n:mobile.tags.mk-timeline.empty%' }
-	</div>
-	<template each={ post, i in posts }>
-		<mk-timeline-post post={ post }/>
-		<p class="date" v-if="i != posts.length - 1 && post._date != posts[i + 1]._date">
-			<span>%fa:angle-up%{ post._datetext }</span>
-			<span>%fa:angle-down%{ posts[i + 1]._datetext }</span>
-		</p>
-	</template>
-	<footer v-if="!init">
-		<button v-if="canFetchMore" @click="more" disabled={ fetching }>
-			<span v-if="!fetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
-			<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
-		</button>
-	</footer>
-	<style lang="stylus" scoped>
-		:scope
-			display block
-			background #fff
-			border-radius 8px
-			box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
-
-			> .init
-				padding 64px 0
-				text-align center
-				color #999
-
-				> [data-fa]
-					margin-right 4px
-
-			> .empty
-				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
-
-			> .date
-				display block
-				margin 0
-				line-height 32px
-				text-align center
-				font-size 0.9em
-				color #aaa
-				background #fdfdfd
-				border-bottom solid 1px #eaeaea
-
-				span
-					margin 0 16px
-
-				[data-fa]
-					margin-right 8px
-
-			> footer
-				text-align center
-				border-top solid 1px #eaeaea
-				border-bottom-left-radius 4px
-				border-bottom-right-radius 4px
-
-				> button
-					margin 0
-					padding 16px
-					width 100%
-					color $theme-color
-					border-radius 0 0 8px 8px
-
-					&:disabled
-						opacity 0.7
-
-	</style>
-	<script lang="typescript">
-		this.posts = [];
-		this.init = true;
-		this.fetching = false;
-		this.canFetchMore = true;
-
-		this.on('mount', () => {
-			this.opts.init.then(posts => {
-				this.init = false;
-				this.setPosts(posts);
-			});
-		});
-
-		this.on('update', () => {
-			this.posts.forEach(post => {
-				const date = new Date(post.created_at).getDate();
-				const month = new Date(post.created_at).getMonth() + 1;
-				post._date = date;
-				post._datetext = `${month}月 ${date}日`;
-			});
-		});
-
-		this.more = () => {
-			if (this.init || this.fetching || this.posts.length == 0) return;
-			this.update({
-				fetching: true
-			});
-			this.opts.more().then(posts => {
-				this.fetching = false;
-				this.prependPosts(posts);
-			});
-		};
-
-		this.setPosts = posts => {
-			this.update({
-				posts: posts
-			});
-		};
-
-		this.prependPosts = posts => {
-			posts.forEach(post => {
-				this.posts.push(post);
-				this.update();
-			});
-		}
-
-		this.addPost = post => {
-			this.posts.unshift(post);
-			this.update();
-		};
-
-		this.tail = () => {
-			return this.posts[this.posts.length - 1];
-		};
-	</script>
-</mk-timeline>
 
 <mk-timeline-post :class="{ repost: isRepost }">
 	<div class="reply-to" v-if="p.reply">
diff --git a/src/web/app/mobile/views/posts.vue b/src/web/app/mobile/views/posts.vue
new file mode 100644
index 0000000000..0edda5e94c
--- /dev/null
+++ b/src/web/app/mobile/views/posts.vue
@@ -0,0 +1,97 @@
+<template>
+<div class="mk-posts">
+	<slot name="head"></slot>
+	<template v-for="(post, i) in _posts">
+		<mk-posts-post :post="post" :key="post.id"/>
+		<p class="date" :key="post._datetext" v-if="i != posts.length - 1 && post._date != _posts[i + 1]._date">
+			<span>%fa:angle-up%{{ post._datetext }}</span>
+			<span>%fa:angle-down%{{ _posts[i + 1]._datetext }}</span>
+		</p>
+	</template>
+	<slot name="tail"></slot>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	props: {
+		posts: {
+			type: Array,
+			default: () => []
+		}
+	},
+	computed: {
+		_posts(): any[] {
+			return (this.posts as any).map(post => {
+				const date = new Date(post.created_at).getDate();
+				const month = new Date(post.created_at).getMonth() + 1;
+				post._date = date;
+				post._datetext = `${month}月 ${date}日`;
+				return post;
+			});
+		}
+	}
+});
+</script>
+
+<style lang="stylus" scoped>
+.mk-posts
+	background #fff
+	border-radius 8px
+	box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
+
+	> .init
+		padding 64px 0
+		text-align center
+		color #999
+
+		> [data-fa]
+			margin-right 4px
+
+	> .empty
+		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
+
+	> .date
+		display block
+		margin 0
+		line-height 32px
+		text-align center
+		font-size 0.9em
+		color #aaa
+		background #fdfdfd
+		border-bottom solid 1px #eaeaea
+
+		span
+			margin 0 16px
+
+		[data-fa]
+			margin-right 8px
+
+	> footer
+		text-align center
+		border-top solid 1px #eaeaea
+		border-bottom-left-radius 4px
+		border-bottom-right-radius 4px
+
+		> button
+			margin 0
+			padding 16px
+			width 100%
+			color $theme-color
+			border-radius 0 0 8px 8px
+
+			&:disabled
+				opacity 0.7
+
+</style>
diff --git a/src/web/app/mobile/views/timeline.vue b/src/web/app/mobile/views/timeline.vue
new file mode 100644
index 0000000000..3a5df7792f
--- /dev/null
+++ b/src/web/app/mobile/views/timeline.vue
@@ -0,0 +1,89 @@
+<template>
+<div class="mk-timeline">
+	<mk-posts ref="timeline" :posts="posts">
+		<mk-friends-maker v-if="alone" slot="head"/>
+		<div class="init" v-if="fetching">
+			%fa:spinner .pulse%%i18n:common.loading%
+		</div>
+		<div class="empty" v-if="!fetching && posts.length == 0">
+			%fa:R comments%
+			%i18n:mobile.tags.mk-home-timeline.empty-timeline%
+		</div>
+		<button v-if="canFetchMore" @click="more" :disabled="fetching" slot="tail">
+			<span v-if="!fetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
+			<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
+		</button>
+	</mk-posts>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+	props: {
+		date: {
+			type: Date,
+			required: false
+		}
+	},
+	data() {
+		return {
+			fetching: true,
+			moreFetching: false,
+			posts: [],
+			connection: null,
+			connectionId: null
+		};
+	},
+	computed: {
+		alone(): boolean {
+			return this.$root.$data.os.i.following_count == 0;
+		}
+	},
+	mounted() {
+		this.connection = this.$root.$data.os.stream.getConnection();
+		this.connectionId = this.$root.$data.os.stream.use();
+
+		this.connection.on('post', this.onPost);
+		this.connection.on('follow', this.onChangeFollowing);
+		this.connection.on('unfollow', this.onChangeFollowing);
+
+		this.fetch();
+	},
+	beforeDestroy() {
+		this.connection.off('post', this.onPost);
+		this.connection.off('follow', this.onChangeFollowing);
+		this.connection.off('unfollow', this.onChangeFollowing);
+		this.$root.$data.os.stream.dispose(this.connectionId);
+	},
+	methods: {
+		fetch(cb?) {
+			this.fetching = true;
+
+			this.$root.$data.os.api('posts/timeline', {
+				until_date: this.date ? (this.date as any).getTime() : undefined
+			}).then(posts => {
+				this.fetching = false;
+				this.posts = posts;
+				if (cb) cb();
+			});
+		},
+		more() {
+			if (this.moreFetching || this.fetching || this.posts.length == 0) return;
+			this.moreFetching = true;
+			this.$root.$data.os.api('posts/timeline', {
+				until_id: this.posts[this.posts.length - 1].id
+			}).then(posts => {
+				this.moreFetching = false;
+				this.posts.unshift(posts);
+			});
+		},
+		onPost(post) {
+			this.posts.unshift(post);
+		},
+		onChangeFollowing() {
+			this.fetch();
+		}
+	}
+});
+</script>