なんかもうめっちゃやった
This commit is contained in:
		| @@ -2,6 +2,10 @@ ChangeLog (Release Notes) | ||||
| ========================= | ||||
| 主に notable な changes を書いていきます | ||||
|  | ||||
| unreleased | ||||
| ----------------- | ||||
| * ホームのカスタマイズを実装するなど | ||||
|  | ||||
| 2971 (2017/11/08) | ||||
| ----------------- | ||||
| * バグ修正 | ||||
|   | ||||
| @@ -99,7 +99,7 @@ | ||||
| 		"autwh": "0.0.1", | ||||
| 		"bcryptjs": "2.4.3", | ||||
| 		"body-parser": "1.18.2", | ||||
| 		"cafy": "3.1.1", | ||||
| 		"cafy": "3.2.0", | ||||
| 		"chalk": "2.3.0", | ||||
| 		"compression": "1.7.1", | ||||
| 		"cors": "2.8.4", | ||||
| @@ -142,6 +142,7 @@ | ||||
| 		"rndstr": "1.0.0", | ||||
| 		"s-age": "1.1.0", | ||||
| 		"serve-favicon": "2.4.5", | ||||
| 		"sortablejs": "1.7.0", | ||||
| 		"summaly": "2.0.3", | ||||
| 		"syuilo-password-strength": "0.0.1", | ||||
| 		"tcp-port-used": "0.1.2", | ||||
|   | ||||
| @@ -159,6 +159,11 @@ const endpoints: Endpoint[] = [ | ||||
| 		}, | ||||
| 		kind: 'account-write' | ||||
| 	}, | ||||
| 	{ | ||||
| 		name: 'i/update_home', | ||||
| 		withCredential: true, | ||||
| 		kind: 'account-write' | ||||
| 	}, | ||||
| 	{ | ||||
| 		name: 'i/change_password', | ||||
| 		withCredential: true | ||||
|   | ||||
| @@ -13,38 +13,27 @@ import Appdata from '../../../models/appdata'; | ||||
|  * @param {Boolean} isSecure | ||||
|  * @return {Promise<any>} | ||||
|  */ | ||||
| module.exports = (params, user, app, isSecure) => new Promise(async (res, rej) => { | ||||
| module.exports = (params, user, app) => new Promise(async (res, rej) => { | ||||
| 	if (app == null) return rej('このAPIはサードパーティAppからのみ利用できます'); | ||||
|  | ||||
| 	// Get 'key' parameter | ||||
| 	const [key = null, keyError] = $(params.key).optional.nullable.string().match(/[a-z_]+/).$; | ||||
| 	if (keyError) return rej('invalid key param'); | ||||
|  | ||||
| 	if (isSecure) { | ||||
| 		if (!user.data) { | ||||
| 			return res(); | ||||
| 		} | ||||
| 		if (key !== null) { | ||||
| 			const data = {}; | ||||
| 			data[key] = user.data[key]; | ||||
| 			res(data); | ||||
| 		} else { | ||||
| 			res(user.data); | ||||
| 		} | ||||
| 	} else { | ||||
| 		const select = {}; | ||||
| 		if (key !== null) { | ||||
| 			select[`data.${key}`] = true; | ||||
| 		} | ||||
| 		const appdata = await Appdata.findOne({ | ||||
| 			app_id: app._id, | ||||
| 			user_id: user._id | ||||
| 		}, { | ||||
| 				fields: select | ||||
| 			}); | ||||
| 	const select = {}; | ||||
| 	if (key !== null) { | ||||
| 		select[`data.${key}`] = true; | ||||
| 	} | ||||
| 	const appdata = await Appdata.findOne({ | ||||
| 		app_id: app._id, | ||||
| 		user_id: user._id | ||||
| 	}, { | ||||
| 		fields: select | ||||
| 	}); | ||||
|  | ||||
| 		if (appdata) { | ||||
| 			res(appdata.data); | ||||
| 		} else { | ||||
| 			res(); | ||||
| 		} | ||||
| 	if (appdata) { | ||||
| 		res(appdata.data); | ||||
| 	} else { | ||||
| 		res(); | ||||
| 	} | ||||
| }); | ||||
|   | ||||
| @@ -3,9 +3,6 @@ | ||||
|  */ | ||||
| import $ from 'cafy'; | ||||
| import Appdata from '../../../models/appdata'; | ||||
| import User from '../../../models/user'; | ||||
| import serialize from '../../../serializers/user'; | ||||
| import event from '../../../event'; | ||||
|  | ||||
| /** | ||||
|  * Set app data | ||||
| @@ -16,7 +13,9 @@ import event from '../../../event'; | ||||
|  * @param {Boolean} isSecure | ||||
|  * @return {Promise<any>} | ||||
|  */ | ||||
| module.exports = (params, user, app, isSecure) => new Promise(async (res, rej) => { | ||||
| module.exports = (params, user, app) => new Promise(async (res, rej) => { | ||||
| 	if (app == null) return rej('このAPIはサードパーティAppからのみ利用できます'); | ||||
|  | ||||
| 	// Get 'data' parameter | ||||
| 	const [data, dataError] = $(params.data).optional.object() | ||||
| 		.pipe(obj => { | ||||
| @@ -43,31 +42,17 @@ module.exports = (params, user, app, isSecure) => new Promise(async (res, rej) = | ||||
| 		set[`data.${key}`] = value; | ||||
| 	} | ||||
|  | ||||
| 	if (isSecure) { | ||||
| 		const _user = await User.findOneAndUpdate(user._id, { | ||||
| 	await Appdata.update({ | ||||
| 		app_id: app._id, | ||||
| 		user_id: user._id | ||||
| 	}, Object.assign({ | ||||
| 		app_id: app._id, | ||||
| 		user_id: user._id | ||||
| 	}, { | ||||
| 			$set: set | ||||
| 		}), { | ||||
| 			upsert: true | ||||
| 		}); | ||||
|  | ||||
| 		res(204); | ||||
|  | ||||
| 		// Publish i updated event | ||||
| 		event(user._id, 'i_updated', await serialize(_user, user, { | ||||
| 			detail: true, | ||||
| 			includeSecrets: true | ||||
| 		})); | ||||
| 	} else { | ||||
| 		await Appdata.update({ | ||||
| 			app_id: app._id, | ||||
| 			user_id: user._id | ||||
| 		}, Object.assign({ | ||||
| 			app_id: app._id, | ||||
| 			user_id: user._id | ||||
| 		}, { | ||||
| 				$set: set | ||||
| 			}), { | ||||
| 				upsert: true | ||||
| 			}); | ||||
|  | ||||
| 		res(204); | ||||
| 	} | ||||
| 	res(204); | ||||
| }); | ||||
|   | ||||
| @@ -48,13 +48,19 @@ module.exports = async (params, user, _, isSecure) => new Promise(async (res, re | ||||
| 	if (bannerIdErr) return rej('invalid banner_id param'); | ||||
| 	if (bannerId) user.banner_id = bannerId; | ||||
|  | ||||
| 	// Get 'show_donation' parameter | ||||
| 	const [showDonation, showDonationErr] = $(params.show_donation).optional.boolean().$; | ||||
| 	if (showDonationErr) return rej('invalid show_donation param'); | ||||
| 	if (showDonation) user.client_settings.show_donation = showDonation; | ||||
|  | ||||
| 	await User.update(user._id, { | ||||
| 		$set: { | ||||
| 			name: user.name, | ||||
| 			description: user.description, | ||||
| 			avatar_id: user.avatar_id, | ||||
| 			banner_id: user.banner_id, | ||||
| 			profile: user.profile | ||||
| 			profile: user.profile, | ||||
| 			'client_settings.show_donation': user.client_settings.show_donation | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
|   | ||||
							
								
								
									
										34
									
								
								src/api/endpoints/i/update_home.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/api/endpoints/i/update_home.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| /** | ||||
|  * Module dependencies | ||||
|  */ | ||||
| import $ from 'cafy'; | ||||
| import User from '../../models/user'; | ||||
|  | ||||
| /** | ||||
|  * Update myself | ||||
|  * | ||||
|  * @param {any} params | ||||
|  * @param {any} user | ||||
|  * @param {any} _ | ||||
|  * @param {boolean} isSecure | ||||
|  * @return {Promise<any>} | ||||
|  */ | ||||
| module.exports = async (params, user, _, isSecure) => new Promise(async (res, rej) => { | ||||
| 	// Get 'home' parameter | ||||
| 	const [home, homeErr] = $(params.home).array().each( | ||||
| 		$().strict.object() | ||||
| 			.have('name', $().string()) | ||||
| 			.have('id', $().string()) | ||||
| 			.have('place', $().string()) | ||||
| 			.have('data', $().object())).$; | ||||
| 	if (homeErr) return rej('invalid home param'); | ||||
|  | ||||
| 	await User.update(user._id, { | ||||
| 		$set: { | ||||
| 			'client_settings.home': home | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
| 	// Send response | ||||
| 	res(); | ||||
| }); | ||||
| @@ -1,3 +1,4 @@ | ||||
| import * as uuid from 'uuid'; | ||||
| import * as express from 'express'; | ||||
| import * as bcrypt from 'bcryptjs'; | ||||
| import recaptcha = require('recaptcha-promise'); | ||||
| @@ -11,6 +12,28 @@ recaptcha.init({ | ||||
| 	secret_key: config.recaptcha.secretKey | ||||
| }); | ||||
|  | ||||
| const home = { | ||||
| 	left: [ | ||||
| 		'profile', | ||||
| 		'calendar', | ||||
| 		'activity', | ||||
| 		'rss-reader', | ||||
| 		'trends', | ||||
| 		'photo-stream', | ||||
| 		'version' | ||||
| 	], | ||||
| 	right: [ | ||||
| 		'broadcast', | ||||
| 		'notifications', | ||||
| 		'user-recommendation', | ||||
| 		'recommended-polls', | ||||
| 		'server', | ||||
| 		'donation', | ||||
| 		'nav', | ||||
| 		'tips' | ||||
| 	] | ||||
| }; | ||||
|  | ||||
| export default async (req: express.Request, res: express.Response) => { | ||||
| 	// Verify recaptcha | ||||
| 	// ただしテスト時はこの機構は障害となるため無効にする | ||||
| @@ -60,6 +83,28 @@ export default async (req: express.Request, res: express.Response) => { | ||||
| 	// Generate secret | ||||
| 	const secret = generateUserToken(); | ||||
|  | ||||
| 	//#region Construct home data | ||||
| 	const homeData = []; | ||||
|  | ||||
| 	home.left.forEach(widget => { | ||||
| 		homeData.push({ | ||||
| 			name: widget, | ||||
| 			id: uuid(), | ||||
| 			place: 'left', | ||||
| 			data: {} | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
| 	home.right.forEach(widget => { | ||||
| 		homeData.push({ | ||||
| 			name: widget, | ||||
| 			id: uuid(), | ||||
| 			place: 'right', | ||||
| 			data: {} | ||||
| 		}); | ||||
| 	}); | ||||
| 	//#endregion | ||||
|  | ||||
| 	// Create account | ||||
| 	const account: IUser = await User.insert({ | ||||
| 		token: secret, | ||||
| @@ -88,6 +133,11 @@ export default async (req: express.Request, res: express.Response) => { | ||||
| 			height: null, | ||||
| 			location: null, | ||||
| 			weight: null | ||||
| 		}, | ||||
| 		settings: {}, | ||||
| 		client_settings: { | ||||
| 			home: homeData, | ||||
| 			show_donation: false | ||||
| 		} | ||||
| 	}); | ||||
|  | ||||
|   | ||||
| @@ -35,9 +35,10 @@ export default ( | ||||
| 	let _user: any; | ||||
|  | ||||
| 	const fields = opts.detail ? { | ||||
| 		data: false | ||||
| 		settings: false | ||||
| 	} : { | ||||
| 		data: false, | ||||
| 		settings: false, | ||||
| 		client_settings: false, | ||||
| 		profile: false, | ||||
| 		keywords: false, | ||||
| 		domains: false | ||||
| @@ -72,7 +73,7 @@ export default ( | ||||
| 	delete _user._id; | ||||
|  | ||||
| 	// Remove needless properties | ||||
| 	delete _user.lates_post; | ||||
| 	delete _user.latest_post; | ||||
|  | ||||
| 	// Remove private properties | ||||
| 	delete _user.password; | ||||
| @@ -86,8 +87,8 @@ export default ( | ||||
|  | ||||
| 	// Visible via only the official client | ||||
| 	if (!opts.includeSecrets) { | ||||
| 		delete _user.data; | ||||
| 		delete _user.email; | ||||
| 		delete _user.client_settings; | ||||
| 	} | ||||
|  | ||||
| 	_user.avatar_url = _user.avatar_id != null | ||||
|   | ||||
| @@ -1,45 +0,0 @@ | ||||
| import uuid from './uuid'; | ||||
|  | ||||
| const home = { | ||||
| 	left: [ | ||||
| 		'profile', | ||||
| 		'calendar', | ||||
| 		'rss-reader', | ||||
| 		'photo-stream', | ||||
| 		'version' | ||||
| 	], | ||||
| 	right: [ | ||||
| 		'broadcast', | ||||
| 		'notifications', | ||||
| 		'user-recommendation', | ||||
| 		'donation', | ||||
| 		'nav', | ||||
| 		'tips' | ||||
| 	] | ||||
| }; | ||||
|  | ||||
| export default () => { | ||||
| 	const homeData = []; | ||||
|  | ||||
| 	home.left.forEach(widget => { | ||||
| 		homeData.push({ | ||||
| 			name: widget, | ||||
| 			id: uuid(), | ||||
| 			place: 'left' | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
| 	home.right.forEach(widget => { | ||||
| 		homeData.push({ | ||||
| 			name: widget, | ||||
| 			id: uuid(), | ||||
| 			place: 'right' | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
| 	const data = { | ||||
| 		home: JSON.stringify(homeData) | ||||
| 	}; | ||||
|  | ||||
| 	return data; | ||||
| }; | ||||
| @@ -9,6 +9,7 @@ let page = null; | ||||
| export default me => { | ||||
| 	route('/',                       index); | ||||
| 	route('/selectdrive',            selectDrive); | ||||
| 	route('/i/customize-home',       customizeHome); | ||||
| 	route('/i/drive',                drive); | ||||
| 	route('/i/drive/folder/:folder', drive); | ||||
| 	route('/i/mentions',             mentions); | ||||
| @@ -27,6 +28,10 @@ export default me => { | ||||
| 		mount(document.createElement('mk-home-page')); | ||||
| 	} | ||||
|  | ||||
| 	function customizeHome() { | ||||
| 		mount(document.createElement('mk-home-customize-page')); | ||||
| 	} | ||||
|  | ||||
| 	function entrance() { | ||||
| 		mount(document.createElement('mk-entrance')); | ||||
| 		document.documentElement.setAttribute('data-page', 'entrance'); | ||||
|   | ||||
| @@ -54,11 +54,10 @@ | ||||
| 			e.preventDefault(); | ||||
| 			e.stopPropagation(); | ||||
|  | ||||
| 			this.I.data.no_donation = 'true'; | ||||
| 			this.I.client_settings.show_donation = false; | ||||
| 			this.I.update(); | ||||
| 			this.api('i/appdata/set', { | ||||
| 				key: 'no_donation', | ||||
| 				value: 'true' | ||||
| 			this.api('i/update', { | ||||
| 				show_donation: false | ||||
| 			}); | ||||
|  | ||||
| 			this.unmount(); | ||||
|   | ||||
| @@ -54,7 +54,7 @@ | ||||
| 		this.mixin('api'); | ||||
|  | ||||
| 		this.initializing = true; | ||||
| 		this.view = 0; | ||||
| 		this.view = this.opts.data.hasOwnProperty('view') ? this.opts.data.view : 0; | ||||
|  | ||||
| 		this.on('mount', () => { | ||||
| 			this.api('aggregation/users/activity', { | ||||
| @@ -71,6 +71,14 @@ | ||||
| 		this.toggle = () => { | ||||
| 			this.view++; | ||||
| 			if (this.view == 2) this.view = 0; | ||||
|  | ||||
| 			// Save view state | ||||
| 			this.I.client_settings.home.filter(w => w.id == this.opts.id)[0].data.view = this.view; | ||||
| 			this.api('i/update_home', { | ||||
| 				home: this.I.client_settings.home | ||||
| 			}).then(() => { | ||||
| 				this.I.update(); | ||||
| 			}); | ||||
| 		}; | ||||
| 	</script> | ||||
| </mk-activity-home-widget> | ||||
|   | ||||
| @@ -56,10 +56,11 @@ | ||||
| 	<script> | ||||
| 		import Connection from '../../../common/scripts/server-stream'; | ||||
|  | ||||
| 		this.mixin('i'); | ||||
| 		this.mixin('api'); | ||||
|  | ||||
| 		this.initializing = true; | ||||
| 		this.view = 0; | ||||
| 		this.view = this.opts.data.hasOwnProperty('view') ? this.opts.data.view : 0; | ||||
| 		this.connection = new Connection(); | ||||
|  | ||||
| 		this.on('mount', () => { | ||||
| @@ -78,6 +79,14 @@ | ||||
| 		this.toggle = () => { | ||||
| 			this.view++; | ||||
| 			if (this.view == 6) this.view = 0; | ||||
|  | ||||
| 			// Save view state | ||||
| 			this.I.client_settings.home.filter(w => w.id == this.opts.id)[0].data.view = this.view; | ||||
| 			this.api('i/update_home', { | ||||
| 				home: this.I.client_settings.home | ||||
| 			}).then(() => { | ||||
| 				this.I.update(); | ||||
| 			}); | ||||
| 		}; | ||||
| 	</script> | ||||
| </mk-server-home-widget> | ||||
|   | ||||
| @@ -1,4 +1,30 @@ | ||||
| <mk-home> | ||||
| <mk-home data-customize={ opts.customize }> | ||||
| 	<div class="customize" if={ opts.customize }> | ||||
| 		<div class="adder"> | ||||
| 			<p>ウィジェットを追加:</p> | ||||
| 			<select ref="widgetSelector"> | ||||
| 				<option value="profile">プロフィール</option> | ||||
| 				<option value="calendar">カレンダー</option> | ||||
| 				<option value="activity">アクティビティ</option> | ||||
| 				<option value="rss-reader">RSSリーダー</option> | ||||
| 				<option value="trends">トレンド</option> | ||||
| 				<option value="photo-stream">フォトストリーム</option> | ||||
| 				<option value="version">バージョン</option> | ||||
| 				<option value="broadcast">ブロードキャスト</option> | ||||
| 				<option value="notifications">通知</option> | ||||
| 				<option value="user-recommendation">おすすめユーザー</option> | ||||
| 				<option value="recommended-polls">投票</option> | ||||
| 				<option value="server">サーバー情報</option> | ||||
| 				<option value="donation">寄付のお願い</option> | ||||
| 				<option value="nav">ナビゲーション</option> | ||||
| 				<option value="tips">ヒント</option> | ||||
| 			</select> | ||||
| 			<button onclick={ addWidget }>追加</button> | ||||
| 		</div> | ||||
| 		<div class="trash" ref="trash"> | ||||
| 			<p class="ignore"><b>ゴミ箱</b> (ここにウィジェットをドロップすると削除できます)</p> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div class="main"> | ||||
| 		<div class="left" ref="left"></div> | ||||
| 		<main> | ||||
| @@ -11,25 +37,37 @@ | ||||
| 		:scope | ||||
| 			display block | ||||
|  | ||||
| 			&:not([data-customize]) | ||||
| 				> .main > *:empty | ||||
| 					display none | ||||
|  | ||||
| 			> .customize | ||||
| 				display flex | ||||
| 				margin 0 auto | ||||
| 				max-width 1200px | ||||
| 				background #fff1c8 | ||||
|  | ||||
| 				> div | ||||
| 					width 50% | ||||
|  | ||||
| 					&.trash | ||||
| 						background #ffc5c5 | ||||
|  | ||||
| 			> .main | ||||
| 				display flex | ||||
| 				justify-content center | ||||
| 				margin 0 auto | ||||
| 				max-width 1200px | ||||
|  | ||||
| 				&:after | ||||
| 					content "" | ||||
| 					display block | ||||
| 					clear both | ||||
|  | ||||
| 				> * | ||||
| 					float left | ||||
|  | ||||
| 					> * | ||||
| 					> *:not(.customize-container) | ||||
| 					> .customize-container > * | ||||
| 						display block | ||||
| 						//border solid 1px #eaeaea | ||||
| 						border solid 1px rgba(0, 0, 0, 0.075) | ||||
| 						border-radius 6px | ||||
| 						//box-shadow 0px 2px 16px rgba(0, 0, 0, 0.2) | ||||
|  | ||||
| 					> *:not(.customize-container) | ||||
| 					> .customize-container | ||||
| 						&:not(:last-child) | ||||
| 							margin-bottom 16px | ||||
|  | ||||
| @@ -40,6 +78,12 @@ | ||||
| 				> *:not(main) | ||||
| 					width 275px | ||||
|  | ||||
| 					> .customize-container | ||||
| 						cursor move | ||||
|  | ||||
| 						> * | ||||
| 							pointer-events none | ||||
|  | ||||
| 				> .left | ||||
| 					padding 16px 0 16px 16px | ||||
|  | ||||
| @@ -58,66 +102,49 @@ | ||||
|  | ||||
| 	</style> | ||||
| 	<script> | ||||
| 		import uuid from 'uuid'; | ||||
| 		import Sortable from 'sortablejs'; | ||||
|  | ||||
| 		this.mixin('i'); | ||||
| 		this.mixin('api'); | ||||
|  | ||||
| 		this.mode = this.opts.mode || 'timeline'; | ||||
|  | ||||
| 		const _home = { | ||||
| 			left: [ | ||||
| 				'profile', | ||||
| 				'calendar', | ||||
| 				'activity', | ||||
| 				'rss-reader', | ||||
| 				'trends', | ||||
| 				'photo-stream', | ||||
| 				'version' | ||||
| 			], | ||||
| 			right: [ | ||||
| 				'broadcast', | ||||
| 				'notifications', | ||||
| 				'user-recommendation', | ||||
| 				'recommended-polls', | ||||
| 				'server', | ||||
| 				'donation', | ||||
| 				'nav', | ||||
| 				'tips' | ||||
| 			] | ||||
| 		}; | ||||
|  | ||||
| 		this.home = []; | ||||
|  | ||||
| 		this.on('mount', () => { | ||||
| 			this.refs.tl.on('loaded', () => { | ||||
| 				this.trigger('loaded'); | ||||
| 			}); | ||||
| /* | ||||
| 			this.I.data.home.forEach(widget => { | ||||
|  | ||||
| 			this.I.client_settings.home.forEach(widget => { | ||||
| 				try { | ||||
| 					const el = document.createElement(`mk-${widget.name}-home-widget`); | ||||
| 					switch (widget.place) { | ||||
| 						case 'left': this.refs.left.appendChild(el); break; | ||||
| 						case 'right': this.refs.right.appendChild(el); break; | ||||
| 					} | ||||
| 					this.home.push(riot.mount(el, { | ||||
| 						id: widget.id, | ||||
| 						data: widget.data | ||||
| 					})[0]); | ||||
| 					this.setWidget(widget); | ||||
| 				} catch (e) { | ||||
| 					// noop | ||||
| 				} | ||||
| 			}); | ||||
| */ | ||||
| 			_home.left.forEach(widget => { | ||||
| 				const el = document.createElement(`mk-${widget}-home-widget`); | ||||
| 				this.refs.left.appendChild(el); | ||||
| 				this.home.push(riot.mount(el)[0]); | ||||
| 			}); | ||||
|  | ||||
| 			_home.right.forEach(widget => { | ||||
| 				const el = document.createElement(`mk-${widget}-home-widget`); | ||||
| 				this.refs.right.appendChild(el); | ||||
| 				this.home.push(riot.mount(el)[0]); | ||||
| 			}); | ||||
| 			if (this.opts.customize) { | ||||
| 				const sortableOption = { | ||||
| 					group: 'kyoppie', | ||||
| 					animation: 150, | ||||
| 					filter: '.ignore', | ||||
| 					onSort: this.saveHome | ||||
| 				}; | ||||
|  | ||||
| 				new Sortable(this.refs.left, sortableOption); | ||||
| 				new Sortable(this.refs.right, sortableOption); | ||||
| 				new Sortable(this.refs.trash, Object.assign({}, sortableOption, { | ||||
| 					onAdd: evt => { | ||||
| 						const el = evt.item; | ||||
| 						const id = el.getAttribute('data-widget-id'); | ||||
| 						el.parentNode.removeChild(el); | ||||
| 						this.I.client_settings.home = this.I.client_settings.home.filter(w => w.id != id); | ||||
| 						this.saveHome(); | ||||
| 					} | ||||
| 				})); | ||||
| 			} | ||||
| 		}); | ||||
|  | ||||
| 		this.on('unmount', () => { | ||||
| @@ -125,5 +152,83 @@ | ||||
| 				widget.unmount(); | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		this.setWidget = (widget, prepend = false) => { | ||||
| 			const el = document.createElement(`mk-${widget.name}-home-widget`); | ||||
|  | ||||
| 			let actualEl; | ||||
|  | ||||
| 			if (this.opts.customize) { | ||||
| 				const container = document.createElement('div'); | ||||
| 				container.classList.add('customize-container'); | ||||
| 				container.setAttribute('data-widget-id', widget.id); | ||||
| 				container.appendChild(el); | ||||
| 				actualEl = container; | ||||
| 			} else { | ||||
| 				actualEl = el; | ||||
| 			} | ||||
|  | ||||
| 			switch (widget.place) { | ||||
| 				case 'left': | ||||
| 					if (prepend) { | ||||
| 						this.refs.left.insertBefore(actualEl, this.refs.left.firstChild); | ||||
| 					} else { | ||||
| 						this.refs.left.appendChild(actualEl); | ||||
| 					} | ||||
| 					break; | ||||
| 				case 'right': | ||||
| 					if (prepend) { | ||||
| 						this.refs.right.insertBefore(actualEl, this.refs.right.firstChild); | ||||
| 					} else { | ||||
| 						this.refs.right.appendChild(actualEl); | ||||
| 					} | ||||
| 					break; | ||||
| 			} | ||||
|  | ||||
| 			this.home.push(riot.mount(el, { | ||||
| 				id: widget.id, | ||||
| 				data: widget.data | ||||
| 			})[0]); | ||||
| 		}; | ||||
|  | ||||
| 		this.addWidget = () => { | ||||
| 			const widget = { | ||||
| 				name: this.refs.widgetSelector.options[this.refs.widgetSelector.selectedIndex].value, | ||||
| 				id: uuid(), | ||||
| 				place: 'left', | ||||
| 				data: {} | ||||
| 			}; | ||||
|  | ||||
| 			this.I.client_settings.home.unshift(widget); | ||||
|  | ||||
| 			this.setWidget(widget, true); | ||||
|  | ||||
| 			this.saveHome(); | ||||
| 		}; | ||||
|  | ||||
| 		this.saveHome = () => { | ||||
| 			const data = []; | ||||
|  | ||||
| 			Array.from(this.refs.left.children).forEach(el => { | ||||
| 				const id = el.getAttribute('data-widget-id'); | ||||
| 				const widget = this.I.client_settings.home.find(w => w.id == id); | ||||
| 				widget.place = 'left'; | ||||
| 				data.push(widget); | ||||
| 			}); | ||||
|  | ||||
| 			Array.from(this.refs.right.children).forEach(el => { | ||||
| 				const id = el.getAttribute('data-widget-id'); | ||||
| 				const widget = this.I.client_settings.home.find(w => w.id == id); | ||||
| 				widget.place = 'right'; | ||||
| 				data.push(widget); | ||||
| 			}); | ||||
|  | ||||
| 			this.api('i/update_home', { | ||||
| 				home: data | ||||
| 			}).then(() => { | ||||
| 				this.I.client_settings.home = data; | ||||
| 				this.I.update(); | ||||
| 			}); | ||||
| 		}; | ||||
| 	</script> | ||||
| </mk-home> | ||||
|   | ||||
| @@ -57,6 +57,7 @@ require('./pages/entrance.tag'); | ||||
| require('./pages/entrance/signin.tag'); | ||||
| require('./pages/entrance/signup.tag'); | ||||
| require('./pages/home.tag'); | ||||
| require('./pages/home-customize.tag'); | ||||
| require('./pages/user.tag'); | ||||
| require('./pages/post.tag'); | ||||
| require('./pages/search.tag'); | ||||
|   | ||||
							
								
								
									
										14
									
								
								src/web/app/desktop/tags/pages/home-customize.tag
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/web/app/desktop/tags/pages/home-customize.tag
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| <mk-home-customize-page> | ||||
| 	<mk-ui ref="ui" page="timeline"> | ||||
| 		<mk-home ref="home" mode={ parent.opts.mode } customize={ true }/> | ||||
| 	</mk-ui> | ||||
| 	<style> | ||||
| 		:scope | ||||
| 			display block | ||||
| 	</style> | ||||
| 	<script> | ||||
| 		this.on('mount', () => { | ||||
| 			document.title = 'Misskey - ホームのカスタマイズ'; | ||||
| 		}); | ||||
| 	</script> | ||||
| </mk-home-customize-page> | ||||
| @@ -38,6 +38,7 @@ | ||||
|  | ||||
| 		<section class="web" show={ page == 'web' }> | ||||
| 			<h1>デザイン</h1> | ||||
| 			<a href="/i/customize-home">ホームをカスタマイズ</a> | ||||
| 		</section> | ||||
|  | ||||
| 		<section class="web" show={ page == 'web' }> | ||||
|   | ||||
| @@ -37,7 +37,7 @@ | ||||
| </mk-ui> | ||||
|  | ||||
| <mk-ui-header> | ||||
| 	<mk-donation if={ SIGNIN && I.data.no_donation != 'true' }/> | ||||
| 	<mk-donation if={ SIGNIN && I.client_settings.show_donation }/> | ||||
| 	<mk-special-message/> | ||||
| 	<div class="main"> | ||||
| 		<div class="backdrop"></div> | ||||
|   | ||||
| @@ -11,7 +11,6 @@ import checkForUpdate from './common/scripts/check-for-update'; | ||||
| import Connection from './common/scripts/home-stream'; | ||||
| import Progress from './common/scripts/loading'; | ||||
| import mixin from './common/mixins'; | ||||
| import generateDefaultUserdata from './common/scripts/generate-default-userdata'; | ||||
| import CONFIG from './common/scripts/config'; | ||||
| require('./common/tags'); | ||||
|  | ||||
| @@ -156,9 +155,7 @@ function fetchme(token, cb) { | ||||
| 		res.json().then(i => { | ||||
| 			me = i; | ||||
| 			me.token = token; | ||||
|  | ||||
| 			// initialize it if user data is empty | ||||
| 			me.data ? done() : init(); | ||||
| 			done(); | ||||
| 		}); | ||||
| 	}, () => { // When failure | ||||
| 		// Render the error screen | ||||
| @@ -170,17 +167,6 @@ function fetchme(token, cb) { | ||||
| 	function done() { | ||||
| 		if (cb) cb(me); | ||||
| 	} | ||||
|  | ||||
| 	// Initialize user data | ||||
| 	function init() { | ||||
| 		const data = generateDefaultUserdata(); | ||||
| 		api(token, 'i/appdata/set', { | ||||
| 			data | ||||
| 		}).then(() => { | ||||
| 			me.data = data; | ||||
| 			done(); | ||||
| 		}); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // BSoD | ||||
|   | ||||
							
								
								
									
										89
									
								
								tools/migration/node.2017-11-08..js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								tools/migration/node.2017-11-08..js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| const uuid = require('uuid'); | ||||
| const { default: User } = require('../../built/api/models/user') | ||||
| const { default: zip } = require('@prezzemolo/zip') | ||||
|  | ||||
| const home = { | ||||
| 	left: [ | ||||
| 		'profile', | ||||
| 		'calendar', | ||||
| 		'activity', | ||||
| 		'rss-reader', | ||||
| 		'trends', | ||||
| 		'photo-stream', | ||||
| 		'version' | ||||
| 	], | ||||
| 	right: [ | ||||
| 		'broadcast', | ||||
| 		'notifications', | ||||
| 		'user-recommendation', | ||||
| 		'recommended-polls', | ||||
| 		'server', | ||||
| 		'donation', | ||||
| 		'nav', | ||||
| 		'tips' | ||||
| 	] | ||||
| }; | ||||
|  | ||||
|  | ||||
| const migrate = async (doc) => { | ||||
|  | ||||
| 	//#region Construct home data | ||||
| 	const homeData = []; | ||||
|  | ||||
| 	home.left.forEach(widget => { | ||||
| 		homeData.push({ | ||||
| 			name: widget, | ||||
| 			id: uuid(), | ||||
| 			place: 'left', | ||||
| 			data: {} | ||||
| 		}); | ||||
| 	}); | ||||
|  | ||||
| 	home.right.forEach(widget => { | ||||
| 		homeData.push({ | ||||
| 			name: widget, | ||||
| 			id: uuid(), | ||||
| 			place: 'right', | ||||
| 			data: {} | ||||
| 		}); | ||||
| 	}); | ||||
| 	//#endregion | ||||
|  | ||||
| 	const result = await User.update(doc._id, { | ||||
| 		$unset: { | ||||
| 			data: '' | ||||
| 		}, | ||||
| 		$set: { | ||||
| 			'settings': {}, | ||||
| 			'client_settings.home': homeData, | ||||
| 			'client_settings.show_donation': false | ||||
| 		} | ||||
| 	}) | ||||
|  | ||||
| 	return added && result.ok === 1 | ||||
| } | ||||
|  | ||||
| async function main() { | ||||
| 	const count = await db.get('users').count(); | ||||
|  | ||||
| 	console.log(`there are ${count} users.`) | ||||
|  | ||||
| 	const dop = Number.parseInt(process.argv[2]) || 5 | ||||
| 	const idop = ((count - (count % dop)) / dop) + 1 | ||||
|  | ||||
| 	return zip( | ||||
| 		1, | ||||
| 		async (time) => { | ||||
| 			console.log(`${time} / ${idop}`) | ||||
| 			const docs = await db.get('users').find({}, { limit: dop, skip: time * dop }) | ||||
| 			return Promise.all(docs.map(migrate)) | ||||
| 		}, | ||||
| 		idop | ||||
| 	).then(a => { | ||||
| 		const rv = [] | ||||
| 		a.forEach(e => rv.push(...e)) | ||||
| 		return rv | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| main().then(console.dir).catch(console.error) | ||||
		Reference in New Issue
	
	Block a user
	 syuilo
					syuilo