From 64bbfed43222e8469a39eba5249c196c16f96eb7 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Sun, 10 May 2020 16:22:39 +0900
Subject: [PATCH] feat(client): Implement threaded replies

Resolve #2113
Resolve #5819
---
 src/client/components/note.sub.vue | 118 +++++++++++++++++++----------
 src/client/components/note.vue     |   2 +-
 2 files changed, 79 insertions(+), 41 deletions(-)

diff --git a/src/client/components/note.sub.vue b/src/client/components/note.sub.vue
index 5efbb8f1e9..fcdb06bd9e 100644
--- a/src/client/components/note.sub.vue
+++ b/src/client/components/note.sub.vue
@@ -1,18 +1,21 @@
 <template>
-<div class="wrpstxzv" v-size="[{ max: 450 }]">
-	<mk-avatar class="avatar" :user="note.user"/>
+<div class="wrpstxzv" :class="{ children }" v-size="[{ max: 450 }]">
 	<div class="main">
-		<x-note-header class="header" :note="note" :mini="true"/>
+		<mk-avatar class="avatar" :user="note.user"/>
 		<div class="body">
-			<p v-if="note.cw != null" class="cw">
-				<mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$store.state.i" :custom-emojis="note.emojis" />
-				<x-cw-button v-model="showContent" :note="note"/>
-			</p>
-			<div class="content" v-show="note.cw == null || showContent">
-				<x-sub-note-content class="text" :note="note"/>
+			<x-note-header class="header" :note="note" :mini="true"/>
+			<div class="body">
+				<p v-if="note.cw != null" class="cw">
+					<mfm v-if="note.cw != ''" class="text" :text="note.cw" :author="note.user" :i="$store.state.i" :custom-emojis="note.emojis" />
+					<x-cw-button v-model="showContent" :note="note"/>
+				</p>
+				<div class="content" v-show="note.cw == null || showContent">
+					<x-sub-note-content class="text" :note="note"/>
+				</div>
 			</div>
 		</div>
 	</div>
+	<x-sub v-for="reply in replies" :key="reply.id" :note="reply" class="reply" :detail="true" :children="true"/>
 </div>
 </template>
 
@@ -23,6 +26,8 @@ import XSubNoteContent from './sub-note-content.vue';
 import XCwButton from './cw-button.vue';
 
 export default Vue.extend({
+	name: 'x-sub',
+
 	components: {
 		XNoteHeader,
 		XSubNoteContent,
@@ -34,6 +39,16 @@ export default Vue.extend({
 			type: Object,
 			required: true
 		},
+		detail: {
+			type: Boolean,
+			required: false,
+			default: false
+		},
+		children: {
+			type: Boolean,
+			required: false,
+			default: false
+		},
 		// TODO
 		truncate: {
 			type: Boolean,
@@ -41,23 +56,28 @@ export default Vue.extend({
 		}
 	},
 
-	inject: {
-		narrow: {
-			default: false
-		}
-	},
-
 	data() {
 		return {
-			showContent: false
+			showContent: false,
+			replies: [],
 		};
-	}
+	},
+
+	created() {
+		if (this.detail) {
+			this.$root.api('notes/children', {
+				noteId: this.note.id,
+				limit: 5
+			}).then(replies => {
+				this.replies = replies;
+			});
+		}
+	},
 });
 </script>
 
 <style lang="scss" scoped>
 .wrpstxzv {
-	display: flex;
 	padding: 16px 32px;
 	font-size: 0.9em;
 
@@ -65,43 +85,61 @@ export default Vue.extend({
 		padding: 14px 16px;
 	}
 
-	> .avatar {
-		flex-shrink: 0;
-		display: block;
-		margin: 0 8px 0 0;
-		width: 38px;
-		height: 38px;
-		border-radius: 8px;
+	&.children {
+		padding: 10px 0 0 16px;
+		font-size: 1em;
+
+		&.max-width_450px {
+			padding: 10px 0 0 8px;
+		}
 	}
 
 	> .main {
-		flex: 1;
-		min-width: 0;
+		display: flex;
 
-		> .header {
-			margin-bottom: 2px;
+		> .avatar {
+			flex-shrink: 0;
+			display: block;
+			margin: 0 8px 0 0;
+			width: 38px;
+			height: 38px;
+			border-radius: 8px;
 		}
 
 		> .body {
-			> .cw {
-				cursor: default;
-				display: block;
-				margin: 0;
-				padding: 0;
-				overflow-wrap: break-word;
+			flex: 1;
+			min-width: 0;
 
-				> .text {
-					margin-right: 8px;
-				}
+			> .header {
+				margin-bottom: 2px;
 			}
 
-			> .content {
-				> .text {
+			> .body {
+				> .cw {
+					cursor: default;
+					display: block;
 					margin: 0;
 					padding: 0;
+					overflow-wrap: break-word;
+
+					> .text {
+						margin-right: 8px;
+					}
+				}
+
+				> .content {
+					> .text {
+						margin: 0;
+						padding: 0;
+					}
 				}
 			}
 		}
 	}
+
+	> .reply {
+		border-left: solid 1px var(--divider);
+		margin-top: 10px;
+	}
 }
 </style>
diff --git a/src/client/components/note.vue b/src/client/components/note.vue
index 619d1a5b01..ec31a5f99a 100644
--- a/src/client/components/note.vue
+++ b/src/client/components/note.vue
@@ -79,7 +79,7 @@
 			<div class="deleted" v-if="appearNote.deletedAt != null">{{ $t('deleted') }}</div>
 		</div>
 	</article>
-	<x-sub v-for="note in replies" :key="note.id" :note="note" class="reply"/>
+	<x-sub v-for="note in replies" :key="note.id" :note="note" class="reply" :detail="true"/>
 </div>
 </template>