From df8a2aea358ca3bcec60c878a6399df46390e3e1 Mon Sep 17 00:00:00 2001 From: syuilo <syuilotan@yahoo.co.jp> Date: Sat, 24 Feb 2018 02:46:09 +0900 Subject: [PATCH] Implement #1098 --- src/api/endpoints.ts | 7 +- src/api/endpoints/i/update_home.ts | 11 +- src/api/endpoints/i/update_mobile_home.ts | 50 +++++ src/web/app/common/define-widget.ts | 26 ++- .../app/common/scripts/check-for-update.ts | 4 +- src/web/app/common/views/components/index.ts | 30 +++ .../views/components/widgets/access-log.vue | 70 +++---- .../views/components/widgets/broadcast.vue | 10 +- .../views/components/widgets/calendar.vue | 7 + .../views/components/widgets/donation.vue | 15 +- .../views/components/widgets/nav.vue | 12 +- .../views/components/widgets/photo-stream.vue | 104 +++++++++++ .../views/components/widgets/profile.vue | 0 .../common/views/components/widgets/rss.vue | 93 ++++++++++ .../components/widgets/server.cpu-memory.vue | 0 .../views/components/widgets/server.cpu.vue | 0 .../views/components/widgets/server.disk.vue | 0 .../views/components/widgets/server.info.vue | 0 .../components/widgets/server.memory.vue | 0 .../views/components/widgets/server.pie.vue | 0 .../components/widgets/server.uptimes.vue | 0 .../views/components/widgets/server.vue | 93 ++++++++++ .../views/components/widgets/slideshow.vue | 0 .../views/components/widgets/tips.vue | 0 .../views/components/widgets/version.vue | 0 src/web/app/desktop/views/components/index.ts | 32 +--- .../views/components/widget-container.vue | 72 ++++++++ .../views/components/widgets/photo-stream.vue | 122 ------------ .../desktop/views/components/widgets/rss.vue | 111 ----------- .../views/components/widgets/server.vue | 131 ------------- .../activity.vue} | 4 +- src/web/app/mobile/views/components/home.vue | 29 --- src/web/app/mobile/views/components/index.ts | 14 +- .../app/mobile/views/components/ui.header.vue | 4 +- src/web/app/mobile/views/components/ui.vue | 6 +- .../views/components/widget-container.vue | 65 +++++++ .../views/components/widgets/activity.vue | 23 +++ src/web/app/mobile/views/pages/drive.vue | 4 +- src/web/app/mobile/views/pages/home.vue | 174 +++++++++++++++++- .../app/mobile/views/pages/notifications.vue | 4 +- src/web/app/mobile/views/pages/user.vue | 1 - src/web/app/mobile/views/pages/user/home.vue | 6 +- 42 files changed, 823 insertions(+), 511 deletions(-) create mode 100644 src/api/endpoints/i/update_mobile_home.ts rename src/web/app/{desktop => common}/views/components/widgets/access-log.vue (61%) rename src/web/app/{desktop => common}/views/components/widgets/broadcast.vue (96%) rename src/web/app/{desktop => common}/views/components/widgets/calendar.vue (96%) rename src/web/app/{desktop => common}/views/components/widgets/donation.vue (79%) rename src/web/app/{desktop => common}/views/components/widgets/nav.vue (67%) create mode 100644 src/web/app/common/views/components/widgets/photo-stream.vue rename src/web/app/{desktop => common}/views/components/widgets/profile.vue (100%) create mode 100644 src/web/app/common/views/components/widgets/rss.vue rename src/web/app/{desktop => common}/views/components/widgets/server.cpu-memory.vue (100%) rename src/web/app/{desktop => common}/views/components/widgets/server.cpu.vue (100%) rename src/web/app/{desktop => common}/views/components/widgets/server.disk.vue (100%) rename src/web/app/{desktop => common}/views/components/widgets/server.info.vue (100%) rename src/web/app/{desktop => common}/views/components/widgets/server.memory.vue (100%) rename src/web/app/{desktop => common}/views/components/widgets/server.pie.vue (100%) rename src/web/app/{desktop => common}/views/components/widgets/server.uptimes.vue (100%) create mode 100644 src/web/app/common/views/components/widgets/server.vue rename src/web/app/{desktop => common}/views/components/widgets/slideshow.vue (100%) rename src/web/app/{desktop => common}/views/components/widgets/tips.vue (100%) rename src/web/app/{desktop => common}/views/components/widgets/version.vue (100%) create mode 100644 src/web/app/desktop/views/components/widget-container.vue delete mode 100644 src/web/app/desktop/views/components/widgets/photo-stream.vue delete mode 100644 src/web/app/desktop/views/components/widgets/rss.vue delete mode 100644 src/web/app/desktop/views/components/widgets/server.vue rename src/web/app/mobile/views/{pages/user/home.activity.vue => components/activity.vue} (96%) delete mode 100644 src/web/app/mobile/views/components/home.vue create mode 100644 src/web/app/mobile/views/components/widget-container.vue create mode 100644 src/web/app/mobile/views/components/widgets/activity.vue diff --git a/src/api/endpoints.ts b/src/api/endpoints.ts index ff214c3004..cbc016f20f 100644 --- a/src/api/endpoints.ts +++ b/src/api/endpoints.ts @@ -182,7 +182,12 @@ const endpoints: Endpoint[] = [ { name: 'i/update_home', withCredential: true, - kind: 'account-write' + secure: true + }, + { + name: 'i/update_mobile_home', + withCredential: true, + secure: true }, { name: 'i/change_password', diff --git a/src/api/endpoints/i/update_home.ts b/src/api/endpoints/i/update_home.ts index 429e88529a..5dfb7d7915 100644 --- a/src/api/endpoints/i/update_home.ts +++ b/src/api/endpoints/i/update_home.ts @@ -4,16 +4,7 @@ import $ from 'cafy'; import User from '../../models/user'; -/** - * Update myself - * - * @param {any} params - * @param {any} user - * @param {any} _ - * @param {boolean} isSecure - * @return {Promise<any>} - */ -module.exports = async (params, user, _, isSecure) => new Promise(async (res, rej) => { +module.exports = async (params, user) => new Promise(async (res, rej) => { // Get 'home' parameter const [home, homeErr] = $(params.home).optional.array().each( $().strict.object() diff --git a/src/api/endpoints/i/update_mobile_home.ts b/src/api/endpoints/i/update_mobile_home.ts new file mode 100644 index 0000000000..a87d89cad7 --- /dev/null +++ b/src/api/endpoints/i/update_mobile_home.ts @@ -0,0 +1,50 @@ +/** + * Module dependencies + */ +import $ from 'cafy'; +import User from '../../models/user'; + +module.exports = async (params, user) => new Promise(async (res, rej) => { + // Get 'home' parameter + const [home, homeErr] = $(params.home).optional.array().each( + $().strict.object() + .have('name', $().string()) + .have('id', $().string()) + .have('data', $().object())).$; + if (homeErr) return rej('invalid home param'); + + // Get 'id' parameter + const [id, idErr] = $(params.id).optional.string().$; + if (idErr) return rej('invalid id param'); + + // Get 'data' parameter + const [data, dataErr] = $(params.data).optional.object().$; + if (dataErr) return rej('invalid data param'); + + if (home) { + await User.update(user._id, { + $set: { + 'client_settings.mobile_home': home + } + }); + + res(); + } else { + if (id == null && data == null) return rej('you need to set id and data params if home param unset'); + + const _home = user.client_settings.mobile_home || []; + const widget = _home.find(w => w.id == id); + + if (widget == null) return rej('widget not found'); + + widget.data = data; + + await User.update(user._id, { + $set: { + 'client_settings.mobile_home': _home + } + }); + + res(); + } +}); diff --git a/src/web/app/common/define-widget.ts b/src/web/app/common/define-widget.ts index fd13a3395b..60cd1969c0 100644 --- a/src/web/app/common/define-widget.ts +++ b/src/web/app/common/define-widget.ts @@ -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 }); diff --git a/src/web/app/common/scripts/check-for-update.ts b/src/web/app/common/scripts/check-for-update.ts index 0855676a42..fe539407da 100644 --- a/src/web/app/common/scripts/check-for-update.ts +++ b/src/web/app/common/scripts/check-for-update.ts @@ -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()); diff --git a/src/web/app/common/views/components/index.ts b/src/web/app/common/views/components/index.ts index ab0f1767d4..e66a323266 100644 --- a/src/web/app/common/views/components/index.ts +++ b/src/web/app/common/views/components/index.ts @@ -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 diff --git a/src/web/app/desktop/views/components/widgets/access-log.vue b/src/web/app/common/views/components/widgets/access-log.vue similarity index 61% rename from src/web/app/desktop/views/components/widgets/access-log.vue rename to src/web/app/common/views/components/widgets/access-log.vue index a04da1daaf..c810c2d157 100644 --- a/src/web/app/desktop/views/components/widgets/access-log.vue +++ b/src/web/app/common/views/components/widgets/access-log.vue @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/broadcast.vue b/src/web/app/common/views/components/widgets/broadcast.vue similarity index 96% rename from src/web/app/desktop/views/components/widgets/broadcast.vue rename to src/web/app/common/views/components/widgets/broadcast.vue index e4b7e25321..0bb59caf43 100644 --- a/src/web/app/desktop/views/components/widgets/broadcast.vue +++ b/src/web/app/common/views/components/widgets/broadcast.vue @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/calendar.vue b/src/web/app/common/views/components/widgets/calendar.vue similarity index 96% rename from src/web/app/desktop/views/components/widgets/calendar.vue rename to src/web/app/common/views/components/widgets/calendar.vue index c16602db46..bfcbd7f68d 100644 --- a/src/web/app/desktop/views/components/widgets/calendar.vue +++ b/src/web/app/common/views/components/widgets/calendar.vue @@ -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 diff --git a/src/web/app/desktop/views/components/widgets/donation.vue b/src/web/app/common/views/components/widgets/donation.vue similarity index 79% rename from src/web/app/desktop/views/components/widgets/donation.vue rename to src/web/app/common/views/components/widgets/donation.vue index fbab0fca6c..08aab8ecd1 100644 --- a/src/web/app/desktop/views/components/widgets/donation.vue +++ b/src/web/app/common/views/components/widgets/donation.vue @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/nav.vue b/src/web/app/common/views/components/widgets/nav.vue similarity index 67% rename from src/web/app/desktop/views/components/widgets/nav.vue rename to src/web/app/common/views/components/widgets/nav.vue index 5e04c266cf..ce88e587a8 100644 --- a/src/web/app/desktop/views/components/widgets/nav.vue +++ b/src/web/app/common/views/components/widgets/nav.vue @@ -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 diff --git a/src/web/app/common/views/components/widgets/photo-stream.vue b/src/web/app/common/views/components/widgets/photo-stream.vue new file mode 100644 index 0000000000..dcaa6624dd --- /dev/null +++ b/src/web/app/common/views/components/widgets/photo-stream.vue @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/profile.vue b/src/web/app/common/views/components/widgets/profile.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/profile.vue rename to src/web/app/common/views/components/widgets/profile.vue diff --git a/src/web/app/common/views/components/widgets/rss.vue b/src/web/app/common/views/components/widgets/rss.vue new file mode 100644 index 0000000000..e80896bea6 --- /dev/null +++ b/src/web/app/common/views/components/widgets/rss.vue @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/server.cpu-memory.vue b/src/web/app/common/views/components/widgets/server.cpu-memory.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/server.cpu-memory.vue rename to src/web/app/common/views/components/widgets/server.cpu-memory.vue diff --git a/src/web/app/desktop/views/components/widgets/server.cpu.vue b/src/web/app/common/views/components/widgets/server.cpu.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/server.cpu.vue rename to src/web/app/common/views/components/widgets/server.cpu.vue diff --git a/src/web/app/desktop/views/components/widgets/server.disk.vue b/src/web/app/common/views/components/widgets/server.disk.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/server.disk.vue rename to src/web/app/common/views/components/widgets/server.disk.vue diff --git a/src/web/app/desktop/views/components/widgets/server.info.vue b/src/web/app/common/views/components/widgets/server.info.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/server.info.vue rename to src/web/app/common/views/components/widgets/server.info.vue diff --git a/src/web/app/desktop/views/components/widgets/server.memory.vue b/src/web/app/common/views/components/widgets/server.memory.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/server.memory.vue rename to src/web/app/common/views/components/widgets/server.memory.vue diff --git a/src/web/app/desktop/views/components/widgets/server.pie.vue b/src/web/app/common/views/components/widgets/server.pie.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/server.pie.vue rename to src/web/app/common/views/components/widgets/server.pie.vue diff --git a/src/web/app/desktop/views/components/widgets/server.uptimes.vue b/src/web/app/common/views/components/widgets/server.uptimes.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/server.uptimes.vue rename to src/web/app/common/views/components/widgets/server.uptimes.vue diff --git a/src/web/app/common/views/components/widgets/server.vue b/src/web/app/common/views/components/widgets/server.vue new file mode 100644 index 0000000000..4ebc5767d6 --- /dev/null +++ b/src/web/app/common/views/components/widgets/server.vue @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/slideshow.vue b/src/web/app/common/views/components/widgets/slideshow.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/slideshow.vue rename to src/web/app/common/views/components/widgets/slideshow.vue diff --git a/src/web/app/desktop/views/components/widgets/tips.vue b/src/web/app/common/views/components/widgets/tips.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/tips.vue rename to src/web/app/common/views/components/widgets/tips.vue diff --git a/src/web/app/desktop/views/components/widgets/version.vue b/src/web/app/common/views/components/widgets/version.vue similarity index 100% rename from src/web/app/desktop/views/components/widgets/version.vue rename to src/web/app/common/views/components/widgets/version.vue diff --git a/src/web/app/desktop/views/components/index.ts b/src/web/app/desktop/views/components/index.ts index da59d9219e..7584cb4983 100644 --- a/src/web/app/desktop/views/components/index.ts +++ b/src/web/app/desktop/views/components/index.ts @@ -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 diff --git a/src/web/app/desktop/views/components/widget-container.vue b/src/web/app/desktop/views/components/widget-container.vue new file mode 100644 index 0000000000..7b4e1f55f0 --- /dev/null +++ b/src/web/app/desktop/views/components/widget-container.vue @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/photo-stream.vue b/src/web/app/desktop/views/components/widgets/photo-stream.vue deleted file mode 100644 index 04b71975b3..0000000000 --- a/src/web/app/desktop/views/components/widgets/photo-stream.vue +++ /dev/null @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/rss.vue b/src/web/app/desktop/views/components/widgets/rss.vue deleted file mode 100644 index 3507129716..0000000000 --- a/src/web/app/desktop/views/components/widgets/rss.vue +++ /dev/null @@ -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> diff --git a/src/web/app/desktop/views/components/widgets/server.vue b/src/web/app/desktop/views/components/widgets/server.vue deleted file mode 100644 index 1c0da84225..0000000000 --- a/src/web/app/desktop/views/components/widgets/server.vue +++ /dev/null @@ -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> diff --git a/src/web/app/mobile/views/pages/user/home.activity.vue b/src/web/app/mobile/views/components/activity.vue similarity index 96% rename from src/web/app/mobile/views/pages/user/home.activity.vue rename to src/web/app/mobile/views/components/activity.vue index 87970795b2..b50044b3de 100644 --- a/src/web/app/mobile/views/pages/user/home.activity.vue +++ b/src/web/app/mobile/views/components/activity.vue @@ -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 diff --git a/src/web/app/mobile/views/components/home.vue b/src/web/app/mobile/views/components/home.vue deleted file mode 100644 index 3feab581d2..0000000000 --- a/src/web/app/mobile/views/components/home.vue +++ /dev/null @@ -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> diff --git a/src/web/app/mobile/views/components/index.ts b/src/web/app/mobile/views/components/index.ts index 905baaf20d..d372f22332 100644 --- a/src/web/app/mobile/views/components/index.ts +++ b/src/web/app/mobile/views/components/index.ts @@ -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 diff --git a/src/web/app/mobile/views/components/ui.header.vue b/src/web/app/mobile/views/components/ui.header.vue index 2df5ea162e..026e7eb1b4 100644 --- a/src/web/app/mobile/views/components/ui.header.vue +++ b/src/web/app/mobile/views/components/ui.header.vue @@ -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> diff --git a/src/web/app/mobile/views/components/ui.vue b/src/web/app/mobile/views/components/ui.vue index 91d7ea29b6..325ce9d40e 100644 --- a/src/web/app/mobile/views/components/ui.vue +++ b/src/web/app/mobile/views/components/ui.vue @@ -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, diff --git a/src/web/app/mobile/views/components/widget-container.vue b/src/web/app/mobile/views/components/widget-container.vue new file mode 100644 index 0000000000..1775188a93 --- /dev/null +++ b/src/web/app/mobile/views/components/widget-container.vue @@ -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> diff --git a/src/web/app/mobile/views/components/widgets/activity.vue b/src/web/app/mobile/views/components/widgets/activity.vue new file mode 100644 index 0000000000..c3fe63f264 --- /dev/null +++ b/src/web/app/mobile/views/components/widgets/activity.vue @@ -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> diff --git a/src/web/app/mobile/views/pages/drive.vue b/src/web/app/mobile/views/pages/drive.vue index 47aeb52f49..ea61661cf6 100644 --- a/src/web/app/mobile/views/pages/drive.vue +++ b/src/web/app/mobile/views/pages/drive.vue @@ -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" diff --git a/src/web/app/mobile/views/pages/home.vue b/src/web/app/mobile/views/pages/home.vue index c81cbcadb3..0466fbbbf8 100644 --- a/src/web/app/mobile/views/pages/home.vue +++ b/src/web/app/mobile/views/pages/home.vue @@ -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> diff --git a/src/web/app/mobile/views/pages/notifications.vue b/src/web/app/mobile/views/pages/notifications.vue index b1243dbc74..3dcfb2f38c 100644 --- a/src/web/app/mobile/views/pages/notifications.vue +++ b/src/web/app/mobile/views/pages/notifications.vue @@ -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> diff --git a/src/web/app/mobile/views/pages/user.vue b/src/web/app/mobile/views/pages/user.vue index 27f65e623d..378beeaf13 100644 --- a/src/web/app/mobile/views/pages/user.vue +++ b/src/web/app/mobile/views/pages/user.vue @@ -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> diff --git a/src/web/app/mobile/views/pages/user/home.vue b/src/web/app/mobile/views/pages/user/home.vue index 4c68317879..fdbfd1bf55 100644 --- a/src/web/app/mobile/views/pages/user/home.vue +++ b/src/web/app/mobile/views/pages/user/home.vue @@ -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'] });