From ba85942f76c0b7c0b458b48289569ada9aee2be1 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sat, 11 Nov 2017 10:58:13 +0900
Subject: [PATCH] #905

---
 locales/en.yml                                |   9 +
 locales/ja.yml                                |   9 +
 src/api/endpoints/posts/timeline.ts           |  23 ++-
 .../desktop/tags/home-widgets/timeline.tag    |  18 +-
 .../desktop/tags/home-widgets/timemachine.tag | 165 ++++++++++++++++++
 src/web/app/desktop/tags/home.tag             |   4 +-
 src/web/app/desktop/tags/index.js             |   1 +
 7 files changed, 224 insertions(+), 5 deletions(-)
 create mode 100644 src/web/app/desktop/tags/home-widgets/timemachine.tag

diff --git a/locales/en.yml b/locales/en.yml
index c69dc22b1d..4eae825074 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -13,6 +13,15 @@ common:
     months_ago: "{}month(s) ago"
     years_ago: "{}year(s) ago"
 
+  weekday-short:
+    sunday: "S"
+    monday: "M"
+    tuesday: "T"
+    wednesday: "W"
+    thursday: "T"
+    friday: "F"
+    satruday: "S"
+
   reactions:
     like: "Like"
     love: "Love"
diff --git a/locales/ja.yml b/locales/ja.yml
index 782b87bd83..82589aadff 100644
--- a/locales/ja.yml
+++ b/locales/ja.yml
@@ -13,6 +13,15 @@ common:
     months_ago: "{}ヶ月前"
     years_ago: "{}年前"
 
+  weekday-short:
+    sunday: "日"
+    monday: "月"
+    tuesday: "火"
+    wednesday: "水"
+    thursday: "木"
+    friday: "金"
+    satruday: "土"
+
   reactions:
     like: "いいね"
     love: "ハート"
diff --git a/src/api/endpoints/posts/timeline.ts b/src/api/endpoints/posts/timeline.ts
index 7af435e82a..0d08b95463 100644
--- a/src/api/endpoints/posts/timeline.ts
+++ b/src/api/endpoints/posts/timeline.ts
@@ -29,9 +29,17 @@ module.exports = async (params, user, app) => {
 	const [maxId, maxIdErr] = $(params.max_id).optional.id().$;
 	if (maxIdErr) throw 'invalid max_id param';
 
-	// Check if both of since_id and max_id is specified
-	if (sinceId && maxId) {
-		throw 'cannot set since_id and max_id';
+	// Get 'since_date' parameter
+	const [sinceDate, sinceDateErr] = $(params.since_date).optional.number().$;
+	if (sinceDateErr) throw 'invalid since_date param';
+
+	// Get 'max_date' parameter
+	const [maxDate, maxDateErr] = $(params.max_date).optional.number().$;
+	if (maxDateErr) throw 'invalid max_date param';
+
+	// Check if only one of since_id, max_id, since_date, max_date specified
+	if ([sinceId, maxId, sinceDate, maxDate].filter(x => x != null).length > 1) {
+		throw 'only one of since_id, max_id, since_date, max_date can be specified';
 	}
 
 	const { followingIds, watchingChannelIds } = await rap({
@@ -81,6 +89,15 @@ module.exports = async (params, user, app) => {
 		query._id = {
 			$lt: maxId
 		};
+	} else if (sinceDate) {
+		sort._id = 1;
+		query.created_at = {
+			$gt: new Date(sinceDate)
+		};
+	} else if (maxDate) {
+		query.created_at = {
+			$lt: new Date(maxDate)
+		};
 	}
 	//#endregion
 
diff --git a/src/web/app/desktop/tags/home-widgets/timeline.tag b/src/web/app/desktop/tags/home-widgets/timeline.tag
index 08d96ad715..735783049c 100644
--- a/src/web/app/desktop/tags/home-widgets/timeline.tag
+++ b/src/web/app/desktop/tags/home-widgets/timeline.tag
@@ -70,7 +70,13 @@
 		};
 
 		this.load = (cb) => {
-			this.api('posts/timeline').then(posts => {
+			this.update({
+				isLoading: true
+			});
+
+			this.api('posts/timeline', {
+				max_date: this.date ? this.date.getTime() : undefined
+			}).then(posts => {
 				this.update({
 					isLoading: false,
 					isEmpty: posts.length == 0
@@ -114,5 +120,15 @@
 			const current = window.scrollY + window.innerHeight;
 			if (current > document.body.offsetHeight - 8) this.more();
 		};
+
+		this.warp = date => {
+			console.log(date);
+
+			this.update({
+				date: date
+			});
+
+			this.load();
+		};
 	</script>
 </mk-timeline-home-widget>
diff --git a/src/web/app/desktop/tags/home-widgets/timemachine.tag b/src/web/app/desktop/tags/home-widgets/timemachine.tag
new file mode 100644
index 0000000000..b6c53e0284
--- /dev/null
+++ b/src/web/app/desktop/tags/home-widgets/timemachine.tag
@@ -0,0 +1,165 @@
+<mk-timemachine-home-widget>
+	<button onclick={ prev }><i class="fa fa-chevron-circle-left"></i></button>
+	<p class="title">{ year }/{ month }</p>
+	<button onclick={ next }><i class="fa fa-chevron-circle-right"></i></button>
+
+	<div class="calendar">
+		<div class="weekday" each={ day, i in Array(7).fill(0) }>{ weekdayText[i] }</div>
+		<div each={ day, i in Array(paddingDays).fill(0) }></div>
+		<div class="day" each={ day, i in Array(days).fill(0) } data-today={ isToday(i + 1) } onclick={ go.bind(null, i + 1) }>{ i + 1 }</div>
+	</div>
+	<style>
+		:scope
+			display block
+			color #777
+			background #fff
+
+			> .title
+				z-index 1
+				margin 0
+				padding 0 16px
+				text-align center
+				line-height 42px
+				font-size 0.9em
+				font-weight bold
+				color #888
+				box-shadow 0 1px rgba(0, 0, 0, 0.07)
+
+				> i
+					margin-right 4px
+
+			> button
+				position absolute
+				z-index 2
+				top 0
+				padding 0
+				width 42px
+				font-size 0.9em
+				line-height 42px
+				color #ccc
+
+				&:hover
+					color #aaa
+
+				&:active
+					color #999
+
+				&:first-of-type
+					left 0
+
+				&:last-of-type
+					right 0
+
+			> .calendar
+				display flex
+				flex-wrap wrap
+				padding 16px
+
+				> div
+					width calc(100% * (1/7))
+					text-align center
+					line-height 32px
+					font-size 14px
+
+					&.weekday
+						color #19a2a9
+
+					&.day
+						cursor pointer
+
+						&:hover
+							background rgba(0, 0, 0, 0.025)
+
+						&:active
+							background rgba(0, 0, 0, 0.05)
+
+						&[data-today]
+							color $theme-color-foreground
+							background $theme-color
+
+							&:hover
+								background lighten($theme-color, 10%)
+
+							&:active
+								background darken($theme-color, 10%)
+
+	</style>
+	<script>
+		const eachMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
+
+		function isLeapYear(year) {
+			return (year % 400 == 0) ? true :
+				(year % 100 == 0) ? false :
+					(year % 4 == 0) ? true :
+						false;
+		}
+
+		this.today = new Date();
+		this.year = this.today.getFullYear();
+		this.month = this.today.getMonth() + 1;
+		this.weekdayText = [
+			'%i18n:common.weekday-short.sunday%',
+			'%i18n:common.weekday-short.monday%',
+			'%i18n:common.weekday-short.tuesday%',
+			'%i18n:common.weekday-short.wednesday%',
+			'%i18n:common.weekday-short.thursday%',
+			'%i18n:common.weekday-short.friday%',
+			'%i18n:common.weekday-short.satruday%'
+		];
+
+		this.on('mount', () => {
+			this.calc();
+		});
+
+		this.isToday = day => {
+			return this.year == this.today.getFullYear() && this.month == this.today.getMonth() + 1 && day == this.today.getDate();
+		};
+
+		this.calc = () => {
+			let days = eachMonthDays[this.month - 1];
+
+			// うるう年なら+1日
+			if (this.month == 2 && isLeapYear(this.year)) days++;
+
+			const date = new Date(this.year, this.month - 1, 1);
+			const weekday = date.getDay();
+
+			this.update({
+				paddingDays: weekday,
+				days: days
+			});
+		};
+
+		this.prev = () => {
+			if (this.month == 1) {
+				this.update({
+					year: this.year - 1,
+					month: 12
+				});
+			} else {
+				this.update({
+					month: this.month - 1
+				});
+			}
+			this.calc();
+		};
+
+		this.next = () => {
+			if (this.month == 12) {
+				this.update({
+					year: this.year + 1,
+					month: 1
+				});
+			} else {
+				this.update({
+					month: this.month + 1
+				});
+			}
+			this.calc();
+		};
+
+		this.go = day => {
+			this.opts.tl.warp(new Date(this.year, this.month - 1, day, 23, 59, 59, 999));
+		};
+</script>
+</mk-timemachine-home-widget>
diff --git a/src/web/app/desktop/tags/home.tag b/src/web/app/desktop/tags/home.tag
index ecfe23adee..452499d70c 100644
--- a/src/web/app/desktop/tags/home.tag
+++ b/src/web/app/desktop/tags/home.tag
@@ -5,6 +5,7 @@
 			<select ref="widgetSelector">
 				<option value="profile">プロフィール</option>
 				<option value="calendar">カレンダー</option>
+				<option value="timemachine">カレンダー(タイムマシン)</option>
 				<option value="activity">アクティビティ</option>
 				<option value="rss-reader">RSSリーダー</option>
 				<option value="trends">トレンド</option>
@@ -214,7 +215,8 @@
 
 			this.home.push(riot.mount(el, {
 				id: widget.id,
-				data: widget.data
+				data: widget.data,
+				tl: this.refs.tl
 			})[0]);
 		};
 
diff --git a/src/web/app/desktop/tags/index.js b/src/web/app/desktop/tags/index.js
index ff513657ac..865c3d3e06 100644
--- a/src/web/app/desktop/tags/index.js
+++ b/src/web/app/desktop/tags/index.js
@@ -41,6 +41,7 @@ require('./home-widgets/activity.tag');
 require('./home-widgets/server.tag');
 require('./home-widgets/slideshow.tag');
 require('./home-widgets/channel.tag');
+require('./home-widgets/timemachine.tag');
 require('./timeline.tag');
 require('./messaging/window.tag');
 require('./messaging/room-window.tag');