From f2c5dc468c9bdb86e096565e9306f53eb1876ce0 Mon Sep 17 00:00:00 2001
From: syuilo <syuilotan@yahoo.co.jp>
Date: Wed, 22 Feb 2017 12:43:15 +0900
Subject: [PATCH] Extract user's profile

---
 src/api/endpoints/i/update.js             | 24 +++++------
 src/api/private/signup.ts                 | 16 ++++++--
 src/web/app/desktop/tags/list-user.tag    | 50 +++++++++++------------
 src/web/app/desktop/tags/user-header.tag  |  2 +-
 src/web/app/desktop/tags/user-preview.tag |  4 +-
 src/web/app/desktop/tags/user-profile.tag |  8 ++--
 src/web/app/mobile/tags/user-preview.tag  | 14 +++++--
 src/web/app/mobile/tags/user.tag          | 12 +++---
 test/api.js                               | 23 +++++++----
 tools/migration/user-profile.js           | 18 ++++++++
 10 files changed, 102 insertions(+), 69 deletions(-)
 create mode 100644 tools/migration/user-profile.js

diff --git a/src/api/endpoints/i/update.js b/src/api/endpoints/i/update.js
index 13b5c523e0..5aeafa60c8 100644
--- a/src/api/endpoints/i/update.js
+++ b/src/api/endpoints/i/update.js
@@ -39,7 +39,7 @@ module.exports = async (params, user, _, isSecure) =>
 			return rej('too long location');
 		}
 
-		user.location = location;
+		user.profile.location = location;
 	}
 
 	// Get 'bio' parameter
@@ -49,21 +49,19 @@ module.exports = async (params, user, _, isSecure) =>
 			return rej('too long bio');
 		}
 
-		user.bio = bio;
+		user.profile.bio = bio;
 	}
 
 	// Get 'birthday' parameter
 	const birthday = params.birthday;
 	if (birthday != null) {
-		if (birthday != '') {
-			if (!isValidBirthday(birthday)) {
-				return rej('invalid birthday');
-			}
-
-			user.birthday = birthday;
-		} else {
-			user.birthday = null;
+		if (!isValidBirthday(birthday)) {
+			return rej('invalid birthday');
 		}
+
+		user.profile.birthday = birthday;
+	} else {
+		user.profile.birthday = null;
 	}
 
 	// Get 'avatar_id' parameter
@@ -81,11 +79,9 @@ module.exports = async (params, user, _, isSecure) =>
 	await User.update(user._id, {
 		$set: {
 			name: user.name,
-			location: user.location,
-			bio: user.bio,
-			birthday: user.birthday,
 			avatar_id: user.avatar_id,
-			banner_id: user.banner_id
+			banner_id: user.banner_id,
+			profile: user.profile
 		}
 	});
 
diff --git a/src/api/private/signup.ts b/src/api/private/signup.ts
index 2b679b914c..73e04f8b37 100644
--- a/src/api/private/signup.ts
+++ b/src/api/private/signup.ts
@@ -65,14 +65,12 @@ export default async (req: express.Request, res: express.Response) => {
 		token: secret,
 		avatar_id: null,
 		banner_id: null,
-		birthday: null,
 		created_at: new Date(),
-		bio: null,
+		description: null,
 		email: null,
 		followers_count: 0,
 		following_count: 0,
 		links: null,
-		location: null,
 		name: name,
 		password: hash,
 		posts_count: 0,
@@ -80,7 +78,17 @@ export default async (req: express.Request, res: express.Response) => {
 		liked_count: 0,
 		drive_capacity: 1073741824, // 1GB
 		username: username,
-		username_lower: username.toLowerCase()
+		username_lower: username.toLowerCase(),
+		profile: {
+			bio: null,
+			birthday: null,
+			blood: null,
+			gender: null,
+			handedness: null,
+			height: null,
+			location: null,
+			weight: null
+		}
 	});
 
 	// Response
diff --git a/src/web/app/desktop/tags/list-user.tag b/src/web/app/desktop/tags/list-user.tag
index 133efd2d1e..e709d8d708 100644
--- a/src/web/app/desktop/tags/list-user.tag
+++ b/src/web/app/desktop/tags/list-user.tag
@@ -1,11 +1,15 @@
-<mk-list-user><a class="avatar-anchor" href={ CONFIG.url + '/' + user.username }><img class="avatar" src={ user.avatar_url + '?thumbnail&size=64' } alt="avatar"/></a>
+<mk-list-user>
+	<a class="avatar-anchor" href={ CONFIG.url + '/' + user.username }>
+		<img class="avatar" src={ user.avatar_url + '?thumbnail&size=64' } alt="avatar"/>
+	</a>
 	<div class="main">
 		<header>
-			<div class="left"><a class="name" href={ CONFIG.url + '/' + user.username }>{ user.name }</a><span class="username">@{ user.username }</span></div>
+			<a class="name" href={ CONFIG.url + '/' + user.username }>{ user.name }</a>
+			<span class="username">@{ user.username }</span>
 		</header>
 		<div class="body">
 			<p class="followed" if={ user.is_followed }>フォローされています</p>
-			<div class="bio">{ user.bio }</div>
+			<div class="description">{ user.description }</div>
 		</div>
 	</div>
 	<mk-follow-button user={ user }></mk-follow-button>
@@ -41,31 +45,23 @@
 				> header
 					margin-bottom 2px
 
-					&:after
-						content ""
-						display block
-						clear both
+					> .name
+						display inline
+						margin 0
+						padding 0
+						color #777
+						font-size 1em
+						font-weight 700
+						text-align left
+						text-decoration none
 
-					> .left
-						float left
+						&:hover
+							text-decoration underline
 
-						> .name
-							display inline
-							margin 0
-							padding 0
-							color #777
-							font-size 1em
-							font-weight 700
-							text-align left
-							text-decoration none
-
-							&:hover
-								text-decoration underline
-
-						> .username
-							text-align left
-							margin 0 0 0 8px
-							color #ccc
+					> .username
+						text-align left
+						margin 0 0 0 8px
+						color #ccc
 
 				> .body
 					> .followed
@@ -78,7 +74,7 @@
 						background #eefaff
 						border-radius 4px
 
-					> .bio
+					> .description
 						cursor default
 						display block
 						margin 0
diff --git a/src/web/app/desktop/tags/user-header.tag b/src/web/app/desktop/tags/user-header.tag
index c265357169..c7ebaff446 100644
--- a/src/web/app/desktop/tags/user-header.tag
+++ b/src/web/app/desktop/tags/user-header.tag
@@ -3,7 +3,7 @@
 	<div class="title">
 		<p class="name" href={ CONFIG.url + '/' + user.username }>{ user.name }</p>
 		<p class="username">@{ user.username }</p>
-		<p class="location" if={ user.location }><i class="fa fa-map-marker"></i>{ user.location }</p>
+		<p class="location" if={ user.profile.location }><i class="fa fa-map-marker"></i>{ user.profile.location }</p>
 	</div>
 	<footer><a href={ '/' + user.username }>投稿</a><a href={ '/' + user.username + '/media' }>メディア</a><a href={ '/' + user.username + '/graphs' }>グラフ</a>
 		<button onclick={ NotImplementedException }><i class="fa fa-ellipsis-h"></i></button>
diff --git a/src/web/app/desktop/tags/user-preview.tag b/src/web/app/desktop/tags/user-preview.tag
index f9806534e2..be5501ede3 100644
--- a/src/web/app/desktop/tags/user-preview.tag
+++ b/src/web/app/desktop/tags/user-preview.tag
@@ -5,7 +5,7 @@
 			<p class="name">{ user.name }</p>
 			<p class="username">@{ user.username }</p>
 		</div>
-		<div class="bio">{ user.bio }</div>
+		<div class="description">{ user.description }</div>
 		<div class="status">
 			<div>
 				<p>投稿</p><a>{ user.posts_count }</a>
@@ -69,7 +69,7 @@
 					font-size 0.8em
 					color #999
 
-			> .bio
+			> .description
 				padding 0 16px
 				font-size 0.7em
 				color #555
diff --git a/src/web/app/desktop/tags/user-profile.tag b/src/web/app/desktop/tags/user-profile.tag
index 03697fd5f3..e6def9ffa8 100644
--- a/src/web/app/desktop/tags/user-profile.tag
+++ b/src/web/app/desktop/tags/user-profile.tag
@@ -3,9 +3,9 @@
 		<mk-big-follow-button user={ user }></mk-big-follow-button>
 		<p class="followed" if={ user.is_followed }>フォローされています</p>
 	</div>
-	<div class="bio" if={ user.bio != '' }>{ user.bio }</div>
-	<div class="birthday" if={ user.birthday }>
-		<p><i class="fa fa-birthday-cake"></i>{ user.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.birthday) }歳)</p>
+	<div class="description" if={ user.description }>{ user.description }</div>
+	<div class="birthday" if={ user.profile.birthday }>
+		<p><i class="fa fa-birthday-cake"></i>{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.profile.birthday) }歳)</p>
 	</div>
 	<div class="twitter" if={ user.twitter }>
 		<p><i class="fa fa-twitter"></i><a href={ 'https://twitter.com/' + user.twitter.screen_name } target="_blank">@{ user.twitter.screen_name }</a></p>
@@ -39,7 +39,7 @@
 					background #eefaff
 					border-radius 4px
 
-			> .bio
+			> .description
 				padding 16px
 				color #555
 				border-top solid 1px #eee
diff --git a/src/web/app/mobile/tags/user-preview.tag b/src/web/app/mobile/tags/user-preview.tag
index 09cfeb49e7..eae4d610d8 100644
--- a/src/web/app/mobile/tags/user-preview.tag
+++ b/src/web/app/mobile/tags/user-preview.tag
@@ -1,8 +1,14 @@
-<mk-user-preview><a class="avatar-anchor" href={ CONFIG.url + '/' + user.username }><img class="avatar" src={ user.avatar_url + '?thumbnail&size=64' } alt="avatar"/></a>
+<mk-user-preview>
+	<a class="avatar-anchor" href={ CONFIG.url + '/' + user.username }>
+		<img class="avatar" src={ user.avatar_url + '?thumbnail&size=64' } alt="avatar"/>
+	</a>
 	<div class="main">
-		<header><a class="name" href={ CONFIG.url + '/' + user.username }>{ user.name }</a><span class="username">@{ user.username }</span></header>
+		<header>
+			<a class="name" href={ CONFIG.url + '/' + user.username }>{ user.name }</a>
+			<span class="username">@{ user.username }</span>
+		</header>
 		<div class="body">
-			<div class="bio">{ user.bio }</div>
+			<div class="description">{ user.description }</div>
 		</div>
 	</div>
 	<style>
@@ -75,7 +81,7 @@
 
 				> .body
 
-					> .bio
+					> .description
 						cursor default
 						display block
 						margin 0
diff --git a/src/web/app/mobile/tags/user.tag b/src/web/app/mobile/tags/user.tag
index 9d95e99144..ae77644e7a 100644
--- a/src/web/app/mobile/tags/user.tag
+++ b/src/web/app/mobile/tags/user.tag
@@ -14,13 +14,13 @@
 					<span class="username">@{ user.username }</span>
 					<span class="followed" if={ user.is_followed }>フォローされています</span>
 				</div>
-				<div class="bio">{ user.bio }</div>
+				<div class="description">{ user.description }</div>
 				<div class="info">
-					<p class="location" if={ user.location }>
-						<i class="fa fa-map-marker"></i>{ user.location }
+					<p class="location" if={ user.profile.location }>
+						<i class="fa fa-map-marker"></i>{ user.profile.location }
 					</p>
-					<p class="birthday" if={ user.birthday }>
-						<i class="fa fa-birthday-cake"></i>{ user.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.birthday) }歳)
+					<p class="birthday" if={ user.profile.birthday }>
+						<i class="fa fa-birthday-cake"></i>{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.profile.birthday) }歳)
 					</p>
 				</div>
 				<div class="friends">
@@ -119,7 +119,7 @@
 								background #f8f8f8
 								border-radius 4px
 
-						> .bio
+						> .description
 							margin 8px 0
 							color #333
 
diff --git a/test/api.js b/test/api.js
index ce75a4028c..40dbdf201b 100644
--- a/test/api.js
+++ b/test/api.js
@@ -134,7 +134,11 @@ describe('API', () => {
 
 	describe('i/update', () => {
 		it('アカウント設定を更新できる', () => new Promise(async (done) => {
-			const me = await insertSakurako();
+			const me = await insertSakurako({
+				profile: {
+					gender: 'female'
+				}
+			});
 
 			const myName = '大室櫻子';
 			const myLocation = '七森中';
@@ -148,8 +152,10 @@ describe('API', () => {
 				res.should.have.status(200);
 				res.body.should.be.a('object');
 				res.body.should.have.property('name').eql(myName);
-				res.body.should.have.property('location').eql(myLocation);
-				res.body.should.have.property('birthday').eql(myBirthday);
+				res.body.should.have.property('profile').a('object');
+				res.body.should.have.deep.property('profile.location').eql(myLocation);
+				res.body.should.have.deep.property('profile.birthday').eql(myBirthday);
+				res.body.should.have.deep.property('profile.gender').eql('female');
 				done();
 			});
 		}));
@@ -159,11 +165,12 @@ describe('API', () => {
 				birthday: '2000-09-07'
 			});
 			request('/i/update', {
-				birthday: ''
+				birthday: null
 			}, me).then(res => {
 				res.should.have.status(200);
 				res.body.should.be.a('object');
-				res.body.should.have.property('birthday').eql(null);
+				res.body.should.have.property('profile').a('object');
+				res.body.should.have.deep.property('profile.birthday').eql(null);
 				done();
 			});
 		}));
@@ -1214,7 +1221,8 @@ async function insertSakurako(opts) {
 		token: '!00000000000000000000000000000000',
 		username: 'sakurako',
 		username_lower: 'sakurako',
-		password: '$2a$08$FnHXg3tP.M/kINWgQSXNqeoBsiVrkj.ecXX8mW9rfBzMRkibYfjYy' // HimawariDaisuki06160907
+		password: '$2a$08$FnHXg3tP.M/kINWgQSXNqeoBsiVrkj.ecXX8mW9rfBzMRkibYfjYy', // HimawariDaisuki06160907
+		profile: {}
 	}, opts));
 }
 
@@ -1223,7 +1231,8 @@ async function insertHimawari(opts) {
 		token: '!00000000000000000000000000000001',
 		username: 'himawari',
 		username_lower: 'himawari',
-		password: '$2a$08$OPESxR2RE/ZijjGanNKk6ezSqGFitqsbZqTjWUZPLhORMKxHCbc4O' // ilovesakurako
+		password: '$2a$08$OPESxR2RE/ZijjGanNKk6ezSqGFitqsbZqTjWUZPLhORMKxHCbc4O', // ilovesakurako
+		profile: {}
 	}, opts));
 }
 
diff --git a/tools/migration/user-profile.js b/tools/migration/user-profile.js
new file mode 100644
index 0000000000..e6666319e1
--- /dev/null
+++ b/tools/migration/user-profile.js
@@ -0,0 +1,18 @@
+db.users.find({}).forEach(function(user) {
+	print(user._id);
+	db.users.update({ _id: user._id }, {
+		$rename: {
+			bio: 'description'
+		},
+		$unset: {
+			location: '',
+			birthday: ''
+		},
+		$set: {
+			profile: {
+				location: user.location || null,
+				birthday: user.birthday || null
+			}
+		}
+	}, false, false);
+});