wip
This commit is contained in:
		
							parent
							
								
									aa0dadcf07
								
							
						
					
					
						commit
						d1aba96a29
					
				
					 2 changed files with 392 additions and 388 deletions
				
			
		| 
						 | 
					@ -1,388 +0,0 @@
 | 
				
			||||||
<mk-home data-customize={ opts.customize }>
 | 
					 | 
				
			||||||
	<div class="customize" v-if="opts.customize">
 | 
					 | 
				
			||||||
		<a href="/">%fa:check%完了</a>
 | 
					 | 
				
			||||||
		<div>
 | 
					 | 
				
			||||||
			<div class="adder">
 | 
					 | 
				
			||||||
				<p>ウィジェットを追加:</p>
 | 
					 | 
				
			||||||
				<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>
 | 
					 | 
				
			||||||
					<option value="photo-stream">フォトストリーム</option>
 | 
					 | 
				
			||||||
					<option value="slideshow">スライドショー</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="post-form">投稿フォーム</option>
 | 
					 | 
				
			||||||
					<option value="messaging">メッセージ</option>
 | 
					 | 
				
			||||||
					<option value="channel">チャンネル</option>
 | 
					 | 
				
			||||||
					<option value="access-log">アクセスログ</option>
 | 
					 | 
				
			||||||
					<option value="server">サーバー情報</option>
 | 
					 | 
				
			||||||
					<option value="donation">寄付のお願い</option>
 | 
					 | 
				
			||||||
					<option value="nav">ナビゲーション</option>
 | 
					 | 
				
			||||||
					<option value="tips">ヒント</option>
 | 
					 | 
				
			||||||
				</select>
 | 
					 | 
				
			||||||
				<button @click="addWidget">追加</button>
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
			<div class="trash">
 | 
					 | 
				
			||||||
				<div ref="trash"></div>
 | 
					 | 
				
			||||||
				<p>ゴミ箱</p>
 | 
					 | 
				
			||||||
			</div>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
	<div class="main">
 | 
					 | 
				
			||||||
		<div class="left">
 | 
					 | 
				
			||||||
			<div ref="left" data-place="left"></div>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
		<main ref="main">
 | 
					 | 
				
			||||||
			<div class="maintop" ref="maintop" data-place="main" v-if="opts.customize"></div>
 | 
					 | 
				
			||||||
			<mk-timeline-home-widget ref="tl" v-if="mode == 'timeline'"/>
 | 
					 | 
				
			||||||
			<mk-mentions-home-widget ref="tl" v-if="mode == 'mentions'"/>
 | 
					 | 
				
			||||||
		</main>
 | 
					 | 
				
			||||||
		<div class="right">
 | 
					 | 
				
			||||||
			<div ref="right" data-place="right"></div>
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
	</div>
 | 
					 | 
				
			||||||
	<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
		:scope
 | 
					 | 
				
			||||||
			display block
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			&[data-customize]
 | 
					 | 
				
			||||||
				padding-top 48px
 | 
					 | 
				
			||||||
				background-image url('/assets/desktop/grid.svg')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> .main > main > *:not(.maintop)
 | 
					 | 
				
			||||||
					cursor not-allowed
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> *
 | 
					 | 
				
			||||||
						pointer-events none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			&:not([data-customize])
 | 
					 | 
				
			||||||
				> .main > *:empty
 | 
					 | 
				
			||||||
					display none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> .customize
 | 
					 | 
				
			||||||
				position fixed
 | 
					 | 
				
			||||||
				z-index 1000
 | 
					 | 
				
			||||||
				top 0
 | 
					 | 
				
			||||||
				left 0
 | 
					 | 
				
			||||||
				width 100%
 | 
					 | 
				
			||||||
				height 48px
 | 
					 | 
				
			||||||
				background #f7f7f7
 | 
					 | 
				
			||||||
				box-shadow 0 1px 1px rgba(0, 0, 0, 0.075)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> a
 | 
					 | 
				
			||||||
					display block
 | 
					 | 
				
			||||||
					position absolute
 | 
					 | 
				
			||||||
					z-index 1001
 | 
					 | 
				
			||||||
					top 0
 | 
					 | 
				
			||||||
					right 0
 | 
					 | 
				
			||||||
					padding 0 16px
 | 
					 | 
				
			||||||
					line-height 48px
 | 
					 | 
				
			||||||
					text-decoration none
 | 
					 | 
				
			||||||
					color $theme-color-foreground
 | 
					 | 
				
			||||||
					background $theme-color
 | 
					 | 
				
			||||||
					transition background 0.1s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:hover
 | 
					 | 
				
			||||||
						background lighten($theme-color, 10%)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					&:active
 | 
					 | 
				
			||||||
						background darken($theme-color, 10%)
 | 
					 | 
				
			||||||
						transition background 0s ease
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> [data-fa]
 | 
					 | 
				
			||||||
						margin-right 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> div
 | 
					 | 
				
			||||||
					display flex
 | 
					 | 
				
			||||||
					margin 0 auto
 | 
					 | 
				
			||||||
					max-width 1200px - 32px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> div
 | 
					 | 
				
			||||||
						width 50%
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						&.adder
 | 
					 | 
				
			||||||
							> p
 | 
					 | 
				
			||||||
								display inline
 | 
					 | 
				
			||||||
								line-height 48px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						&.trash
 | 
					 | 
				
			||||||
							border-left solid 1px #ddd
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							> div
 | 
					 | 
				
			||||||
								width 100%
 | 
					 | 
				
			||||||
								height 100%
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							> p
 | 
					 | 
				
			||||||
								position absolute
 | 
					 | 
				
			||||||
								top 0
 | 
					 | 
				
			||||||
								left 0
 | 
					 | 
				
			||||||
								width 100%
 | 
					 | 
				
			||||||
								line-height 48px
 | 
					 | 
				
			||||||
								margin 0
 | 
					 | 
				
			||||||
								text-align center
 | 
					 | 
				
			||||||
								pointer-events none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> .main
 | 
					 | 
				
			||||||
				display flex
 | 
					 | 
				
			||||||
				justify-content center
 | 
					 | 
				
			||||||
				margin 0 auto
 | 
					 | 
				
			||||||
				max-width 1200px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> *
 | 
					 | 
				
			||||||
					.customize-container
 | 
					 | 
				
			||||||
						cursor move
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						> *
 | 
					 | 
				
			||||||
							pointer-events none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> main
 | 
					 | 
				
			||||||
					padding 16px
 | 
					 | 
				
			||||||
					width calc(100% - 275px * 2)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> *:not(.maintop):not(:last-child)
 | 
					 | 
				
			||||||
					> .maintop > *:not(:last-child)
 | 
					 | 
				
			||||||
						margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> .maintop
 | 
					 | 
				
			||||||
						min-height 64px
 | 
					 | 
				
			||||||
						margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> *:not(main)
 | 
					 | 
				
			||||||
					width 275px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> *
 | 
					 | 
				
			||||||
						padding 16px 0 16px 0
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
						> *:not(:last-child)
 | 
					 | 
				
			||||||
							margin-bottom 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> .left
 | 
					 | 
				
			||||||
					padding-left 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> .right
 | 
					 | 
				
			||||||
					padding-right 16px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				@media (max-width 1100px)
 | 
					 | 
				
			||||||
					> *:not(main)
 | 
					 | 
				
			||||||
						display none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
					> main
 | 
					 | 
				
			||||||
						float none
 | 
					 | 
				
			||||||
						width 100%
 | 
					 | 
				
			||||||
						max-width 700px
 | 
					 | 
				
			||||||
						margin 0 auto
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	</style>
 | 
					 | 
				
			||||||
	<script lang="typescript">
 | 
					 | 
				
			||||||
		import uuid from 'uuid';
 | 
					 | 
				
			||||||
		import Sortable from 'sortablejs';
 | 
					 | 
				
			||||||
		import dialog from '../scripts/dialog';
 | 
					 | 
				
			||||||
		import ScrollFollower from '../scripts/scroll-follower';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.mixin('i');
 | 
					 | 
				
			||||||
		this.mixin('api');
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.mode = this.opts.mode || 'timeline';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.home = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.bakeHomeData = () => JSON.stringify(this.I.client_settings.home);
 | 
					 | 
				
			||||||
		this.bakedHomeData = this.bakeHomeData();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.on('mount', () => {
 | 
					 | 
				
			||||||
			this.$refs.tl.on('loaded', () => {
 | 
					 | 
				
			||||||
				this.trigger('loaded');
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.I.on('refreshed', this.onMeRefreshed);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.I.client_settings.home.forEach(widget => {
 | 
					 | 
				
			||||||
				try {
 | 
					 | 
				
			||||||
					this.setWidget(widget);
 | 
					 | 
				
			||||||
				} catch (e) {
 | 
					 | 
				
			||||||
					// noop
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!this.opts.customize) {
 | 
					 | 
				
			||||||
				if (this.$refs.left.children.length == 0) {
 | 
					 | 
				
			||||||
					this.$refs.left.parentNode.removeChild(this.$refs.left);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if (this.$refs.right.children.length == 0) {
 | 
					 | 
				
			||||||
					this.$refs.right.parentNode.removeChild(this.$refs.right);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (this.opts.customize) {
 | 
					 | 
				
			||||||
				dialog('%fa:info-circle%カスタマイズのヒント',
 | 
					 | 
				
			||||||
					'<p>ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。</p>' +
 | 
					 | 
				
			||||||
					'<p>一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。</p>' +
 | 
					 | 
				
			||||||
					'<p>ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。</p>' +
 | 
					 | 
				
			||||||
					'<p>カスタマイズを終了するには、右上の「完了」をクリックします。</p>',
 | 
					 | 
				
			||||||
				[{
 | 
					 | 
				
			||||||
					text: 'Got it!'
 | 
					 | 
				
			||||||
				}]);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				const sortableOption = {
 | 
					 | 
				
			||||||
					group: 'kyoppie',
 | 
					 | 
				
			||||||
					animation: 150,
 | 
					 | 
				
			||||||
					onMove: evt => {
 | 
					 | 
				
			||||||
						const id = evt.dragged.getAttribute('data-widget-id');
 | 
					 | 
				
			||||||
						this.home.find(tag => tag.id == id).update({ place: evt.to.getAttribute('data-place') });
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					onSort: () => {
 | 
					 | 
				
			||||||
						this.saveHome();
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				new Sortable(this.$refs.left, sortableOption);
 | 
					 | 
				
			||||||
				new Sortable(this.$refs.right, sortableOption);
 | 
					 | 
				
			||||||
				new Sortable(this.$refs.maintop, 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();
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
				}));
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!this.opts.customize) {
 | 
					 | 
				
			||||||
				this.scrollFollowerLeft = this.$refs.left.parentNode ? new ScrollFollower(this.$refs.left, this.root.getBoundingClientRect().top) : null;
 | 
					 | 
				
			||||||
				this.scrollFollowerRight = this.$refs.right.parentNode ? new ScrollFollower(this.$refs.right, this.root.getBoundingClientRect().top) : null;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.on('unmount', () => {
 | 
					 | 
				
			||||||
			this.I.off('refreshed', this.onMeRefreshed);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.home.forEach(widget => {
 | 
					 | 
				
			||||||
				widget.unmount();
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (!this.opts.customize) {
 | 
					 | 
				
			||||||
				if (this.scrollFollowerLeft) this.scrollFollowerLeft.dispose();
 | 
					 | 
				
			||||||
				if (this.scrollFollowerRight) this.scrollFollowerRight.dispose();
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.onMeRefreshed = () => {
 | 
					 | 
				
			||||||
			if (this.bakedHomeData != this.bakeHomeData()) {
 | 
					 | 
				
			||||||
				alert('別の場所でホームが編集されました。ページを再度読み込みすると編集が反映されます。');
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		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;
 | 
					 | 
				
			||||||
				case 'main':
 | 
					 | 
				
			||||||
					if (this.opts.customize) {
 | 
					 | 
				
			||||||
						this.$refs.maintop.appendChild(actualEl);
 | 
					 | 
				
			||||||
					} else {
 | 
					 | 
				
			||||||
						this.$refs.main.insertBefore(actualEl, this.$refs.tl.root);
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					break;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			const tag = riot.mount(el, {
 | 
					 | 
				
			||||||
				id: widget.id,
 | 
					 | 
				
			||||||
				data: widget.data,
 | 
					 | 
				
			||||||
				place: widget.place,
 | 
					 | 
				
			||||||
				tl: this.$refs.tl
 | 
					 | 
				
			||||||
			})[0];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.home.push(tag);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			if (this.opts.customize) {
 | 
					 | 
				
			||||||
				actualEl.oncontextmenu = e => {
 | 
					 | 
				
			||||||
					e.preventDefault();
 | 
					 | 
				
			||||||
					e.stopImmediatePropagation();
 | 
					 | 
				
			||||||
					if (tag.func) tag.func();
 | 
					 | 
				
			||||||
					return false;
 | 
					 | 
				
			||||||
				};
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		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);
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			Array.from(this.$refs.maintop.children).forEach(el => {
 | 
					 | 
				
			||||||
				const id = el.getAttribute('data-widget-id');
 | 
					 | 
				
			||||||
				const widget = this.I.client_settings.home.find(w => w.id == id);
 | 
					 | 
				
			||||||
				widget.place = 'main';
 | 
					 | 
				
			||||||
				data.push(widget);
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			this.api('i/update_home', {
 | 
					 | 
				
			||||||
				home: data
 | 
					 | 
				
			||||||
			}).then(() => {
 | 
					 | 
				
			||||||
				this.I.update();
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	</script>
 | 
					 | 
				
			||||||
</mk-home>
 | 
					 | 
				
			||||||
							
								
								
									
										392
									
								
								src/web/app/desktop/tags/home.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								src/web/app/desktop/tags/home.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,392 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
						<div :data-customize="customize">
 | 
				
			||||||
 | 
							<div class="customize" v-if="customize">
 | 
				
			||||||
 | 
								<a href="/">%fa:check%完了</a>
 | 
				
			||||||
 | 
								<div>
 | 
				
			||||||
 | 
									<div class="adder">
 | 
				
			||||||
 | 
										<p>ウィジェットを追加:</p>
 | 
				
			||||||
 | 
										<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>
 | 
				
			||||||
 | 
											<option value="photo-stream">フォトストリーム</option>
 | 
				
			||||||
 | 
											<option value="slideshow">スライドショー</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="post-form">投稿フォーム</option>
 | 
				
			||||||
 | 
											<option value="messaging">メッセージ</option>
 | 
				
			||||||
 | 
											<option value="channel">チャンネル</option>
 | 
				
			||||||
 | 
											<option value="access-log">アクセスログ</option>
 | 
				
			||||||
 | 
											<option value="server">サーバー情報</option>
 | 
				
			||||||
 | 
											<option value="donation">寄付のお願い</option>
 | 
				
			||||||
 | 
											<option value="nav">ナビゲーション</option>
 | 
				
			||||||
 | 
											<option value="tips">ヒント</option>
 | 
				
			||||||
 | 
										</select>
 | 
				
			||||||
 | 
										<button @click="addWidget">追加</button>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
									<div class="trash">
 | 
				
			||||||
 | 
										<div ref="trash"></div>
 | 
				
			||||||
 | 
										<p>ゴミ箱</p>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
							<div class="main">
 | 
				
			||||||
 | 
								<div class="left">
 | 
				
			||||||
 | 
									<div ref="left" data-place="left"></div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
								<main ref="main">
 | 
				
			||||||
 | 
									<div class="maintop" ref="maintop" data-place="main" v-if="customize"></div>
 | 
				
			||||||
 | 
									<mk-timeline-home-widget ref="tl" v-if="mode == 'timeline'"/>
 | 
				
			||||||
 | 
									<mk-mentions-home-widget ref="tl" v-if="mode == 'mentions'"/>
 | 
				
			||||||
 | 
								</main>
 | 
				
			||||||
 | 
								<div class="right">
 | 
				
			||||||
 | 
									<div ref="right" data-place="right"></div>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>	
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
						:scope
 | 
				
			||||||
 | 
							display block
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&[data-customize]
 | 
				
			||||||
 | 
								padding-top 48px
 | 
				
			||||||
 | 
								background-image url('/assets/desktop/grid.svg')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> .main > main > *:not(.maintop)
 | 
				
			||||||
 | 
									cursor not-allowed
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> *
 | 
				
			||||||
 | 
										pointer-events none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							&:not([data-customize])
 | 
				
			||||||
 | 
								> .main > *:empty
 | 
				
			||||||
 | 
									display none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> .customize
 | 
				
			||||||
 | 
								position fixed
 | 
				
			||||||
 | 
								z-index 1000
 | 
				
			||||||
 | 
								top 0
 | 
				
			||||||
 | 
								left 0
 | 
				
			||||||
 | 
								width 100%
 | 
				
			||||||
 | 
								height 48px
 | 
				
			||||||
 | 
								background #f7f7f7
 | 
				
			||||||
 | 
								box-shadow 0 1px 1px rgba(0, 0, 0, 0.075)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> a
 | 
				
			||||||
 | 
									display block
 | 
				
			||||||
 | 
									position absolute
 | 
				
			||||||
 | 
									z-index 1001
 | 
				
			||||||
 | 
									top 0
 | 
				
			||||||
 | 
									right 0
 | 
				
			||||||
 | 
									padding 0 16px
 | 
				
			||||||
 | 
									line-height 48px
 | 
				
			||||||
 | 
									text-decoration none
 | 
				
			||||||
 | 
									color $theme-color-foreground
 | 
				
			||||||
 | 
									background $theme-color
 | 
				
			||||||
 | 
									transition background 0.1s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:hover
 | 
				
			||||||
 | 
										background lighten($theme-color, 10%)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									&:active
 | 
				
			||||||
 | 
										background darken($theme-color, 10%)
 | 
				
			||||||
 | 
										transition background 0s ease
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> [data-fa]
 | 
				
			||||||
 | 
										margin-right 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> div
 | 
				
			||||||
 | 
									display flex
 | 
				
			||||||
 | 
									margin 0 auto
 | 
				
			||||||
 | 
									max-width 1200px - 32px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> div
 | 
				
			||||||
 | 
										width 50%
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										&.adder
 | 
				
			||||||
 | 
											> p
 | 
				
			||||||
 | 
												display inline
 | 
				
			||||||
 | 
												line-height 48px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										&.trash
 | 
				
			||||||
 | 
											border-left solid 1px #ddd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											> div
 | 
				
			||||||
 | 
												width 100%
 | 
				
			||||||
 | 
												height 100%
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
											> p
 | 
				
			||||||
 | 
												position absolute
 | 
				
			||||||
 | 
												top 0
 | 
				
			||||||
 | 
												left 0
 | 
				
			||||||
 | 
												width 100%
 | 
				
			||||||
 | 
												line-height 48px
 | 
				
			||||||
 | 
												margin 0
 | 
				
			||||||
 | 
												text-align center
 | 
				
			||||||
 | 
												pointer-events none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> .main
 | 
				
			||||||
 | 
								display flex
 | 
				
			||||||
 | 
								justify-content center
 | 
				
			||||||
 | 
								margin 0 auto
 | 
				
			||||||
 | 
								max-width 1200px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> *
 | 
				
			||||||
 | 
									.customize-container
 | 
				
			||||||
 | 
										cursor move
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										> *
 | 
				
			||||||
 | 
											pointer-events none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> main
 | 
				
			||||||
 | 
									padding 16px
 | 
				
			||||||
 | 
									width calc(100% - 275px * 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> *:not(.maintop):not(:last-child)
 | 
				
			||||||
 | 
									> .maintop > *:not(:last-child)
 | 
				
			||||||
 | 
										margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> .maintop
 | 
				
			||||||
 | 
										min-height 64px
 | 
				
			||||||
 | 
										margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> *:not(main)
 | 
				
			||||||
 | 
									width 275px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> *
 | 
				
			||||||
 | 
										padding 16px 0 16px 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										> *:not(:last-child)
 | 
				
			||||||
 | 
											margin-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> .left
 | 
				
			||||||
 | 
									padding-left 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> .right
 | 
				
			||||||
 | 
									padding-right 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								@media (max-width 1100px)
 | 
				
			||||||
 | 
									> *:not(main)
 | 
				
			||||||
 | 
										display none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									> main
 | 
				
			||||||
 | 
										float none
 | 
				
			||||||
 | 
										width 100%
 | 
				
			||||||
 | 
										max-width 700px
 | 
				
			||||||
 | 
										margin 0 auto
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="typescript">
 | 
				
			||||||
 | 
						import uuid from 'uuid';
 | 
				
			||||||
 | 
						import Sortable from 'sortablejs';
 | 
				
			||||||
 | 
						import dialog from '../scripts/dialog';
 | 
				
			||||||
 | 
						import ScrollFollower from '../scripts/scroll-follower';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.mixin('i');
 | 
				
			||||||
 | 
						this.mixin('api');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.mode = this.opts.mode || 'timeline';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.home = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.bakeHomeData = () => JSON.stringify(this.I.client_settings.home);
 | 
				
			||||||
 | 
						this.bakedHomeData = this.bakeHomeData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.on('mount', () => {
 | 
				
			||||||
 | 
							this.$refs.tl.on('loaded', () => {
 | 
				
			||||||
 | 
								this.trigger('loaded');
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.I.on('refreshed', this.onMeRefreshed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.I.client_settings.home.forEach(widget => {
 | 
				
			||||||
 | 
								try {
 | 
				
			||||||
 | 
									this.setWidget(widget);
 | 
				
			||||||
 | 
								} catch (e) {
 | 
				
			||||||
 | 
									// noop
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!this.opts.customize) {
 | 
				
			||||||
 | 
								if (this.$refs.left.children.length == 0) {
 | 
				
			||||||
 | 
									this.$refs.left.parentNode.removeChild(this.$refs.left);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if (this.$refs.right.children.length == 0) {
 | 
				
			||||||
 | 
									this.$refs.right.parentNode.removeChild(this.$refs.right);
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this.opts.customize) {
 | 
				
			||||||
 | 
								dialog('%fa:info-circle%カスタマイズのヒント',
 | 
				
			||||||
 | 
									'<p>ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。</p>' +
 | 
				
			||||||
 | 
									'<p>一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。</p>' +
 | 
				
			||||||
 | 
									'<p>ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。</p>' +
 | 
				
			||||||
 | 
									'<p>カスタマイズを終了するには、右上の「完了」をクリックします。</p>',
 | 
				
			||||||
 | 
								[{
 | 
				
			||||||
 | 
									text: 'Got it!'
 | 
				
			||||||
 | 
								}]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								const sortableOption = {
 | 
				
			||||||
 | 
									group: 'kyoppie',
 | 
				
			||||||
 | 
									animation: 150,
 | 
				
			||||||
 | 
									onMove: evt => {
 | 
				
			||||||
 | 
										const id = evt.dragged.getAttribute('data-widget-id');
 | 
				
			||||||
 | 
										this.home.find(tag => tag.id == id).update({ place: evt.to.getAttribute('data-place') });
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									onSort: () => {
 | 
				
			||||||
 | 
										this.saveHome();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								new Sortable(this.$refs.left, sortableOption);
 | 
				
			||||||
 | 
								new Sortable(this.$refs.right, sortableOption);
 | 
				
			||||||
 | 
								new Sortable(this.$refs.maintop, 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();
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}));
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!this.opts.customize) {
 | 
				
			||||||
 | 
								this.scrollFollowerLeft = this.$refs.left.parentNode ? new ScrollFollower(this.$refs.left, this.root.getBoundingClientRect().top) : null;
 | 
				
			||||||
 | 
								this.scrollFollowerRight = this.$refs.right.parentNode ? new ScrollFollower(this.$refs.right, this.root.getBoundingClientRect().top) : null;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.on('unmount', () => {
 | 
				
			||||||
 | 
							this.I.off('refreshed', this.onMeRefreshed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.home.forEach(widget => {
 | 
				
			||||||
 | 
								widget.unmount();
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!this.opts.customize) {
 | 
				
			||||||
 | 
								if (this.scrollFollowerLeft) this.scrollFollowerLeft.dispose();
 | 
				
			||||||
 | 
								if (this.scrollFollowerRight) this.scrollFollowerRight.dispose();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						this.onMeRefreshed = () => {
 | 
				
			||||||
 | 
							if (this.bakedHomeData != this.bakeHomeData()) {
 | 
				
			||||||
 | 
								alert('別の場所でホームが編集されました。ページを再度読み込みすると編集が反映されます。');
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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;
 | 
				
			||||||
 | 
								case 'main':
 | 
				
			||||||
 | 
									if (this.opts.customize) {
 | 
				
			||||||
 | 
										this.$refs.maintop.appendChild(actualEl);
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										this.$refs.main.insertBefore(actualEl, this.$refs.tl.root);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							const tag = riot.mount(el, {
 | 
				
			||||||
 | 
								id: widget.id,
 | 
				
			||||||
 | 
								data: widget.data,
 | 
				
			||||||
 | 
								place: widget.place,
 | 
				
			||||||
 | 
								tl: this.$refs.tl
 | 
				
			||||||
 | 
							})[0];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.home.push(tag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (this.opts.customize) {
 | 
				
			||||||
 | 
								actualEl.oncontextmenu = e => {
 | 
				
			||||||
 | 
									e.preventDefault();
 | 
				
			||||||
 | 
									e.stopImmediatePropagation();
 | 
				
			||||||
 | 
									if (tag.func) tag.func();
 | 
				
			||||||
 | 
									return false;
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							Array.from(this.$refs.maintop.children).forEach(el => {
 | 
				
			||||||
 | 
								const id = el.getAttribute('data-widget-id');
 | 
				
			||||||
 | 
								const widget = this.I.client_settings.home.find(w => w.id == id);
 | 
				
			||||||
 | 
								widget.place = 'main';
 | 
				
			||||||
 | 
								data.push(widget);
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.api('i/update_home', {
 | 
				
			||||||
 | 
								home: data
 | 
				
			||||||
 | 
							}).then(() => {
 | 
				
			||||||
 | 
								this.I.update();
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue