wip
This commit is contained in:
		
							parent
							
								
									bc8a0083e2
								
							
						
					
					
						commit
						6ab0c386cb
					
				
					 6 changed files with 432 additions and 326 deletions
				
			
		| 
						 | 
					@ -15,6 +15,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import getNoteSummary from '../../../../../renderers/get-note-summary';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const fetchLimit = 10;
 | 
					const fetchLimit = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +34,7 @@ export default Vue.extend({
 | 
				
			||||||
			existMore: false,
 | 
								existMore: false,
 | 
				
			||||||
			connection: null,
 | 
								connection: null,
 | 
				
			||||||
			connectionId: null,
 | 
								connectionId: null,
 | 
				
			||||||
 | 
								unreadCount: 0,
 | 
				
			||||||
			date: null
 | 
								date: null
 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					@ -74,6 +76,7 @@ export default Vue.extend({
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		document.addEventListener('keydown', this.onKeydown);
 | 
							document.addEventListener('keydown', this.onKeydown);
 | 
				
			||||||
 | 
							document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.fetch();
 | 
							this.fetch();
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					@ -87,10 +90,11 @@ export default Vue.extend({
 | 
				
			||||||
		this.stream.dispose(this.connectionId);
 | 
							this.stream.dispose(this.connectionId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		document.removeEventListener('keydown', this.onKeydown);
 | 
							document.removeEventListener('keydown', this.onKeydown);
 | 
				
			||||||
 | 
							document.removeEventListener('visibilitychange', this.onVisibilitychange);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		fetch(cb?) {
 | 
							fetch() {
 | 
				
			||||||
			this.fetching = true;
 | 
								this.fetching = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
								(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
				
			||||||
| 
						 | 
					@ -107,7 +111,6 @@ export default Vue.extend({
 | 
				
			||||||
					res(notes);
 | 
										res(notes);
 | 
				
			||||||
					this.fetching = false;
 | 
										this.fetching = false;
 | 
				
			||||||
					this.$emit('loaded');
 | 
										this.$emit('loaded');
 | 
				
			||||||
					if (cb) cb();
 | 
					 | 
				
			||||||
				}, rej);
 | 
									}, rej);
 | 
				
			||||||
			}));
 | 
								}));
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
| 
						 | 
					@ -134,6 +137,11 @@ export default Vue.extend({
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		onNote(note) {
 | 
							onNote(note) {
 | 
				
			||||||
 | 
								if (document.hidden && note.userId !== (this as any).os.i.id) {
 | 
				
			||||||
 | 
									this.unreadCount++;
 | 
				
			||||||
 | 
									document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Prepend a note
 | 
								// Prepend a note
 | 
				
			||||||
			(this.$refs.timeline as any).prepend(note);
 | 
								(this.$refs.timeline as any).prepend(note);
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
| 
						 | 
					@ -151,13 +159,20 @@ export default Vue.extend({
 | 
				
			||||||
			this.fetch();
 | 
								this.fetch();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onVisibilitychange() {
 | 
				
			||||||
 | 
								if (!document.hidden) {
 | 
				
			||||||
 | 
									this.unreadCount = 0;
 | 
				
			||||||
 | 
									document.title = 'Misskey';
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		onKeydown(e) {
 | 
							onKeydown(e) {
 | 
				
			||||||
			if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
 | 
								if (e.target.tagName != 'INPUT' && e.target.tagName != 'TEXTAREA') {
 | 
				
			||||||
				if (e.which == 84) { // t
 | 
									if (e.which == 84) { // t
 | 
				
			||||||
					this.focus();
 | 
										this.focus();
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,6 @@
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import ui from './ui.vue';
 | 
					import ui from './ui.vue';
 | 
				
			||||||
import timeline from './timeline.vue';
 | 
					 | 
				
			||||||
import note from './note.vue';
 | 
					import note from './note.vue';
 | 
				
			||||||
import notes from './notes.vue';
 | 
					import notes from './notes.vue';
 | 
				
			||||||
import mediaImage from './media-image.vue';
 | 
					import mediaImage from './media-image.vue';
 | 
				
			||||||
| 
						 | 
					@ -24,7 +23,6 @@ import activity from './activity.vue';
 | 
				
			||||||
import widgetContainer from './widget-container.vue';
 | 
					import widgetContainer from './widget-container.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vue.component('mk-ui', ui);
 | 
					Vue.component('mk-ui', ui);
 | 
				
			||||||
Vue.component('mk-timeline', timeline);
 | 
					 | 
				
			||||||
Vue.component('mk-note', note);
 | 
					Vue.component('mk-note', note);
 | 
				
			||||||
Vue.component('mk-notes', notes);
 | 
					Vue.component('mk-notes', notes);
 | 
				
			||||||
Vue.component('mk-media-image', mediaImage);
 | 
					Vue.component('mk-media-image', mediaImage);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,119 +0,0 @@
 | 
				
			||||||
<template>
 | 
					 | 
				
			||||||
<div class="mk-timeline">
 | 
					 | 
				
			||||||
	<mk-friends-maker v-if="alone"/>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	<mk-notes ref="timeline" :more="existMore ? more : null">
 | 
					 | 
				
			||||||
		<div slot="empty">
 | 
					 | 
				
			||||||
			%fa:R comments%
 | 
					 | 
				
			||||||
			%i18n:@empty%
 | 
					 | 
				
			||||||
		</div>
 | 
					 | 
				
			||||||
	</mk-notes>
 | 
					 | 
				
			||||||
</div>
 | 
					 | 
				
			||||||
</template>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<script lang="ts">
 | 
					 | 
				
			||||||
import Vue from 'vue';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const fetchLimit = 10;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
export default Vue.extend({
 | 
					 | 
				
			||||||
	props: {
 | 
					 | 
				
			||||||
		date: {
 | 
					 | 
				
			||||||
			type: Date,
 | 
					 | 
				
			||||||
			required: false,
 | 
					 | 
				
			||||||
			default: null
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	data() {
 | 
					 | 
				
			||||||
		return {
 | 
					 | 
				
			||||||
			fetching: true,
 | 
					 | 
				
			||||||
			moreFetching: false,
 | 
					 | 
				
			||||||
			existMore: false,
 | 
					 | 
				
			||||||
			connection: null,
 | 
					 | 
				
			||||||
			connectionId: null
 | 
					 | 
				
			||||||
		};
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	computed: {
 | 
					 | 
				
			||||||
		alone(): boolean {
 | 
					 | 
				
			||||||
			return (this as any).os.i.followingCount == 0;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	mounted() {
 | 
					 | 
				
			||||||
		this.connection = (this as any).os.stream.getConnection();
 | 
					 | 
				
			||||||
		this.connectionId = (this as any).os.stream.use();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.connection.on('note', this.onNote);
 | 
					 | 
				
			||||||
		this.connection.on('follow', this.onChangeFollowing);
 | 
					 | 
				
			||||||
		this.connection.on('unfollow', this.onChangeFollowing);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.fetch();
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	beforeDestroy() {
 | 
					 | 
				
			||||||
		this.connection.off('note', this.onNote);
 | 
					 | 
				
			||||||
		this.connection.off('follow', this.onChangeFollowing);
 | 
					 | 
				
			||||||
		this.connection.off('unfollow', this.onChangeFollowing);
 | 
					 | 
				
			||||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	methods: {
 | 
					 | 
				
			||||||
		fetch(cb?) {
 | 
					 | 
				
			||||||
			this.fetching = true;
 | 
					 | 
				
			||||||
			(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
					 | 
				
			||||||
				(this as any).api('notes/timeline', {
 | 
					 | 
				
			||||||
					limit: fetchLimit + 1,
 | 
					 | 
				
			||||||
					includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
 | 
					 | 
				
			||||||
					includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
 | 
					 | 
				
			||||||
				}).then(notes => {
 | 
					 | 
				
			||||||
					if (notes.length == fetchLimit + 1) {
 | 
					 | 
				
			||||||
						notes.pop();
 | 
					 | 
				
			||||||
						this.existMore = true;
 | 
					 | 
				
			||||||
					}
 | 
					 | 
				
			||||||
					res(notes);
 | 
					 | 
				
			||||||
					this.fetching = false;
 | 
					 | 
				
			||||||
					this.$emit('loaded');
 | 
					 | 
				
			||||||
					if (cb) cb();
 | 
					 | 
				
			||||||
				}, rej);
 | 
					 | 
				
			||||||
			}));
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		more() {
 | 
					 | 
				
			||||||
			this.moreFetching = true;
 | 
					 | 
				
			||||||
			(this as any).api('notes/timeline', {
 | 
					 | 
				
			||||||
				limit: fetchLimit + 1,
 | 
					 | 
				
			||||||
				untilId: (this.$refs.timeline as any).tail().id,
 | 
					 | 
				
			||||||
				includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
 | 
					 | 
				
			||||||
				includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
 | 
					 | 
				
			||||||
			}).then(notes => {
 | 
					 | 
				
			||||||
				if (notes.length == fetchLimit + 1) {
 | 
					 | 
				
			||||||
					notes.pop();
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					this.existMore = false;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				notes.forEach(n => (this.$refs.timeline as any).append(n));
 | 
					 | 
				
			||||||
				this.moreFetching = false;
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		onNote(note) {
 | 
					 | 
				
			||||||
			// Prepend a note
 | 
					 | 
				
			||||||
			(this.$refs.timeline as any).prepend(note);
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		onChangeFollowing() {
 | 
					 | 
				
			||||||
			this.fetch();
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
</script>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					 | 
				
			||||||
@import '~const.styl'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.mk-timeline
 | 
					 | 
				
			||||||
	> .mk-friends-maker
 | 
					 | 
				
			||||||
		margin-bottom 8px
 | 
					 | 
				
			||||||
</style>
 | 
					 | 
				
			||||||
							
								
								
									
										196
									
								
								src/client/app/mobile/views/pages/dashboard.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								src/client/app/mobile/views/pages/dashboard.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,196 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<mk-ui>
 | 
				
			||||||
 | 
						<span slot="header">%fa:home%ダッシュボード</span>
 | 
				
			||||||
 | 
						<template slot="func">
 | 
				
			||||||
 | 
							<button @click="customizing = !customizing">%fa:cog%</button>
 | 
				
			||||||
 | 
						</template>
 | 
				
			||||||
 | 
						<main>
 | 
				
			||||||
 | 
							<template v-if="customizing">
 | 
				
			||||||
 | 
								<header>
 | 
				
			||||||
 | 
									<select v-model="widgetAdderSelected">
 | 
				
			||||||
 | 
										<option value="profile">プロフィール</option>
 | 
				
			||||||
 | 
										<option value="calendar">カレンダー</option>
 | 
				
			||||||
 | 
										<option value="activity">アクティビティ</option>
 | 
				
			||||||
 | 
										<option value="rss">RSSリーダー</option>
 | 
				
			||||||
 | 
										<option value="photo-stream">フォトストリーム</option>
 | 
				
			||||||
 | 
										<option value="slideshow">スライドショー</option>
 | 
				
			||||||
 | 
										<option value="version">バージョン</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>
 | 
				
			||||||
 | 
									<p><a @click="hint">カスタマイズのヒント</a></p>
 | 
				
			||||||
 | 
								</header>
 | 
				
			||||||
 | 
								<x-draggable
 | 
				
			||||||
 | 
									:list="widgets"
 | 
				
			||||||
 | 
									:options="{ handle: '.handle', animation: 150 }"
 | 
				
			||||||
 | 
									@sort="onWidgetSort"
 | 
				
			||||||
 | 
								>
 | 
				
			||||||
 | 
									<div v-for="widget in widgets" class="customize-container" :key="widget.id">
 | 
				
			||||||
 | 
										<header>
 | 
				
			||||||
 | 
											<span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
 | 
				
			||||||
 | 
										</header>
 | 
				
			||||||
 | 
										<div @click="widgetFunc(widget.id)">
 | 
				
			||||||
 | 
											<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" :is-mobile="true"/>
 | 
				
			||||||
 | 
										</div>
 | 
				
			||||||
 | 
									</div>
 | 
				
			||||||
 | 
								</x-draggable>
 | 
				
			||||||
 | 
							</template>
 | 
				
			||||||
 | 
							<template v-else>
 | 
				
			||||||
 | 
								<component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" :is-mobile="true" @chosen="warp"/>
 | 
				
			||||||
 | 
							</template>
 | 
				
			||||||
 | 
						</main>
 | 
				
			||||||
 | 
					</mk-ui>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import * as XDraggable from 'vuedraggable';
 | 
				
			||||||
 | 
					import * as uuid from 'uuid';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						components: {
 | 
				
			||||||
 | 
							XDraggable
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								showNav: false,
 | 
				
			||||||
 | 
								widgets: [],
 | 
				
			||||||
 | 
								customizing: false,
 | 
				
			||||||
 | 
								widgetAdderSelected: null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						created() {
 | 
				
			||||||
 | 
							if ((this as any).os.i.clientSettings.mobileHome == null) {
 | 
				
			||||||
 | 
								Vue.set((this as any).os.i.clientSettings, 'mobileHome', [{
 | 
				
			||||||
 | 
									name: 'calendar',
 | 
				
			||||||
 | 
									id: 'a', data: {}
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									name: 'activity',
 | 
				
			||||||
 | 
									id: 'b', data: {}
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									name: 'rss',
 | 
				
			||||||
 | 
									id: 'c', data: {}
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									name: 'photo-stream',
 | 
				
			||||||
 | 
									id: 'd', data: {}
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									name: 'donation',
 | 
				
			||||||
 | 
									id: 'e', data: {}
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									name: 'nav',
 | 
				
			||||||
 | 
									id: 'f', data: {}
 | 
				
			||||||
 | 
								}, {
 | 
				
			||||||
 | 
									name: 'version',
 | 
				
			||||||
 | 
									id: 'g', data: {}
 | 
				
			||||||
 | 
								}]);
 | 
				
			||||||
 | 
								this.widgets = (this as any).os.i.clientSettings.mobileHome;
 | 
				
			||||||
 | 
								this.saveHome();
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								this.widgets = (this as any).os.i.clientSettings.mobileHome;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.$watch('os.i.clientSettings', i => {
 | 
				
			||||||
 | 
								this.widgets = (this as any).os.i.clientSettings.mobileHome;
 | 
				
			||||||
 | 
							}, {
 | 
				
			||||||
 | 
								deep: true
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							document.title = 'Misskey';
 | 
				
			||||||
 | 
							document.documentElement.style.background = '#313a42';
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							onHomeUpdated(data) {
 | 
				
			||||||
 | 
								if (data.home) {
 | 
				
			||||||
 | 
									(this as any).os.i.clientSettings.mobileHome = data.home;
 | 
				
			||||||
 | 
									this.widgets = data.home;
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									const w = (this as any).os.i.clientSettings.mobileHome.find(w => w.id == data.id);
 | 
				
			||||||
 | 
									if (w != null) {
 | 
				
			||||||
 | 
										w.data = data.data;
 | 
				
			||||||
 | 
										this.$refs[w.id][0].preventSave = true;
 | 
				
			||||||
 | 
										this.$refs[w.id][0].props = w.data;
 | 
				
			||||||
 | 
										this.widgets = (this as any).os.i.clientSettings.mobileHome;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							hint() {
 | 
				
			||||||
 | 
								alert('ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。');
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							widgetFunc(id) {
 | 
				
			||||||
 | 
								const w = this.$refs[id][0];
 | 
				
			||||||
 | 
								if (w.func) w.func();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							onWidgetSort() {
 | 
				
			||||||
 | 
								this.saveHome();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							addWidget() {
 | 
				
			||||||
 | 
								const widget = {
 | 
				
			||||||
 | 
									name: this.widgetAdderSelected,
 | 
				
			||||||
 | 
									id: uuid(),
 | 
				
			||||||
 | 
									data: {}
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.widgets.unshift(widget);
 | 
				
			||||||
 | 
								this.saveHome();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							removeWidget(widget) {
 | 
				
			||||||
 | 
								this.widgets = this.widgets.filter(w => w.id != widget.id);
 | 
				
			||||||
 | 
								this.saveHome();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							saveHome() {
 | 
				
			||||||
 | 
								(this as any).os.i.clientSettings.mobileHome = this.widgets;
 | 
				
			||||||
 | 
								(this as any).api('i/update_mobile_home', {
 | 
				
			||||||
 | 
									home: this.widgets
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
 | 
					main
 | 
				
			||||||
 | 
						margin 0 auto
 | 
				
			||||||
 | 
						max-width 500px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						@media (min-width 500px)
 | 
				
			||||||
 | 
							padding 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> header
 | 
				
			||||||
 | 
							padding 8px
 | 
				
			||||||
 | 
							background #fff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.widget
 | 
				
			||||||
 | 
							margin 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						.customize-container
 | 
				
			||||||
 | 
							margin 8px
 | 
				
			||||||
 | 
							background #fff
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> header
 | 
				
			||||||
 | 
								line-height 32px
 | 
				
			||||||
 | 
								background #eee
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> .handle
 | 
				
			||||||
 | 
									padding 0 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> .remove
 | 
				
			||||||
 | 
									position absolute
 | 
				
			||||||
 | 
									top 0
 | 
				
			||||||
 | 
									right 0
 | 
				
			||||||
 | 
									padding 0 8px
 | 
				
			||||||
 | 
									line-height 32px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> div
 | 
				
			||||||
 | 
								padding 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								> *
 | 
				
			||||||
 | 
									pointer-events none
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										166
									
								
								src/client/app/mobile/views/pages/home.timeline.vue
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								src/client/app/mobile/views/pages/home.timeline.vue
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,166 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					<div>
 | 
				
			||||||
 | 
						<mk-friends-maker v-if="src == 'home' && alone" style="margin-bottom:8px"/>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<mk-notes ref="timeline" :more="existMore ? more : null">
 | 
				
			||||||
 | 
							<div slot="empty">
 | 
				
			||||||
 | 
								%fa:R comments%
 | 
				
			||||||
 | 
								%i18n:@empty%
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
						</mk-notes>
 | 
				
			||||||
 | 
					</div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script lang="ts">
 | 
				
			||||||
 | 
					import Vue from 'vue';
 | 
				
			||||||
 | 
					import getNoteSummary from '../../../../../renderers/get-note-summary';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const fetchLimit = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default Vue.extend({
 | 
				
			||||||
 | 
						props: {
 | 
				
			||||||
 | 
							src: {
 | 
				
			||||||
 | 
								type: String,
 | 
				
			||||||
 | 
								required: true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data() {
 | 
				
			||||||
 | 
							return {
 | 
				
			||||||
 | 
								fetching: true,
 | 
				
			||||||
 | 
								moreFetching: false,
 | 
				
			||||||
 | 
								existMore: false,
 | 
				
			||||||
 | 
								connection: null,
 | 
				
			||||||
 | 
								connectionId: null,
 | 
				
			||||||
 | 
								unreadCount: 0,
 | 
				
			||||||
 | 
								date: null
 | 
				
			||||||
 | 
							};
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						computed: {
 | 
				
			||||||
 | 
							alone(): boolean {
 | 
				
			||||||
 | 
								return (this as any).os.i.followingCount == 0;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							stream(): any {
 | 
				
			||||||
 | 
								return this.src == 'home'
 | 
				
			||||||
 | 
									? (this as any).os.stream
 | 
				
			||||||
 | 
									: this.src == 'local'
 | 
				
			||||||
 | 
										? (this as any).os.streams.localTimelineStream
 | 
				
			||||||
 | 
										: (this as any).os.streams.globalTimelineStream;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							endpoint(): string {
 | 
				
			||||||
 | 
								return this.src == 'home'
 | 
				
			||||||
 | 
									? 'notes/timeline'
 | 
				
			||||||
 | 
									: this.src == 'local'
 | 
				
			||||||
 | 
										? 'notes/local-timeline'
 | 
				
			||||||
 | 
										: 'notes/global-timeline';
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							canFetchMore(): boolean {
 | 
				
			||||||
 | 
								return !this.moreFetching && !this.fetching && this.existMore;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mounted() {
 | 
				
			||||||
 | 
							this.connection = this.stream.getConnection();
 | 
				
			||||||
 | 
							this.connectionId = this.stream.use();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.connection.on('note', this.onNote);
 | 
				
			||||||
 | 
							if (this.src == 'home') {
 | 
				
			||||||
 | 
								this.connection.on('follow', this.onChangeFollowing);
 | 
				
			||||||
 | 
								this.connection.on('unfollow', this.onChangeFollowing);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							this.fetch();
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						beforeDestroy() {
 | 
				
			||||||
 | 
							this.connection.off('note', this.onNote);
 | 
				
			||||||
 | 
							if (this.src == 'home') {
 | 
				
			||||||
 | 
								this.connection.off('follow', this.onChangeFollowing);
 | 
				
			||||||
 | 
								this.connection.off('unfollow', this.onChangeFollowing);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							this.stream.dispose(this.connectionId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							document.removeEventListener('visibilitychange', this.onVisibilitychange);
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						methods: {
 | 
				
			||||||
 | 
							fetch() {
 | 
				
			||||||
 | 
								this.fetching = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								(this.$refs.timeline as any).init(() => new Promise((res, rej) => {
 | 
				
			||||||
 | 
									(this as any).api(this.endpoint, {
 | 
				
			||||||
 | 
										limit: fetchLimit + 1,
 | 
				
			||||||
 | 
										untilDate: this.date ? this.date.getTime() : undefined,
 | 
				
			||||||
 | 
										includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
 | 
				
			||||||
 | 
										includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
 | 
				
			||||||
 | 
									}).then(notes => {
 | 
				
			||||||
 | 
										if (notes.length == fetchLimit + 1) {
 | 
				
			||||||
 | 
											notes.pop();
 | 
				
			||||||
 | 
											this.existMore = true;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										res(notes);
 | 
				
			||||||
 | 
										this.fetching = false;
 | 
				
			||||||
 | 
										this.$emit('loaded');
 | 
				
			||||||
 | 
									}, rej);
 | 
				
			||||||
 | 
								}));
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							more() {
 | 
				
			||||||
 | 
								if (!this.canFetchMore) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								this.moreFetching = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								(this as any).api(this.endpoint, {
 | 
				
			||||||
 | 
									limit: fetchLimit + 1,
 | 
				
			||||||
 | 
									untilId: (this.$refs.timeline as any).tail().id,
 | 
				
			||||||
 | 
									includeMyRenotes: (this as any).os.i.clientSettings.showMyRenotes,
 | 
				
			||||||
 | 
									includeRenotedMyNotes: (this as any).os.i.clientSettings.showRenotedMyNotes
 | 
				
			||||||
 | 
								}).then(notes => {
 | 
				
			||||||
 | 
									if (notes.length == fetchLimit + 1) {
 | 
				
			||||||
 | 
										notes.pop();
 | 
				
			||||||
 | 
									} else {
 | 
				
			||||||
 | 
										this.existMore = false;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									notes.forEach(n => (this.$refs.timeline as any).append(n));
 | 
				
			||||||
 | 
									this.moreFetching = false;
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onNote(note) {
 | 
				
			||||||
 | 
								if (document.hidden && note.userId !== (this as any).os.i.id) {
 | 
				
			||||||
 | 
									this.unreadCount++;
 | 
				
			||||||
 | 
									document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Prepend a note
 | 
				
			||||||
 | 
								(this.$refs.timeline as any).prepend(note);
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onChangeFollowing() {
 | 
				
			||||||
 | 
								this.fetch();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							focus() {
 | 
				
			||||||
 | 
								(this.$refs.timeline as any).focus();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							warp(date) {
 | 
				
			||||||
 | 
								this.date = date;
 | 
				
			||||||
 | 
								this.fetch();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onVisibilitychange() {
 | 
				
			||||||
 | 
								if (!document.hidden) {
 | 
				
			||||||
 | 
									this.unreadCount = 0;
 | 
				
			||||||
 | 
									document.title = 'Misskey';
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					@ -1,59 +1,37 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<mk-ui>
 | 
					<mk-ui>
 | 
				
			||||||
	<span slot="header" @click="showTl = !showTl">
 | 
						<span slot="header" @click="showNav = true">
 | 
				
			||||||
		<template v-if="showTl">%fa:home%%i18n:@timeline%</template>
 | 
							<span>
 | 
				
			||||||
		<template v-else>%fa:home%ウィジェット</template>
 | 
								<span v-if="src == 'home'">%fa:home%ホーム</span>
 | 
				
			||||||
 | 
								<span v-if="src == 'local'">%fa:R comments%ローカル</span>
 | 
				
			||||||
 | 
								<span v-if="src == 'global'">%fa:globe%グローバル</span>
 | 
				
			||||||
 | 
								<span v-if="src == 'list'">%fa:list%{{ list.title }}</span>
 | 
				
			||||||
 | 
							</span>
 | 
				
			||||||
		<span style="margin-left:8px">
 | 
							<span style="margin-left:8px">
 | 
				
			||||||
			<template v-if="showTl">%fa:angle-down%</template>
 | 
								<template v-if="!showNav">%fa:angle-down%</template>
 | 
				
			||||||
			<template v-else>%fa:angle-up%</template>
 | 
								<template v-else>%fa:angle-up%</template>
 | 
				
			||||||
		</span>
 | 
							</span>
 | 
				
			||||||
	</span>
 | 
						</span>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<template slot="func">
 | 
						<template slot="func">
 | 
				
			||||||
		<button @click="fn" v-if="showTl">%fa:pencil-alt%</button>
 | 
							<button @click="fn">%fa:pencil-alt%</button>
 | 
				
			||||||
		<button @click="customizing = !customizing" v-else>%fa:cog%</button>
 | 
					 | 
				
			||||||
	</template>
 | 
						</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<main>
 | 
						<main>
 | 
				
			||||||
 | 
							<div class="nav" v-if="showNav">
 | 
				
			||||||
 | 
								<div class="bg" @click="showNav = false"></div>
 | 
				
			||||||
 | 
								<div class="body">
 | 
				
			||||||
 | 
									<span :data-is-active="src == 'home'" @click="src = 'home'">%fa:home% ホーム</span>
 | 
				
			||||||
 | 
									<span :data-is-active="src == 'local'" @click="src = 'local'">%fa:R comments% ローカル</span>
 | 
				
			||||||
 | 
									<span :data-is-active="src == 'global'" @click="src = 'global'">%fa:globe% グローバル</span>
 | 
				
			||||||
 | 
								</div>
 | 
				
			||||||
 | 
							</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		<div class="tl">
 | 
							<div class="tl">
 | 
				
			||||||
			<mk-timeline @loaded="onLoaded" v-show="showTl"/>
 | 
								<x-tl v-if="src == 'home'" ref="tl" key="home" src="home" @loaded="onLoaded"/>
 | 
				
			||||||
		</div>
 | 
								<x-tl v-if="src == 'local'" ref="tl" key="local" src="local"/>
 | 
				
			||||||
		<div class="widgets" v-show="!showTl">
 | 
								<x-tl v-if="src == 'global'" ref="tl" key="global" src="global"/>
 | 
				
			||||||
			<template v-if="customizing">
 | 
								<mk-user-list-timeline v-if="src == 'list'" ref="tl" key="list" :list="list"/>
 | 
				
			||||||
				<header>
 | 
					 | 
				
			||||||
					<select v-model="widgetAdderSelected">
 | 
					 | 
				
			||||||
						<option value="profile">プロフィール</option>
 | 
					 | 
				
			||||||
						<option value="calendar">カレンダー</option>
 | 
					 | 
				
			||||||
						<option value="activity">アクティビティ</option>
 | 
					 | 
				
			||||||
						<option value="rss">RSSリーダー</option>
 | 
					 | 
				
			||||||
						<option value="photo-stream">フォトストリーム</option>
 | 
					 | 
				
			||||||
						<option value="slideshow">スライドショー</option>
 | 
					 | 
				
			||||||
						<option value="version">バージョン</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>
 | 
					 | 
				
			||||||
					<p><a @click="hint">カスタマイズのヒント</a></p>
 | 
					 | 
				
			||||||
				</header>
 | 
					 | 
				
			||||||
				<x-draggable
 | 
					 | 
				
			||||||
					:list="widgets"
 | 
					 | 
				
			||||||
					:options="{ handle: '.handle', animation: 150 }"
 | 
					 | 
				
			||||||
					@sort="onWidgetSort"
 | 
					 | 
				
			||||||
				>
 | 
					 | 
				
			||||||
					<div v-for="widget in widgets" class="customize-container" :key="widget.id">
 | 
					 | 
				
			||||||
						<header>
 | 
					 | 
				
			||||||
							<span class="handle">%fa:bars%</span>{{ widget.name }}<button class="remove" @click="removeWidget(widget)">%fa:times%</button>
 | 
					 | 
				
			||||||
						</header>
 | 
					 | 
				
			||||||
						<div @click="widgetFunc(widget.id)">
 | 
					 | 
				
			||||||
							<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :is-customize-mode="true" :is-mobile="true"/>
 | 
					 | 
				
			||||||
						</div>
 | 
					 | 
				
			||||||
					</div>
 | 
					 | 
				
			||||||
				</x-draggable>
 | 
					 | 
				
			||||||
			</template>
 | 
					 | 
				
			||||||
			<template v-else>
 | 
					 | 
				
			||||||
				<component class="widget" v-for="widget in widgets" :is="`mkw-${widget.name}`" :key="widget.id" :ref="widget.id" :widget="widget" :is-mobile="true" @chosen="warp"/>
 | 
					 | 
				
			||||||
			</template>
 | 
					 | 
				
			||||||
		</div>
 | 
							</div>
 | 
				
			||||||
	</main>
 | 
						</main>
 | 
				
			||||||
</mk-ui>
 | 
					</mk-ui>
 | 
				
			||||||
| 
						 | 
					@ -61,144 +39,38 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script lang="ts">
 | 
					<script lang="ts">
 | 
				
			||||||
import Vue from 'vue';
 | 
					import Vue from 'vue';
 | 
				
			||||||
import * as XDraggable from 'vuedraggable';
 | 
					 | 
				
			||||||
import * as uuid from 'uuid';
 | 
					 | 
				
			||||||
import Progress from '../../../common/scripts/loading';
 | 
					import Progress from '../../../common/scripts/loading';
 | 
				
			||||||
import getNoteSummary from '../../../../../renderers/get-note-summary';
 | 
					import XTl from './home.timeline.vue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default Vue.extend({
 | 
					export default Vue.extend({
 | 
				
			||||||
	components: {
 | 
						components: {
 | 
				
			||||||
		XDraggable
 | 
							XTl
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	data() {
 | 
						data() {
 | 
				
			||||||
		return {
 | 
							return {
 | 
				
			||||||
			connection: null,
 | 
								src: 'home',
 | 
				
			||||||
			connectionId: null,
 | 
								list: null,
 | 
				
			||||||
			unreadCount: 0,
 | 
								showNav: false
 | 
				
			||||||
			showTl: true,
 | 
					 | 
				
			||||||
			widgets: [],
 | 
					 | 
				
			||||||
			customizing: false,
 | 
					 | 
				
			||||||
			widgetAdderSelected: null
 | 
					 | 
				
			||||||
		};
 | 
							};
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	created() {
 | 
					 | 
				
			||||||
		if ((this as any).os.i.clientSettings.mobileHome == null) {
 | 
					 | 
				
			||||||
			Vue.set((this as any).os.i.clientSettings, 'mobileHome', [{
 | 
					 | 
				
			||||||
				name: 'calendar',
 | 
					 | 
				
			||||||
				id: 'a', data: {}
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				name: 'activity',
 | 
					 | 
				
			||||||
				id: 'b', data: {}
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				name: 'rss',
 | 
					 | 
				
			||||||
				id: 'c', data: {}
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				name: 'photo-stream',
 | 
					 | 
				
			||||||
				id: 'd', data: {}
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				name: 'donation',
 | 
					 | 
				
			||||||
				id: 'e', data: {}
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				name: 'nav',
 | 
					 | 
				
			||||||
				id: 'f', data: {}
 | 
					 | 
				
			||||||
			}, {
 | 
					 | 
				
			||||||
				name: 'version',
 | 
					 | 
				
			||||||
				id: 'g', data: {}
 | 
					 | 
				
			||||||
			}]);
 | 
					 | 
				
			||||||
			this.widgets = (this as any).os.i.clientSettings.mobileHome;
 | 
					 | 
				
			||||||
			this.saveHome();
 | 
					 | 
				
			||||||
		} else {
 | 
					 | 
				
			||||||
			this.widgets = (this as any).os.i.clientSettings.mobileHome;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.$watch('os.i.clientSettings', i => {
 | 
					 | 
				
			||||||
			this.widgets = (this as any).os.i.clientSettings.mobileHome;
 | 
					 | 
				
			||||||
		}, {
 | 
					 | 
				
			||||||
			deep: true
 | 
					 | 
				
			||||||
		});
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		document.title = 'Misskey';
 | 
							document.title = 'Misskey';
 | 
				
			||||||
		document.documentElement.style.background = '#313a42';
 | 
							document.documentElement.style.background = '#313a42';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		this.connection = (this as any).os.stream.getConnection();
 | 
					 | 
				
			||||||
		this.connectionId = (this as any).os.stream.use();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		this.connection.on('note', this.onStreamNote);
 | 
					 | 
				
			||||||
		this.connection.on('mobile_home_updated', this.onHomeUpdated);
 | 
					 | 
				
			||||||
		document.addEventListener('visibilitychange', this.onVisibilitychange, false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		Progress.start();
 | 
							Progress.start();
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
	beforeDestroy() {
 | 
					
 | 
				
			||||||
		this.connection.off('note', this.onStreamNote);
 | 
					 | 
				
			||||||
		this.connection.off('mobile_home_updated', this.onHomeUpdated);
 | 
					 | 
				
			||||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
					 | 
				
			||||||
		document.removeEventListener('visibilitychange', this.onVisibilitychange);
 | 
					 | 
				
			||||||
	},
 | 
					 | 
				
			||||||
	methods: {
 | 
						methods: {
 | 
				
			||||||
		fn() {
 | 
							fn() {
 | 
				
			||||||
			(this as any).apis.post();
 | 
								(this as any).apis.post();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		onLoaded() {
 | 
							onLoaded() {
 | 
				
			||||||
			Progress.done();
 | 
								Progress.done();
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		onStreamNote(note) {
 | 
					 | 
				
			||||||
			if (document.hidden && note.userId !== (this as any).os.i.id) {
 | 
					 | 
				
			||||||
				this.unreadCount++;
 | 
					 | 
				
			||||||
				document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		onVisibilitychange() {
 | 
					 | 
				
			||||||
			if (!document.hidden) {
 | 
					 | 
				
			||||||
				this.unreadCount = 0;
 | 
					 | 
				
			||||||
				document.title = 'Misskey';
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		onHomeUpdated(data) {
 | 
					 | 
				
			||||||
			if (data.home) {
 | 
					 | 
				
			||||||
				(this as any).os.i.clientSettings.mobileHome = data.home;
 | 
					 | 
				
			||||||
				this.widgets = data.home;
 | 
					 | 
				
			||||||
			} else {
 | 
					 | 
				
			||||||
				const w = (this as any).os.i.clientSettings.mobileHome.find(w => w.id == data.id);
 | 
					 | 
				
			||||||
				if (w != null) {
 | 
					 | 
				
			||||||
					w.data = data.data;
 | 
					 | 
				
			||||||
					this.$refs[w.id][0].preventSave = true;
 | 
					 | 
				
			||||||
					this.$refs[w.id][0].props = w.data;
 | 
					 | 
				
			||||||
					this.widgets = (this as any).os.i.clientSettings.mobileHome;
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		hint() {
 | 
					 | 
				
			||||||
			alert('ウィジェットを追加/削除したり並べ替えたりできます。ウィジェットを移動するには「三」をドラッグします。ウィジェットを削除するには「x」をタップします。いくつかのウィジェットはタップすることで表示を変更できます。');
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		widgetFunc(id) {
 | 
					 | 
				
			||||||
			const w = this.$refs[id][0];
 | 
					 | 
				
			||||||
			if (w.func) w.func();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		onWidgetSort() {
 | 
					 | 
				
			||||||
			this.saveHome();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		addWidget() {
 | 
					 | 
				
			||||||
			const widget = {
 | 
					 | 
				
			||||||
				name: this.widgetAdderSelected,
 | 
					 | 
				
			||||||
				id: uuid(),
 | 
					 | 
				
			||||||
				data: {}
 | 
					 | 
				
			||||||
			};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			this.widgets.unshift(widget);
 | 
					 | 
				
			||||||
			this.saveHome();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		removeWidget(widget) {
 | 
					 | 
				
			||||||
			this.widgets = this.widgets.filter(w => w.id != widget.id);
 | 
					 | 
				
			||||||
			this.saveHome();
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		saveHome() {
 | 
					 | 
				
			||||||
			(this as any).os.i.clientSettings.mobileHome = this.widgets;
 | 
					 | 
				
			||||||
			(this as any).api('i/update_mobile_home', {
 | 
					 | 
				
			||||||
				home: this.widgets
 | 
					 | 
				
			||||||
			});
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		warp() {
 | 
							warp() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -208,9 +80,25 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<style lang="stylus" scoped>
 | 
					<style lang="stylus" scoped>
 | 
				
			||||||
main
 | 
					main
 | 
				
			||||||
 | 
						> .nav
 | 
				
			||||||
 | 
							> .bg
 | 
				
			||||||
 | 
								position fixed
 | 
				
			||||||
 | 
								z-index 10000
 | 
				
			||||||
 | 
								top 0
 | 
				
			||||||
 | 
								left 0
 | 
				
			||||||
 | 
								width 100%
 | 
				
			||||||
 | 
								height 100%
 | 
				
			||||||
 | 
								background rgba(#000, 0.5)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							> .body
 | 
				
			||||||
 | 
								position fixed
 | 
				
			||||||
 | 
								z-index 10001
 | 
				
			||||||
 | 
								top 48px
 | 
				
			||||||
 | 
								left 0
 | 
				
			||||||
 | 
								background #fff
 | 
				
			||||||
 | 
								border-radius 8px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .tl
 | 
						> .tl
 | 
				
			||||||
		> .mk-timeline
 | 
					 | 
				
			||||||
		max-width 600px
 | 
							max-width 600px
 | 
				
			||||||
		margin 0 auto
 | 
							margin 0 auto
 | 
				
			||||||
		padding 8px
 | 
							padding 8px
 | 
				
			||||||
| 
						 | 
					@ -218,42 +106,4 @@ main
 | 
				
			||||||
		@media (min-width 500px)
 | 
							@media (min-width 500px)
 | 
				
			||||||
			padding 16px
 | 
								padding 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .widgets
 | 
					 | 
				
			||||||
		margin 0 auto
 | 
					 | 
				
			||||||
		max-width 500px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		@media (min-width 500px)
 | 
					 | 
				
			||||||
			padding 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		> header
 | 
					 | 
				
			||||||
			padding 8px
 | 
					 | 
				
			||||||
			background #fff
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		.widget
 | 
					 | 
				
			||||||
			margin 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		.customize-container
 | 
					 | 
				
			||||||
			margin 8px
 | 
					 | 
				
			||||||
			background #fff
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> header
 | 
					 | 
				
			||||||
				line-height 32px
 | 
					 | 
				
			||||||
				background #eee
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> .handle
 | 
					 | 
				
			||||||
					padding 0 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> .remove
 | 
					 | 
				
			||||||
					position absolute
 | 
					 | 
				
			||||||
					top 0
 | 
					 | 
				
			||||||
					right 0
 | 
					 | 
				
			||||||
					padding 0 8px
 | 
					 | 
				
			||||||
					line-height 32px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			> div
 | 
					 | 
				
			||||||
				padding 8px
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				> *
 | 
					 | 
				
			||||||
					pointer-events none
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue