Implement #1098
This commit is contained in:
		@@ -8,6 +8,10 @@ export default function<T extends object>(data: {
 | 
			
		||||
		props: {
 | 
			
		||||
			widget: {
 | 
			
		||||
				type: Object
 | 
			
		||||
			},
 | 
			
		||||
			isMobile: {
 | 
			
		||||
				type: Boolean,
 | 
			
		||||
				default: false
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		computed: {
 | 
			
		||||
@@ -21,6 +25,7 @@ export default function<T extends object>(data: {
 | 
			
		||||
			};
 | 
			
		||||
		},
 | 
			
		||||
		created() {
 | 
			
		||||
			if (this.widget.data == null) this.widget.data = {};
 | 
			
		||||
			if (this.props) {
 | 
			
		||||
				Object.keys(this.props).forEach(prop => {
 | 
			
		||||
					if (this.widget.data.hasOwnProperty(prop)) {
 | 
			
		||||
@@ -30,12 +35,21 @@ export default function<T extends object>(data: {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			this.$watch('props', newProps => {
 | 
			
		||||
				(this as any).api('i/update_home', {
 | 
			
		||||
					id: this.id,
 | 
			
		||||
					data: newProps
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
					(this as any).os.i.client_settings.home.find(w => w.id == this.id).data = newProps;
 | 
			
		||||
				});
 | 
			
		||||
				if (this.isMobile) {
 | 
			
		||||
					(this as any).api('i/update_mobile_home', {
 | 
			
		||||
						id: this.id,
 | 
			
		||||
						data: newProps
 | 
			
		||||
					}).then(() => {
 | 
			
		||||
						(this as any).os.i.client_settings.mobile_home.find(w => w.id == this.id).data = newProps;
 | 
			
		||||
					});
 | 
			
		||||
				} else {
 | 
			
		||||
					(this as any).api('i/update_home', {
 | 
			
		||||
						id: this.id,
 | 
			
		||||
						data: newProps
 | 
			
		||||
					}).then(() => {
 | 
			
		||||
						(this as any).os.i.client_settings.home.find(w => w.id == this.id).data = newProps;
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			}, {
 | 
			
		||||
				deep: true
 | 
			
		||||
			});
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,9 @@ export default async function(mios: MiOS) {
 | 
			
		||||
 | 
			
		||||
		// Clear cache (serive worker)
 | 
			
		||||
		try {
 | 
			
		||||
			navigator.serviceWorker.controller.postMessage('clear');
 | 
			
		||||
			if (navigator.serviceWorker.controller) {
 | 
			
		||||
				navigator.serviceWorker.controller.postMessage('clear');
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			navigator.serviceWorker.getRegistrations().then(registrations => {
 | 
			
		||||
				registrations.forEach(registration => registration.unregister());
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,21 @@ import urlPreview from './url-preview.vue';
 | 
			
		||||
import twitterSetting from './twitter-setting.vue';
 | 
			
		||||
import fileTypeIcon from './file-type-icon.vue';
 | 
			
		||||
 | 
			
		||||
//#region widgets
 | 
			
		||||
import wAccessLog from './widgets/access-log.vue';
 | 
			
		||||
import wVersion from './widgets/version.vue';
 | 
			
		||||
import wRss from './widgets/rss.vue';
 | 
			
		||||
import wProfile from './widgets/profile.vue';
 | 
			
		||||
import wServer from './widgets/server.vue';
 | 
			
		||||
import wBroadcast from './widgets/broadcast.vue';
 | 
			
		||||
import wCalendar from './widgets/calendar.vue';
 | 
			
		||||
import wPhotoStream from './widgets/photo-stream.vue';
 | 
			
		||||
import wSlideshow from './widgets/slideshow.vue';
 | 
			
		||||
import wTips from './widgets/tips.vue';
 | 
			
		||||
import wDonation from './widgets/donation.vue';
 | 
			
		||||
import wNav from './widgets/nav.vue';
 | 
			
		||||
//#endregion
 | 
			
		||||
 | 
			
		||||
Vue.component('mk-signin', signin);
 | 
			
		||||
Vue.component('mk-signup', signup);
 | 
			
		||||
Vue.component('mk-forkit', forkit);
 | 
			
		||||
@@ -41,3 +56,18 @@ Vue.component('mk-messaging-room', messagingRoom);
 | 
			
		||||
Vue.component('mk-url-preview', urlPreview);
 | 
			
		||||
Vue.component('mk-twitter-setting', twitterSetting);
 | 
			
		||||
Vue.component('mk-file-type-icon', fileTypeIcon);
 | 
			
		||||
 | 
			
		||||
//#region widgets
 | 
			
		||||
Vue.component('mkw-nav', wNav);
 | 
			
		||||
Vue.component('mkw-calendar', wCalendar);
 | 
			
		||||
Vue.component('mkw-photo-stream', wPhotoStream);
 | 
			
		||||
Vue.component('mkw-slideshow', wSlideshow);
 | 
			
		||||
Vue.component('mkw-tips', wTips);
 | 
			
		||||
Vue.component('mkw-donation', wDonation);
 | 
			
		||||
Vue.component('mkw-broadcast', wBroadcast);
 | 
			
		||||
Vue.component('mkw-profile', wProfile);
 | 
			
		||||
Vue.component('mkw-server', wServer);
 | 
			
		||||
Vue.component('mkw-rss', wRss);
 | 
			
		||||
Vue.component('mkw-version', wVersion);
 | 
			
		||||
Vue.component('mkw-access-log', wAccessLog);
 | 
			
		||||
//#endregion
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,16 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-access-log">
 | 
			
		||||
	<template v-if="props.design == 0">
 | 
			
		||||
		<p class="title">%fa:server%%i18n:desktop.tags.mk-access-log-home-widget.title%</p>
 | 
			
		||||
	</template>
 | 
			
		||||
	<div ref="log">
 | 
			
		||||
		<p v-for="req in requests">
 | 
			
		||||
			<span class="ip" :style="`color:${ req.fg }; background:${ req.bg }`">{{ req.ip }}</span>
 | 
			
		||||
			<b>{{ req.method }}</b>
 | 
			
		||||
			<span>{{ req.path }}</span>
 | 
			
		||||
		</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	<mk-widget-container :show-header="props.design == 0">
 | 
			
		||||
		<template slot="header">%fa:server%%i18n:desktop.tags.mk-access-log-home-widget.title%</template>
 | 
			
		||||
 | 
			
		||||
		<div :class="$style.logs" ref="log">
 | 
			
		||||
			<p v-for="req in requests">
 | 
			
		||||
				<span :class="$style.ip" :style="`color:${ req.fg }; background:${ req.bg }`">{{ req.ip }}</span>
 | 
			
		||||
				<b>{{ req.method }}</b>
 | 
			
		||||
				<span>{{ req.path }}</span>
 | 
			
		||||
			</p>
 | 
			
		||||
		</div>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -65,44 +66,25 @@ export default define({
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mkw-access-log
 | 
			
		||||
	overflow hidden
 | 
			
		||||
	background #fff
 | 
			
		||||
	border solid 1px rgba(0, 0, 0, 0.075)
 | 
			
		||||
	border-radius 6px
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.logs
 | 
			
		||||
	max-height 250px
 | 
			
		||||
	overflow auto
 | 
			
		||||
 | 
			
		||||
	> .title
 | 
			
		||||
		z-index 1
 | 
			
		||||
	> p
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 0 16px
 | 
			
		||||
		line-height 42px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		font-weight bold
 | 
			
		||||
		color #888
 | 
			
		||||
		box-shadow 0 1px rgba(0, 0, 0, 0.07)
 | 
			
		||||
		padding 8px
 | 
			
		||||
		font-size 0.8em
 | 
			
		||||
		color #555
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		&:nth-child(odd)
 | 
			
		||||
			background rgba(0, 0, 0, 0.025)
 | 
			
		||||
 | 
			
		||||
		> b
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
		max-height 250px
 | 
			
		||||
		overflow auto
 | 
			
		||||
 | 
			
		||||
		> p
 | 
			
		||||
			margin 0
 | 
			
		||||
			padding 8px
 | 
			
		||||
			font-size 0.8em
 | 
			
		||||
			color #555
 | 
			
		||||
 | 
			
		||||
			&:nth-child(odd)
 | 
			
		||||
				background rgba(0, 0, 0, 0.025)
 | 
			
		||||
 | 
			
		||||
			> .ip
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
				padding 0 4px
 | 
			
		||||
 | 
			
		||||
			> b
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
.ip
 | 
			
		||||
	margin-right 4px
 | 
			
		||||
	padding 0 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,5 +1,9 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-broadcast" :data-found="broadcasts.length != 0" :data-melt="props.design == 1">
 | 
			
		||||
<div class="mkw-broadcast"
 | 
			
		||||
	:data-found="broadcasts.length != 0"
 | 
			
		||||
	:data-melt="props.design == 1"
 | 
			
		||||
	:data-mobile="isMobile"
 | 
			
		||||
>
 | 
			
		||||
	<div class="icon">
 | 
			
		||||
		<svg height="32" version="1.1" viewBox="0 0 32 32" width="32">
 | 
			
		||||
			<path class="tower" d="M16.04,11.24c1.79,0,3.239-1.45,3.239-3.24S17.83,4.76,16.04,4.76c-1.79,0-3.24,1.45-3.24,3.24 C12.78,9.78,14.24,11.24,16.04,11.24z M16.04,13.84c-0.82,0-1.66-0.2-2.4-0.6L7.34,29.98h2.98l1.72-2h8l1.681,2H24.7L18.42,13.24 C17.66,13.64,16.859,13.84,16.04,13.84z M16.02,14.8l2.02,7.2h-4L16.02,14.8z M12.04,25.98l2-2h4l2,2H12.04z"></path>
 | 
			
		||||
@@ -150,4 +154,8 @@ export default define({
 | 
			
		||||
		display block
 | 
			
		||||
		font-size 0.7em
 | 
			
		||||
 | 
			
		||||
	&[data-mobile]
 | 
			
		||||
		> p
 | 
			
		||||
			color #fff
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -2,6 +2,7 @@
 | 
			
		||||
<div class="mkw-calendar"
 | 
			
		||||
	:data-melt="props.design == 1"
 | 
			
		||||
	:data-special="special"
 | 
			
		||||
	:data-mobile="isMobile"
 | 
			
		||||
>
 | 
			
		||||
	<div class="calendar" :data-is-holiday="isHoliday">
 | 
			
		||||
		<p class="month-and-year">
 | 
			
		||||
@@ -66,6 +67,7 @@ export default define({
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		func() {
 | 
			
		||||
			if (this.isMobile) return;
 | 
			
		||||
			if (this.props.design == 2) {
 | 
			
		||||
				this.props.design = 0;
 | 
			
		||||
			} else {
 | 
			
		||||
@@ -119,6 +121,11 @@ export default define({
 | 
			
		||||
		background transparent
 | 
			
		||||
		border none
 | 
			
		||||
 | 
			
		||||
	&[data-mobile]
 | 
			
		||||
		border none
 | 
			
		||||
		border-radius 8px
 | 
			
		||||
		box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
	&:after
 | 
			
		||||
		content ""
 | 
			
		||||
		display block
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-donation">
 | 
			
		||||
<div class="mkw-donation" :data-mobile="isMobile">
 | 
			
		||||
	<article>
 | 
			
		||||
		<h1>%fa:heart%%i18n:desktop.tags.mk-donation-home-widget.title%</h1>
 | 
			
		||||
		<p>
 | 
			
		||||
@@ -42,4 +42,17 @@ export default define({
 | 
			
		||||
			font-size 0.8em
 | 
			
		||||
			color #999
 | 
			
		||||
 | 
			
		||||
	&[data-mobile]
 | 
			
		||||
		border none
 | 
			
		||||
		background #ead8bb
 | 
			
		||||
		border-radius 8px
 | 
			
		||||
		box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
 | 
			
		||||
 | 
			
		||||
		> article
 | 
			
		||||
			> h1
 | 
			
		||||
				color #7b8871
 | 
			
		||||
 | 
			
		||||
			> p
 | 
			
		||||
				color #777d71
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,6 +1,10 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-nav">
 | 
			
		||||
	<mk-nav/>
 | 
			
		||||
	<mk-widget-container>
 | 
			
		||||
		<div :class="$style.body">
 | 
			
		||||
			<mk-nav/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -11,14 +15,12 @@ export default define({
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mkw-nav
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.body
 | 
			
		||||
	padding 16px
 | 
			
		||||
	font-size 12px
 | 
			
		||||
	color #aaa
 | 
			
		||||
	background #fff
 | 
			
		||||
	border solid 1px rgba(0, 0, 0, 0.075)
 | 
			
		||||
	border-radius 6px
 | 
			
		||||
 | 
			
		||||
	a
 | 
			
		||||
		color #999
 | 
			
		||||
							
								
								
									
										104
									
								
								src/web/app/common/views/components/widgets/photo-stream.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/web/app/common/views/components/widgets/photo-stream.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-photo-stream" :class="$style.root" :data-melt="props.design == 2">
 | 
			
		||||
	<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
 | 
			
		||||
		<template slot="header">%fa:camera%%i18n:desktop.tags.mk-photo-stream-home-widget.title%</template>
 | 
			
		||||
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<div :class="$style.stream" v-if="!fetching && images.length > 0">
 | 
			
		||||
			<div v-for="image in images" :key="image.id" :class="$style.img" :style="`background-image: url(${image.url}?thumbnail&size=256)`"></div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<p :class="$style.empty" v-if="!fetching && images.length == 0">%i18n:desktop.tags.mk-photo-stream-home-widget.no-photos%</p>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../../common/define-widget';
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'photo-stream',
 | 
			
		||||
	props: () => ({
 | 
			
		||||
		design: 0
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			images: [],
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('drive_file_created', this.onDriveFileCreated);
 | 
			
		||||
 | 
			
		||||
		(this as any).api('drive/stream', {
 | 
			
		||||
			type: 'image/*',
 | 
			
		||||
			limit: 9
 | 
			
		||||
		}).then(images => {
 | 
			
		||||
			this.images = images;
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('drive_file_created', this.onDriveFileCreated);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onDriveFileCreated(file) {
 | 
			
		||||
			if (/^image\/.+$/.test(file.type)) {
 | 
			
		||||
				this.images.unshift(file);
 | 
			
		||||
				if (this.images.length > 9) this.images.pop();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		func() {
 | 
			
		||||
			if (this.props.design == 2) {
 | 
			
		||||
				this.props.design = 0;
 | 
			
		||||
			} else {
 | 
			
		||||
				this.props.design++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.root[data-melt]
 | 
			
		||||
	.stream
 | 
			
		||||
		padding 0
 | 
			
		||||
 | 
			
		||||
	.img
 | 
			
		||||
		border solid 4px transparent
 | 
			
		||||
		border-radius 8px
 | 
			
		||||
 | 
			
		||||
.stream
 | 
			
		||||
	display -webkit-flex
 | 
			
		||||
	display -moz-flex
 | 
			
		||||
	display -ms-flex
 | 
			
		||||
	display flex
 | 
			
		||||
	justify-content center
 | 
			
		||||
	flex-wrap wrap
 | 
			
		||||
	padding 8px
 | 
			
		||||
 | 
			
		||||
	.img
 | 
			
		||||
		flex 1 1 33%
 | 
			
		||||
		width 33%
 | 
			
		||||
		height 80px
 | 
			
		||||
		background-position center center
 | 
			
		||||
		background-size cover
 | 
			
		||||
		border solid 2px transparent
 | 
			
		||||
		border-radius 4px
 | 
			
		||||
 | 
			
		||||
.fetching
 | 
			
		||||
.empty
 | 
			
		||||
	margin 0
 | 
			
		||||
	padding 16px
 | 
			
		||||
	text-align center
 | 
			
		||||
	color #aaa
 | 
			
		||||
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										93
									
								
								src/web/app/common/views/components/widgets/rss.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/web/app/common/views/components/widgets/rss.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-rss" :data-mobile="isMobile">
 | 
			
		||||
	<mk-widget-container :show-header="!props.compact">
 | 
			
		||||
		<template slot="header">%fa:rss-square%RSS</template>
 | 
			
		||||
		<button slot="func" title="設定" @click="setting">%fa:cog%</button>
 | 
			
		||||
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<div :class="$style.feed" v-else>
 | 
			
		||||
			<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
 | 
			
		||||
		</div>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../../common/define-widget';
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'rss',
 | 
			
		||||
	props: () => ({
 | 
			
		||||
		compact: false
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			url: 'http://news.yahoo.co.jp/pickup/rss.xml',
 | 
			
		||||
			items: [],
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			clock: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.fetch();
 | 
			
		||||
		this.clock = setInterval(this.fetch, 60000);
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		clearInterval(this.clock);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		func() {
 | 
			
		||||
			this.props.compact = !this.props.compact;
 | 
			
		||||
		},
 | 
			
		||||
		fetch() {
 | 
			
		||||
			fetch(`https://api.rss2json.com/v1/api.json?rss_url=${this.url}`, {
 | 
			
		||||
				cache: 'no-cache'
 | 
			
		||||
			}).then(res => {
 | 
			
		||||
				res.json().then(feed => {
 | 
			
		||||
					this.items = feed.items;
 | 
			
		||||
					this.fetching = false;
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		setting() {
 | 
			
		||||
			alert('not implemented yet');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.feed
 | 
			
		||||
	padding 12px 16px
 | 
			
		||||
	font-size 0.9em
 | 
			
		||||
 | 
			
		||||
	> a
 | 
			
		||||
		display block
 | 
			
		||||
		padding 4px 0
 | 
			
		||||
		color #666
 | 
			
		||||
		border-bottom dashed 1px #eee
 | 
			
		||||
 | 
			
		||||
		&:last-child
 | 
			
		||||
			border-bottom none
 | 
			
		||||
 | 
			
		||||
.fetching
 | 
			
		||||
	margin 0
 | 
			
		||||
	padding 16px
 | 
			
		||||
	text-align center
 | 
			
		||||
	color #aaa
 | 
			
		||||
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
&[data-mobile]
 | 
			
		||||
	.feed
 | 
			
		||||
		padding 0
 | 
			
		||||
		font-size 1em
 | 
			
		||||
 | 
			
		||||
		> a
 | 
			
		||||
			padding 8px 16px
 | 
			
		||||
 | 
			
		||||
			&:nth-child(even)
 | 
			
		||||
				background #e2e2e2
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										93
									
								
								src/web/app/common/views/components/widgets/server.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/web/app/common/views/components/widgets/server.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-server">
 | 
			
		||||
	<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
 | 
			
		||||
		<template slot="header">%fa:server%%i18n:desktop.tags.mk-server-home-widget.title%</template>
 | 
			
		||||
		<button slot="func" @click="toggle" title="%i18n:desktop.tags.mk-server-home-widget.toggle%">%fa:sort%</button>
 | 
			
		||||
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<template v-if="!fetching">
 | 
			
		||||
			<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
 | 
			
		||||
			<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>
 | 
			
		||||
			<x-memory v-show="props.view == 2" :connection="connection"/>
 | 
			
		||||
			<x-disk v-show="props.view == 3" :connection="connection"/>
 | 
			
		||||
			<x-uptimes v-show="props.view == 4" :connection="connection"/>
 | 
			
		||||
			<x-info v-show="props.view == 5" :connection="connection" :meta="meta"/>
 | 
			
		||||
		</template>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../../common/define-widget';
 | 
			
		||||
import XCpuMemory from './server.cpu-memory.vue';
 | 
			
		||||
import XCpu from './server.cpu.vue';
 | 
			
		||||
import XMemory from './server.memory.vue';
 | 
			
		||||
import XDisk from './server.disk.vue';
 | 
			
		||||
import XUptimes from './server.uptimes.vue';
 | 
			
		||||
import XInfo from './server.info.vue';
 | 
			
		||||
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'server',
 | 
			
		||||
	props: () => ({
 | 
			
		||||
		design: 0,
 | 
			
		||||
		view: 0
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XCpuMemory,
 | 
			
		||||
		XCpu,
 | 
			
		||||
		XMemory,
 | 
			
		||||
		XDisk,
 | 
			
		||||
		XUptimes,
 | 
			
		||||
		XInfo
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			meta: null,
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		(this as any).os.getMeta().then(meta => {
 | 
			
		||||
			this.meta = meta;
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		this.connection = (this as any).os.streams.serverStream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.streams.serverStream.use();
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		(this as any).os.streams.serverStream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		toggle() {
 | 
			
		||||
			if (this.props.view == 5) {
 | 
			
		||||
				this.props.view = 0;
 | 
			
		||||
			} else {
 | 
			
		||||
				this.props.view++;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		func() {
 | 
			
		||||
			if (this.props.design == 2) {
 | 
			
		||||
				this.props.design = 0;
 | 
			
		||||
			} else {
 | 
			
		||||
				this.props.design++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.fetching
 | 
			
		||||
	margin 0
 | 
			
		||||
	padding 16px
 | 
			
		||||
	text-align center
 | 
			
		||||
	color #aaa
 | 
			
		||||
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -27,27 +27,19 @@ import friendsMaker from './friends-maker.vue';
 | 
			
		||||
import followers from './followers.vue';
 | 
			
		||||
import following from './following.vue';
 | 
			
		||||
import usersList from './users-list.vue';
 | 
			
		||||
import wNav from './widgets/nav.vue';
 | 
			
		||||
import wCalendar from './widgets/calendar.vue';
 | 
			
		||||
import wPhotoStream from './widgets/photo-stream.vue';
 | 
			
		||||
import wSlideshow from './widgets/slideshow.vue';
 | 
			
		||||
import wTips from './widgets/tips.vue';
 | 
			
		||||
import wDonation from './widgets/donation.vue';
 | 
			
		||||
import widgetContainer from './widget-container.vue';
 | 
			
		||||
 | 
			
		||||
//#region widgets
 | 
			
		||||
import wNotifications from './widgets/notifications.vue';
 | 
			
		||||
import wBroadcast from './widgets/broadcast.vue';
 | 
			
		||||
import wTimemachine from './widgets/timemachine.vue';
 | 
			
		||||
import wProfile from './widgets/profile.vue';
 | 
			
		||||
import wServer from './widgets/server.vue';
 | 
			
		||||
import wActivity from './widgets/activity.vue';
 | 
			
		||||
import wRss from './widgets/rss.vue';
 | 
			
		||||
import wTrends from './widgets/trends.vue';
 | 
			
		||||
import wVersion from './widgets/version.vue';
 | 
			
		||||
import wUsers from './widgets/users.vue';
 | 
			
		||||
import wPolls from './widgets/polls.vue';
 | 
			
		||||
import wPostForm from './widgets/post-form.vue';
 | 
			
		||||
import wMessaging from './widgets/messaging.vue';
 | 
			
		||||
import wChannel from './widgets/channel.vue';
 | 
			
		||||
import wAccessLog from './widgets/access-log.vue';
 | 
			
		||||
//#endregion
 | 
			
		||||
 | 
			
		||||
Vue.component('mk-ui', ui);
 | 
			
		||||
Vue.component('mk-ui-notification', uiNotification);
 | 
			
		||||
@@ -76,24 +68,16 @@ Vue.component('mk-friends-maker', friendsMaker);
 | 
			
		||||
Vue.component('mk-followers', followers);
 | 
			
		||||
Vue.component('mk-following', following);
 | 
			
		||||
Vue.component('mk-users-list', usersList);
 | 
			
		||||
Vue.component('mkw-nav', wNav);
 | 
			
		||||
Vue.component('mkw-calendar', wCalendar);
 | 
			
		||||
Vue.component('mkw-photo-stream', wPhotoStream);
 | 
			
		||||
Vue.component('mkw-slideshow', wSlideshow);
 | 
			
		||||
Vue.component('mkw-tips', wTips);
 | 
			
		||||
Vue.component('mkw-donation', wDonation);
 | 
			
		||||
Vue.component('mk-widget-container', widgetContainer);
 | 
			
		||||
 | 
			
		||||
//#region widgets
 | 
			
		||||
Vue.component('mkw-notifications', wNotifications);
 | 
			
		||||
Vue.component('mkw-broadcast', wBroadcast);
 | 
			
		||||
Vue.component('mkw-timemachine', wTimemachine);
 | 
			
		||||
Vue.component('mkw-profile', wProfile);
 | 
			
		||||
Vue.component('mkw-server', wServer);
 | 
			
		||||
Vue.component('mkw-activity', wActivity);
 | 
			
		||||
Vue.component('mkw-rss', wRss);
 | 
			
		||||
Vue.component('mkw-trends', wTrends);
 | 
			
		||||
Vue.component('mkw-version', wVersion);
 | 
			
		||||
Vue.component('mkw-users', wUsers);
 | 
			
		||||
Vue.component('mkw-polls', wPolls);
 | 
			
		||||
Vue.component('mkw-post-form', wPostForm);
 | 
			
		||||
Vue.component('mkw-messaging', wMessaging);
 | 
			
		||||
Vue.component('mkw-channel', wChannel);
 | 
			
		||||
Vue.component('mkw-access-log', wAccessLog);
 | 
			
		||||
//#endregion
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										72
									
								
								src/web/app/desktop/views/components/widget-container.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/web/app/desktop/views/components/widget-container.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-widget-container" :class="{ naked }">
 | 
			
		||||
	<header v-if="showHeader">
 | 
			
		||||
		<div class="title"><slot name="header"></slot></div>
 | 
			
		||||
		<slot name="func"></slot>
 | 
			
		||||
	</header>
 | 
			
		||||
	<slot></slot>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		showHeader: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: true
 | 
			
		||||
		},
 | 
			
		||||
		naked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-widget-container
 | 
			
		||||
	background #fff
 | 
			
		||||
	border solid 1px rgba(0, 0, 0, 0.075)
 | 
			
		||||
	border-radius 6px
 | 
			
		||||
	overflow hidden
 | 
			
		||||
 | 
			
		||||
	&.naked
 | 
			
		||||
		background transparent !important
 | 
			
		||||
		border none !important
 | 
			
		||||
 | 
			
		||||
	> header
 | 
			
		||||
		> .title
 | 
			
		||||
			z-index 1
 | 
			
		||||
			margin 0
 | 
			
		||||
			padding 0 16px
 | 
			
		||||
			line-height 42px
 | 
			
		||||
			font-size 0.9em
 | 
			
		||||
			font-weight bold
 | 
			
		||||
			color #888
 | 
			
		||||
			box-shadow 0 1px rgba(0, 0, 0, 0.07)
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
 | 
			
		||||
			&:empty
 | 
			
		||||
				display none
 | 
			
		||||
 | 
			
		||||
		> button
 | 
			
		||||
			position absolute
 | 
			
		||||
			z-index 2
 | 
			
		||||
			top 0
 | 
			
		||||
			right 0
 | 
			
		||||
			padding 0
 | 
			
		||||
			width 42px
 | 
			
		||||
			font-size 0.9em
 | 
			
		||||
			line-height 42px
 | 
			
		||||
			color #ccc
 | 
			
		||||
 | 
			
		||||
			&:hover
 | 
			
		||||
				color #aaa
 | 
			
		||||
 | 
			
		||||
			&:active
 | 
			
		||||
				color #999
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,122 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-photo-stream" :data-melt="props.design == 2">
 | 
			
		||||
	<p class="title" v-if="props.design == 0">%fa:camera%%i18n:desktop.tags.mk-photo-stream-home-widget.title%</p>
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<div class="stream" v-if="!fetching && images.length > 0">
 | 
			
		||||
		<div v-for="image in images" :key="image.id" class="img" :style="`background-image: url(${image.url}?thumbnail&size=256)`"></div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<p class="empty" v-if="!fetching && images.length == 0">%i18n:desktop.tags.mk-photo-stream-home-widget.no-photos%</p>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../../common/define-widget';
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'photo-stream',
 | 
			
		||||
	props: () => ({
 | 
			
		||||
		design: 0
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			images: [],
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.connection = (this as any).os.stream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.stream.use();
 | 
			
		||||
 | 
			
		||||
		this.connection.on('drive_file_created', this.onDriveFileCreated);
 | 
			
		||||
 | 
			
		||||
		(this as any).api('drive/stream', {
 | 
			
		||||
			type: 'image/*',
 | 
			
		||||
			limit: 9
 | 
			
		||||
		}).then(images => {
 | 
			
		||||
			this.images = images;
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('drive_file_created', this.onDriveFileCreated);
 | 
			
		||||
		(this as any).os.stream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		onDriveFileCreated(file) {
 | 
			
		||||
			if (/^image\/.+$/.test(file.type)) {
 | 
			
		||||
				this.images.unshift(file);
 | 
			
		||||
				if (this.images.length > 9) this.images.pop();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		func() {
 | 
			
		||||
			if (this.props.design == 2) {
 | 
			
		||||
				this.props.design = 0;
 | 
			
		||||
			} else {
 | 
			
		||||
				this.props.design++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mkw-photo-stream
 | 
			
		||||
	background #fff
 | 
			
		||||
	border solid 1px rgba(0, 0, 0, 0.075)
 | 
			
		||||
	border-radius 6px
 | 
			
		||||
 | 
			
		||||
	&[data-melt]
 | 
			
		||||
		background transparent !important
 | 
			
		||||
		border none !important
 | 
			
		||||
 | 
			
		||||
		> .stream
 | 
			
		||||
			padding 0
 | 
			
		||||
 | 
			
		||||
			> .img
 | 
			
		||||
				border solid 4px transparent
 | 
			
		||||
				border-radius 8px
 | 
			
		||||
 | 
			
		||||
	> .title
 | 
			
		||||
		z-index 1
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 0 16px
 | 
			
		||||
		line-height 42px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		font-weight bold
 | 
			
		||||
		color #888
 | 
			
		||||
		box-shadow 0 1px rgba(0, 0, 0, 0.07)
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> .stream
 | 
			
		||||
		display -webkit-flex
 | 
			
		||||
		display -moz-flex
 | 
			
		||||
		display -ms-flex
 | 
			
		||||
		display flex
 | 
			
		||||
		justify-content center
 | 
			
		||||
		flex-wrap wrap
 | 
			
		||||
		padding 8px
 | 
			
		||||
 | 
			
		||||
		> .img
 | 
			
		||||
			flex 1 1 33%
 | 
			
		||||
			width 33%
 | 
			
		||||
			height 80px
 | 
			
		||||
			background-position center center
 | 
			
		||||
			background-size cover
 | 
			
		||||
			border solid 2px transparent
 | 
			
		||||
			border-radius 4px
 | 
			
		||||
 | 
			
		||||
	> .fetching
 | 
			
		||||
	> .empty
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 16px
 | 
			
		||||
		text-align center
 | 
			
		||||
		color #aaa
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,111 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-rss">
 | 
			
		||||
	<template v-if="!props.compact">
 | 
			
		||||
		<p class="title">%fa:rss-square%RSS</p>
 | 
			
		||||
		<button title="設定">%fa:cog%</button>
 | 
			
		||||
	</template>
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<div class="feed" v-else>
 | 
			
		||||
		<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../../common/define-widget';
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'rss',
 | 
			
		||||
	props: () => ({
 | 
			
		||||
		compact: false
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			url: 'http://news.yahoo.co.jp/pickup/rss.xml',
 | 
			
		||||
			items: [],
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			clock: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.fetch();
 | 
			
		||||
		this.clock = setInterval(this.fetch, 60000);
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		clearInterval(this.clock);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		func() {
 | 
			
		||||
			this.props.compact = !this.props.compact;
 | 
			
		||||
		},
 | 
			
		||||
		fetch() {
 | 
			
		||||
			fetch(`https://api.rss2json.com/v1/api.json?rss_url=${this.url}`, {
 | 
			
		||||
				cache: 'no-cache'
 | 
			
		||||
			}).then(res => {
 | 
			
		||||
				res.json().then(feed => {
 | 
			
		||||
					this.items = feed.items;
 | 
			
		||||
					this.fetching = false;
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mkw-rss
 | 
			
		||||
	background #fff
 | 
			
		||||
	border solid 1px rgba(0, 0, 0, 0.075)
 | 
			
		||||
	border-radius 6px
 | 
			
		||||
 | 
			
		||||
	> .title
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 0 16px
 | 
			
		||||
		line-height 42px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		font-weight bold
 | 
			
		||||
		color #888
 | 
			
		||||
		box-shadow 0 1px rgba(0, 0, 0, 0.07)
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> button
 | 
			
		||||
		position absolute
 | 
			
		||||
		top 0
 | 
			
		||||
		right 0
 | 
			
		||||
		padding 0
 | 
			
		||||
		width 42px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		line-height 42px
 | 
			
		||||
		color #ccc
 | 
			
		||||
 | 
			
		||||
		&:hover
 | 
			
		||||
			color #aaa
 | 
			
		||||
 | 
			
		||||
		&:active
 | 
			
		||||
			color #999
 | 
			
		||||
 | 
			
		||||
	> .feed
 | 
			
		||||
		padding 12px 16px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
 | 
			
		||||
		> a
 | 
			
		||||
			display block
 | 
			
		||||
			padding 4px 0
 | 
			
		||||
			color #666
 | 
			
		||||
			border-bottom dashed 1px #eee
 | 
			
		||||
 | 
			
		||||
			&:last-child
 | 
			
		||||
				border-bottom none
 | 
			
		||||
 | 
			
		||||
	> .fetching
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 16px
 | 
			
		||||
		text-align center
 | 
			
		||||
		color #aaa
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,131 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-server" :data-melt="props.design == 2">
 | 
			
		||||
	<template v-if="props.design == 0">
 | 
			
		||||
		<p class="title">%fa:server%%i18n:desktop.tags.mk-server-home-widget.title%</p>
 | 
			
		||||
		<button @click="toggle" title="%i18n:desktop.tags.mk-server-home-widget.toggle%">%fa:sort%</button>
 | 
			
		||||
	</template>
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<template v-if="!fetching">
 | 
			
		||||
		<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
 | 
			
		||||
		<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>
 | 
			
		||||
		<x-memory v-show="props.view == 2" :connection="connection"/>
 | 
			
		||||
		<x-disk v-show="props.view == 3" :connection="connection"/>
 | 
			
		||||
		<x-uptimes v-show="props.view == 4" :connection="connection"/>
 | 
			
		||||
		<x-info v-show="props.view == 5" :connection="connection" :meta="meta"/>
 | 
			
		||||
	</template>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../../common/define-widget';
 | 
			
		||||
import XCpuMemory from './server.cpu-memory.vue';
 | 
			
		||||
import XCpu from './server.cpu.vue';
 | 
			
		||||
import XMemory from './server.memory.vue';
 | 
			
		||||
import XDisk from './server.disk.vue';
 | 
			
		||||
import XUptimes from './server.uptimes.vue';
 | 
			
		||||
import XInfo from './server.info.vue';
 | 
			
		||||
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'server',
 | 
			
		||||
	props: () => ({
 | 
			
		||||
		design: 0,
 | 
			
		||||
		view: 0
 | 
			
		||||
	})
 | 
			
		||||
}).extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XCpuMemory,
 | 
			
		||||
		XCpu,
 | 
			
		||||
		XMemory,
 | 
			
		||||
		XDisk,
 | 
			
		||||
		XUptimes,
 | 
			
		||||
		XInfo
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			fetching: true,
 | 
			
		||||
			meta: null,
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		(this as any).os.getMeta().then(meta => {
 | 
			
		||||
			this.meta = meta;
 | 
			
		||||
			this.fetching = false;
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		this.connection = (this as any).os.streams.serverStream.getConnection();
 | 
			
		||||
		this.connectionId = (this as any).os.streams.serverStream.use();
 | 
			
		||||
	},
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		(this as any).os.streams.serverStream.dispose(this.connectionId);
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		toggle() {
 | 
			
		||||
			if (this.props.view == 5) {
 | 
			
		||||
				this.props.view = 0;
 | 
			
		||||
			} else {
 | 
			
		||||
				this.props.view++;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		func() {
 | 
			
		||||
			if (this.props.design == 2) {
 | 
			
		||||
				this.props.design = 0;
 | 
			
		||||
			} else {
 | 
			
		||||
				this.props.design++;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mkw-server
 | 
			
		||||
	background #fff
 | 
			
		||||
	border solid 1px rgba(0, 0, 0, 0.075)
 | 
			
		||||
	border-radius 6px
 | 
			
		||||
 | 
			
		||||
	&[data-melt]
 | 
			
		||||
		background transparent !important
 | 
			
		||||
		border none !important
 | 
			
		||||
 | 
			
		||||
	> .title
 | 
			
		||||
		z-index 1
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 0 16px
 | 
			
		||||
		line-height 42px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		font-weight bold
 | 
			
		||||
		color #888
 | 
			
		||||
		box-shadow 0 1px rgba(0, 0, 0, 0.07)
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> button
 | 
			
		||||
		position absolute
 | 
			
		||||
		z-index 2
 | 
			
		||||
		top 0
 | 
			
		||||
		right 0
 | 
			
		||||
		padding 0
 | 
			
		||||
		width 42px
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		line-height 42px
 | 
			
		||||
		color #ccc
 | 
			
		||||
 | 
			
		||||
		&:hover
 | 
			
		||||
			color #aaa
 | 
			
		||||
 | 
			
		||||
		&:active
 | 
			
		||||
			color #999
 | 
			
		||||
 | 
			
		||||
	> .fetching
 | 
			
		||||
		margin 0
 | 
			
		||||
		padding 16px
 | 
			
		||||
		text-align center
 | 
			
		||||
		color #aaa
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="root activity">
 | 
			
		||||
<div class="mk-activity">
 | 
			
		||||
	<svg v-if="data" ref="canvas" viewBox="0 0 30 1" preserveAspectRatio="none">
 | 
			
		||||
		<g v-for="(d, i) in data">
 | 
			
		||||
			<rect width="0.8" :height="d.postsH"
 | 
			
		||||
@@ -47,7 +47,7 @@ export default Vue.extend({
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.root.activity
 | 
			
		||||
.mk-activity
 | 
			
		||||
	max-width 600px
 | 
			
		||||
	margin 0 auto
 | 
			
		||||
 | 
			
		||||
@@ -1,29 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-home">
 | 
			
		||||
	<mk-timeline @loaded="onTlLoaded"/>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	methods: {
 | 
			
		||||
		onTlLoaded() {
 | 
			
		||||
			this.$emit('loaded');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-home
 | 
			
		||||
 | 
			
		||||
	> .mk-timeline
 | 
			
		||||
		max-width 600px
 | 
			
		||||
		margin 0 auto
 | 
			
		||||
		padding 8px
 | 
			
		||||
 | 
			
		||||
	@media (min-width 500px)
 | 
			
		||||
		padding 16px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
 | 
			
		||||
import ui from './ui.vue';
 | 
			
		||||
import home from './home.vue';
 | 
			
		||||
import timeline from './timeline.vue';
 | 
			
		||||
import posts from './posts.vue';
 | 
			
		||||
import imagesImage from './images-image.vue';
 | 
			
		||||
@@ -19,9 +18,14 @@ import notificationPreview from './notification-preview.vue';
 | 
			
		||||
import usersList from './users-list.vue';
 | 
			
		||||
import userPreview from './user-preview.vue';
 | 
			
		||||
import userTimeline from './user-timeline.vue';
 | 
			
		||||
import activity from './activity.vue';
 | 
			
		||||
import widgetContainer from './widget-container.vue';
 | 
			
		||||
 | 
			
		||||
//#region widgets
 | 
			
		||||
import wActivity from './widgets/activity.vue';
 | 
			
		||||
//#endregion
 | 
			
		||||
 | 
			
		||||
Vue.component('mk-ui', ui);
 | 
			
		||||
Vue.component('mk-home', home);
 | 
			
		||||
Vue.component('mk-timeline', timeline);
 | 
			
		||||
Vue.component('mk-posts', posts);
 | 
			
		||||
Vue.component('mk-images-image', imagesImage);
 | 
			
		||||
@@ -39,3 +43,9 @@ Vue.component('mk-notification-preview', notificationPreview);
 | 
			
		||||
Vue.component('mk-users-list', usersList);
 | 
			
		||||
Vue.component('mk-user-preview', userPreview);
 | 
			
		||||
Vue.component('mk-user-timeline', userTimeline);
 | 
			
		||||
Vue.component('mk-activity', activity);
 | 
			
		||||
Vue.component('mk-widget-container', widgetContainer);
 | 
			
		||||
 | 
			
		||||
//#region widgets
 | 
			
		||||
Vue.component('mkw-activity', wActivity);
 | 
			
		||||
//#endregion
 | 
			
		||||
 
 | 
			
		||||
@@ -9,9 +9,7 @@
 | 
			
		||||
			<h1>
 | 
			
		||||
				<slot>Misskey</slot>
 | 
			
		||||
			</h1>
 | 
			
		||||
			<button v-if="func" @click="func">
 | 
			
		||||
				<slot name="funcIcon"></slot>
 | 
			
		||||
			</button>
 | 
			
		||||
			<slot name="func"></slot>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-ui">
 | 
			
		||||
	<x-header :func="func">
 | 
			
		||||
		<template slot="funcIcon"><slot name="funcIcon"></slot></template>
 | 
			
		||||
	<x-header>
 | 
			
		||||
		<template slot="func"><slot name="func"></slot></template>
 | 
			
		||||
		<slot name="header"></slot>
 | 
			
		||||
	</x-header>
 | 
			
		||||
	<x-nav :is-open="isDrawerOpening"/>
 | 
			
		||||
@@ -23,7 +23,7 @@ export default Vue.extend({
 | 
			
		||||
		XHeader,
 | 
			
		||||
		XNav
 | 
			
		||||
	},
 | 
			
		||||
	props: ['title', 'func'],
 | 
			
		||||
	props: ['title'],
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			isDrawerOpening: false,
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										65
									
								
								src/web/app/mobile/views/components/widget-container.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/web/app/mobile/views/components/widget-container.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-widget-container" :class="{ naked }">
 | 
			
		||||
	<header v-if="showHeader">
 | 
			
		||||
		<div class="title"><slot name="header"></slot></div>
 | 
			
		||||
		<slot name="func"></slot>
 | 
			
		||||
	</header>
 | 
			
		||||
	<slot></slot>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		showHeader: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: true
 | 
			
		||||
		},
 | 
			
		||||
		naked: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-widget-container
 | 
			
		||||
	background #eee
 | 
			
		||||
	border-radius 8px
 | 
			
		||||
	box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
 | 
			
		||||
	overflow hidden
 | 
			
		||||
 | 
			
		||||
	&.naked
 | 
			
		||||
		background transparent !important
 | 
			
		||||
		border none !important
 | 
			
		||||
 | 
			
		||||
	> header
 | 
			
		||||
		> .title
 | 
			
		||||
			margin 0
 | 
			
		||||
			padding 8px 10px
 | 
			
		||||
			font-size 15px
 | 
			
		||||
			font-weight normal
 | 
			
		||||
			color #465258
 | 
			
		||||
			background #fff
 | 
			
		||||
			border-radius 8px 8px 0 0
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
				margin-right 6px
 | 
			
		||||
 | 
			
		||||
			&:empty
 | 
			
		||||
				display none
 | 
			
		||||
 | 
			
		||||
		> button
 | 
			
		||||
			position absolute
 | 
			
		||||
			z-index 2
 | 
			
		||||
			top 0
 | 
			
		||||
			right 0
 | 
			
		||||
			padding 0
 | 
			
		||||
			width 42px
 | 
			
		||||
			height 100%
 | 
			
		||||
			font-size 15px
 | 
			
		||||
			color #465258
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
							
								
								
									
										23
									
								
								src/web/app/mobile/views/components/widgets/activity.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/web/app/mobile/views/components/widgets/activity.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,23 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-activity">
 | 
			
		||||
	<mk-widget-container>
 | 
			
		||||
		<template slot="header">%fa:chart-bar%アクティビティ</template>
 | 
			
		||||
		<div :class="$style.body">
 | 
			
		||||
			<mk-activity :user="os.i"/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</mk-widget-container>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import define from '../../../../common/define-widget';
 | 
			
		||||
 | 
			
		||||
export default define({
 | 
			
		||||
	name: 'activity',
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.body
 | 
			
		||||
	padding 8px
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-ui :func="fn">
 | 
			
		||||
<mk-ui>
 | 
			
		||||
	<span slot="header">
 | 
			
		||||
		<template v-if="folder">%fa:R folder-open%{{ folder.name }}</template>
 | 
			
		||||
		<template v-if="file"><mk-file-type-icon class="icon" :type="file.type"/>{{ file.name }}</template>
 | 
			
		||||
		<template v-if="!folder && !file">%fa:cloud%%i18n:mobile.tags.mk-drive-page.drive%</template>
 | 
			
		||||
	</span>
 | 
			
		||||
	<template slot="funcIcon">%fa:ellipsis-h%</template>
 | 
			
		||||
	<template slot="func"><button @click="fn">%fa:ellipsis-h%</button></template>
 | 
			
		||||
	<mk-drive
 | 
			
		||||
		ref="browser"
 | 
			
		||||
		:init-folder="initFolder"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,24 +1,112 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-ui :func="fn">
 | 
			
		||||
	<span slot="header">%fa:home%%i18n:mobile.tags.mk-home.home%</span>
 | 
			
		||||
	<template slot="funcIcon">%fa:pencil-alt%</template>
 | 
			
		||||
	<mk-home @loaded="onHomeLoaded"/>
 | 
			
		||||
<mk-ui>
 | 
			
		||||
	<span slot="header" @click="showTl = !showTl">
 | 
			
		||||
		<template v-if="showTl">%fa:home%タイムライン</template>
 | 
			
		||||
		<template v-else>%fa:home%ウィジェット</template>
 | 
			
		||||
		<span style="margin-left:8px">
 | 
			
		||||
			<template v-if="showTl">%fa:angle-down%</template>
 | 
			
		||||
			<template v-else>%fa:angle-up%</template>
 | 
			
		||||
		</span>
 | 
			
		||||
	</span>
 | 
			
		||||
	<template slot="func">
 | 
			
		||||
		<button @click="fn" v-if="showTl">%fa:pencil-alt%</button>
 | 
			
		||||
		<button @click="customizing = !customizing" v-else>%fa:cog%</button>
 | 
			
		||||
	</template>
 | 
			
		||||
	<main>
 | 
			
		||||
		<div class="tl">
 | 
			
		||||
			<mk-timeline @loaded="onLoaded" v-show="showTl"/>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="widgets" v-if="!showTl">
 | 
			
		||||
			<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="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>移動するには「三」をドラッグします。削除するには「x」をタップします。</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>
 | 
			
		||||
							<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id" :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" :widget="widget" :is-mobile="true" @chosen="warp"/>
 | 
			
		||||
			</template>
 | 
			
		||||
		</div>
 | 
			
		||||
	</main>
 | 
			
		||||
</mk-ui>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import * as XDraggable from 'vuedraggable';
 | 
			
		||||
import * as uuid from 'uuid';
 | 
			
		||||
import Progress from '../../../common/scripts/loading';
 | 
			
		||||
import getPostSummary from '../../../../../common/get-post-summary';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XDraggable
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			connection: null,
 | 
			
		||||
			connectionId: null,
 | 
			
		||||
			unreadCount: 0
 | 
			
		||||
			unreadCount: 0,
 | 
			
		||||
			showTl: true,
 | 
			
		||||
			widgets: [],
 | 
			
		||||
			customizing: false,
 | 
			
		||||
			widgetAdderSelected: null
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	created() {
 | 
			
		||||
		if ((this as any).os.i.client_settings.mobile_home == null) {
 | 
			
		||||
			Vue.set((this as any).os.i.client_settings, 'mobile_home',  [{
 | 
			
		||||
				name: 'calendar',
 | 
			
		||||
				id: 'a'
 | 
			
		||||
			}, {
 | 
			
		||||
				name: 'activity',
 | 
			
		||||
				id: 'b'
 | 
			
		||||
			}, {
 | 
			
		||||
				name: 'rss',
 | 
			
		||||
				id: 'c'
 | 
			
		||||
			}, {
 | 
			
		||||
				name: 'photo-stream',
 | 
			
		||||
				id: 'd'
 | 
			
		||||
			}, {
 | 
			
		||||
				name: 'donation',
 | 
			
		||||
				id: 'e'
 | 
			
		||||
			}, {
 | 
			
		||||
				name: 'nav',
 | 
			
		||||
				id: 'f'
 | 
			
		||||
			}, {
 | 
			
		||||
				name: 'version',
 | 
			
		||||
				id: 'g'
 | 
			
		||||
			}]);
 | 
			
		||||
		}
 | 
			
		||||
		this.widgets = (this as any).os.i.client_settings.mobile_home;
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		document.title = 'Misskey';
 | 
			
		||||
		document.documentElement.style.background = '#313a42';
 | 
			
		||||
@@ -40,7 +128,7 @@ export default Vue.extend({
 | 
			
		||||
		fn() {
 | 
			
		||||
			(this as any).apis.post();
 | 
			
		||||
		},
 | 
			
		||||
		onHomeLoaded() {
 | 
			
		||||
		onLoaded() {
 | 
			
		||||
			Progress.done();
 | 
			
		||||
		},
 | 
			
		||||
		onStreamPost(post) {
 | 
			
		||||
@@ -54,7 +142,81 @@ export default Vue.extend({
 | 
			
		||||
				this.unreadCount = 0;
 | 
			
		||||
				document.title = 'Misskey';
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
		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).api('i/update_mobile_home', {
 | 
			
		||||
				home: this.widgets
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
		warp() {
 | 
			
		||||
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
main
 | 
			
		||||
 | 
			
		||||
	> .tl
 | 
			
		||||
		> .mk-timeline
 | 
			
		||||
			max-width 600px
 | 
			
		||||
			margin 0 auto
 | 
			
		||||
			padding 8px
 | 
			
		||||
 | 
			
		||||
			@media (min-width 500px)
 | 
			
		||||
				padding 16px
 | 
			
		||||
 | 
			
		||||
	> .widgets
 | 
			
		||||
		margin 0 auto
 | 
			
		||||
		max-width 500px
 | 
			
		||||
 | 
			
		||||
		> 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>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-ui :func="fn">
 | 
			
		||||
<mk-ui>
 | 
			
		||||
	<span slot="header">%fa:R bell%%i18n:mobile.tags.mk-notifications-page.notifications%</span>
 | 
			
		||||
	<span slot="funcIcon">%fa:check%</span>
 | 
			
		||||
	<template slot="func"><button @click="fn">%fa:check%</button></template>
 | 
			
		||||
	<mk-notifications @fetched="onFetched"/>
 | 
			
		||||
</mk-ui>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-ui>
 | 
			
		||||
	<span slot="header" v-if="!fetching">%fa:user% {{ user.name }}</span>
 | 
			
		||||
	<template slot="funcIcon">%fa:pencil-alt%</template>
 | 
			
		||||
	<main v-if="!fetching">
 | 
			
		||||
		<header>
 | 
			
		||||
			<div class="banner" :style="user.banner_url ? `background-image: url(${user.banner_url}?thumbnail&size=1024)` : ''"></div>
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@
 | 
			
		||||
	<section class="activity">
 | 
			
		||||
		<h2>%fa:chart-bar%%i18n:mobile.tags.mk-user-overview.activity%</h2>
 | 
			
		||||
		<div>
 | 
			
		||||
			<x-activity :user="user"/>
 | 
			
		||||
			<mk-activity :user="user"/>
 | 
			
		||||
		</div>
 | 
			
		||||
	</section>
 | 
			
		||||
	<section class="frequently-replied-users">
 | 
			
		||||
@@ -41,15 +41,13 @@ import XPosts from './home.posts.vue';
 | 
			
		||||
import XPhotos from './home.photos.vue';
 | 
			
		||||
import XFriends from './home.friends.vue';
 | 
			
		||||
import XFollowersYouKnow from './home.followers-you-know.vue';
 | 
			
		||||
import XActivity from './home.activity.vue';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XPosts,
 | 
			
		||||
		XPhotos,
 | 
			
		||||
		XFriends,
 | 
			
		||||
		XFollowersYouKnow,
 | 
			
		||||
		XActivity
 | 
			
		||||
		XFollowersYouKnow
 | 
			
		||||
	},
 | 
			
		||||
	props: ['user']
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user