diff --git a/src/web/app/common/define-widget.ts b/src/web/app/common/define-widget.ts index 5102ee1abf..782a69a624 100644 --- a/src/web/app/common/define-widget.ts +++ b/src/web/app/common/define-widget.ts @@ -2,7 +2,7 @@ import Vue from 'vue'; export default function<T extends object>(data: { name: string; - props: T; + props?: T; }) { return Vue.extend({ props: { @@ -26,7 +26,7 @@ export default function<T extends object>(data: { }, data() { return { - props: data.props + props: data.props || {} }; }, watch: { diff --git a/src/web/app/common/views/components/widgets/calendar.vue b/src/web/app/common/views/components/widgets/calendar.vue new file mode 100644 index 0000000000..308f43cd99 --- /dev/null +++ b/src/web/app/common/views/components/widgets/calendar.vue @@ -0,0 +1,192 @@ +<template> +<div class="mkw-calendar" + :data-melt="props.design == 1" + :data-special="special" +> + <div class="calendar" :data-is-holiday="isHoliday"> + <p class="month-and-year"> + <span class="year">{{ year }}年</span> + <span class="month">{{ month }}月</span> + </p> + <p class="day">{{ day }}日</p> + <p class="week-day">{{ weekDay }}曜日</p> + </div> + <div class="info"> + <div> + <p>今日:<b>{{ dayP.toFixed(1) }}%</b></p> + <div class="meter"> + <div class="val" :style="{ width: `${dayP}%` }"></div> + </div> + </div> + <div> + <p>今月:<b>{{ monthP.toFixed(1) }}%</b></p> + <div class="meter"> + <div class="val" :style="{ width: `${monthP}%` }"></div> + </div> + </div> + <div> + <p>今年:<b>{{ yearP.toFixed(1) }}%</b></p> + <div class="meter"> + <div class="val" :style="{ width: `${yearP}%` }"></div> + </div> + </div> + </div> +</div> +</template> + +<script lang="ts"> +import define from '../../../define-widget'; +export default define({ + name: 'calendar', + props: { + design: 0 + } +}).extend({ + data() { + return { + now: new Date(), + year: null, + month: null, + day: null, + weekDay: null, + yearP: null, + dayP: null, + monthP: null, + isHoliday: null, + special: null, + clock: null + }; + }, + created() { + this.tick(); + this.clock = setInterval(this.tick, 1000); + }, + beforeDestroy() { + clearInterval(this.clock); + }, + methods: { + func() { + if (this.props.design == 2) { + this.props.design = 0; + } else { + this.props.design++; + } + }, + tick() { + const now = new Date(); + const nd = now.getDate(); + const nm = now.getMonth(); + const ny = now.getFullYear(); + + this.year = ny; + this.month = nm + 1; + this.day = nd; + this.weekDay = ['日', '月', '火', '水', '木', '金', '土'][now.getDay()]; + + const dayNumer = now.getTime() - new Date(ny, nm, nd).getTime(); + const dayDenom = 1000/*ms*/ * 60/*s*/ * 60/*m*/ * 24/*h*/; + const monthNumer = now.getTime() - new Date(ny, nm, 1).getTime(); + const monthDenom = new Date(ny, nm + 1, 1).getTime() - new Date(ny, nm, 1).getTime(); + const yearNumer = now.getTime() - new Date(ny, 0, 1).getTime(); + const yearDenom = new Date(ny + 1, 0, 1).getTime() - new Date(ny, 0, 1).getTime(); + + this.dayP = dayNumer / dayDenom * 100; + this.monthP = monthNumer / monthDenom * 100; + this.yearP = yearNumer / yearDenom * 100; + + this.isHoliday = now.getDay() == 0 || now.getDay() == 6; + + this.special = + nm == 0 && nd == 1 ? 'on-new-years-day' : + false; + } + } +}); +</script> + +<style lang="stylus" scoped> +.mkw-calendar + padding 16px 0 + color #777 + background #fff + border solid 1px rgba(0, 0, 0, 0.075) + border-radius 6px + + &[data-special='on-new-years-day'] + border-color #ef95a0 + + &[data-melt] + background transparent + border none + + &:after + content "" + display block + clear both + + > .calendar + float left + width 60% + text-align center + + &[data-is-holiday] + > .day + color #ef95a0 + + > p + margin 0 + line-height 18px + font-size 14px + + > span + margin 0 4px + + > .day + margin 10px 0 + line-height 32px + font-size 28px + + > .info + display block + float left + width 40% + padding 0 16px 0 0 + + > div + margin-bottom 8px + + &:last-child + margin-bottom 4px + + > p + margin 0 0 2px 0 + font-size 12px + line-height 18px + color #888 + + > b + margin-left 2px + + > .meter + width 100% + overflow hidden + background #eee + border-radius 8px + + > .val + height 4px + background $theme-color + + &:nth-child(1) + > .meter > .val + background #f7796c + + &:nth-child(2) + > .meter > .val + background #a1de41 + + &:nth-child(3) + > .meter > .val + background #41ddde + +</style> diff --git a/src/web/app/common/views/components/widgets/donation.vue b/src/web/app/common/views/components/widgets/donation.vue new file mode 100644 index 0000000000..50adc531bf --- /dev/null +++ b/src/web/app/common/views/components/widgets/donation.vue @@ -0,0 +1,45 @@ +<template> +<div class="mkw-donation"> + <article> + <h1>%fa:heart%%i18n:desktop.tags.mk-donation-home-widget.title%</h1> + <p> + {{ '%i18n:desktop.tags.mk-donation-home-widget.text%'.substr(0, '%i18n:desktop.tags.mk-donation-home-widget.text%'.indexOf('{')) }} + <a href="/syuilo" data-user-preview="@syuilo">@syuilo</a> + {{ '%i18n:desktop.tags.mk-donation-home-widget.text%'.substr('%i18n:desktop.tags.mk-donation-home-widget.text%'.indexOf('}') + 1) }} + </p> + </article> +</div> +</template> + +<script lang="ts"> +import define from '../../../define-widget'; +export default define({ + name: 'donation' +}); +</script> + +<style lang="stylus" scoped> +.mkw-donation + background #fff + border solid 1px #ead8bb + border-radius 6px + + > article + padding 20px + + > h1 + margin 0 0 5px 0 + font-size 1em + color #888 + + > [data-fa] + margin-right 0.25em + + > p + display block + z-index 1 + margin 0 + font-size 0.8em + color #999 + +</style> diff --git a/src/web/app/common/views/components/widgets/messaging.vue b/src/web/app/common/views/components/widgets/messaging.vue new file mode 100644 index 0000000000..19ef704310 --- /dev/null +++ b/src/web/app/common/views/components/widgets/messaging.vue @@ -0,0 +1,59 @@ +<template> +<div class="mkw-messaging"> + <p class="title" v-if="props.design == 0">%fa:comments%%i18n:desktop.tags.mk-messaging-home-widget.title%</p> + <mk-messaging ref="index" compact @navigate="navigate"/> +</div> +</template> + +<script lang="ts"> +import define from '../../../define-widget'; +export default define({ + name: 'messaging', + props: { + design: 0 + } +}).extend({ + methods: { + navigate(user) { + if (this.platform == 'desktop') { + this.wapi_openMessagingRoomWindow(user); + } else { + // TODO: open room page in new tab + } + }, + func() { + if (this.props.design == 1) { + this.props.design = 0; + } else { + this.props.design++; + } + } + } +}); +</script> + +<style lang="stylus" scoped> +.mkw-messaging + overflow hidden + background #fff + border solid 1px rgba(0, 0, 0, 0.075) + border-radius 6px + + > .title + z-index 2 + 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 + + > mk-messaging + max-height 250px + overflow auto + +</style> diff --git a/src/web/app/common/views/components/widgets/nav.vue b/src/web/app/common/views/components/widgets/nav.vue new file mode 100644 index 0000000000..77e1eea492 --- /dev/null +++ b/src/web/app/common/views/components/widgets/nav.vue @@ -0,0 +1,29 @@ +<template> +<div class="mkw-nav"> + <mk-nav-links/> +</div> +</template> + +<script lang="ts"> +import define from '../../../define-widget'; +export default define({ + name: 'nav' +}); +</script> + +<style lang="stylus" scoped> +.mkw-nav + padding 16px + font-size 12px + color #aaa + background #fff + border solid 1px rgba(0, 0, 0, 0.075) + border-radius 6px + + a + color #999 + + i + color #ccc + +</style> 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..12e568ca00 --- /dev/null +++ b/src/web/app/common/views/components/widgets/photo-stream.vue @@ -0,0 +1,122 @@ +<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 '../../../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.$root.$data.os.stream.getConnection(); + this.connectionId = this.$root.$data.os.stream.use(); + + this.connection.on('drive_file_created', this.onDriveFileCreated); + + this.$root.$data.os.api('drive/stream', { + type: 'image/*', + limit: 9 + }).then(images => { + this.fetching = false; + this.images = images; + }); + }, + beforeDestroy() { + this.connection.off('drive_file_created', this.onDriveFileCreated); + this.$root.$data.os.stream.dispose(this.connectionId); + }, + methods: { + onStreamDriveFileCreated(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/common/views/components/widgets/profile.vue b/src/web/app/common/views/components/widgets/profile.vue index e589eb20b9..70902c7cf5 100644 --- a/src/web/app/common/views/components/widgets/profile.vue +++ b/src/web/app/common/views/components/widgets/profile.vue @@ -1,7 +1,7 @@ <template> <div class="mkw-profile" - data-compact={ data.design == 1 || data.design == 2 } - data-melt={ data.design == 2 } + :data-compact="props.design == 1 || props.design == 2" + :data-melt="props.design == 2" > <div class="banner" style={ I.banner_url ? 'background-image: url(' + I.banner_url + '?thumbnail&size=256)' : '' } diff --git a/src/web/app/common/views/components/widgets/slideshow.vue b/src/web/app/common/views/components/widgets/slideshow.vue new file mode 100644 index 0000000000..6dcd453e25 --- /dev/null +++ b/src/web/app/common/views/components/widgets/slideshow.vue @@ -0,0 +1,154 @@ +<template> +<div class="mkw-slideshow"> + <div @click="choose"> + <p v-if="data.folder === undefined">クリックしてフォルダを指定してください</p> + <p v-if="data.folder !== undefined && images.length == 0 && !fetching">このフォルダには画像がありません</p> + <div ref="slideA" class="slide a"></div> + <div ref="slideB" class="slide b"></div> + </div> + <button @click="resize">%fa:expand%</button> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import * as anime from 'animejs'; +import define from '../../../define-widget'; +export default define({ + name: 'slideshow', + props: { + folder: undefined, + size: 0 + } +}).extend({ + data() { + return { + images: [], + fetching: true, + clock: null + }; + }, + mounted() { + Vue.nextTick(() => { + this.applySize(); + }); + + if (this.props.folder !== undefined) { + this.fetch(); + } + + this.clock = setInterval(this.change, 10000); + }, + beforeDestroy() { + clearInterval(this.clock); + }, + methods: { + applySize() { + let h; + + if (this.props.size == 1) { + h = 250; + } else { + h = 170; + } + + this.$el.style.height = `${h}px`; + }, + resize() { + if (this.props.size == 1) { + this.props.size = 0; + } else { + this.props.size++; + } + + this.applySize(); + }, + change() { + if (this.images.length == 0) return; + + const index = Math.floor(Math.random() * this.images.length); + const img = `url(${ this.images[index].url }?thumbnail&size=1024)`; + + (this.$refs.slideB as any).style.backgroundImage = img; + + anime({ + targets: this.$refs.slideB, + opacity: 1, + duration: 1000, + easing: 'linear', + complete: () => { + (this.$refs.slideA as any).style.backgroundImage = img; + anime({ + targets: this.$refs.slideB, + opacity: 0, + duration: 0 + }); + } + }); + }, + fetch() { + this.fetching = true; + + this.$root.$data.os.api('drive/files', { + folder_id: this.props.folder, + type: 'image/*', + limit: 100 + }).then(images => { + this.fetching = false; + this.images = images; + (this.$refs.slideA as any).style.backgroundImage = ''; + (this.$refs.slideB as any).style.backgroundImage = ''; + this.change(); + }); + }, + choose() { + this.wapi_selectDriveFolder().then(folder => { + this.props.folder = folder ? folder.id : null; + this.fetch(); + }); + } + } +}); +</script> + +<style lang="stylus" scoped> +.mkw-slideshow + overflow hidden + background #fff + border solid 1px rgba(0, 0, 0, 0.075) + border-radius 6px + + &:hover > button + display block + + > button + position absolute + left 0 + bottom 0 + display none + padding 4px + font-size 24px + color #fff + text-shadow 0 0 8px #000 + + > div + width 100% + height 100% + cursor pointer + + > * + pointer-events none + + > .slide + position absolute + top 0 + left 0 + width 100% + height 100% + background-size cover + background-position center + + &.b + opacity 0 + +</style> diff --git a/src/web/app/common/views/components/widgets/tips.vue b/src/web/app/common/views/components/widgets/tips.vue new file mode 100644 index 0000000000..f38ecfe441 --- /dev/null +++ b/src/web/app/common/views/components/widgets/tips.vue @@ -0,0 +1,109 @@ +<template> +<div class="mkw-tips"> + <p ref="tip">%fa:R lightbulb%<span v-html="tip"></span></p> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import * as anime from 'animejs'; +import define from '../../../define-widget'; + +const tips = [ + '<kbd>t</kbd>でタイムラインにフォーカスできます', + '<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます', + '投稿フォームにはファイルをドラッグ&ドロップできます', + '投稿フォームにクリップボードにある画像データをペーストできます', + 'ドライブにファイルをドラッグ&ドロップしてアップロードできます', + 'ドライブでファイルをドラッグしてフォルダ移動できます', + 'ドライブでフォルダをドラッグしてフォルダ移動できます', + 'ホームは設定からカスタマイズできます', + 'MisskeyはMIT Licenseです', + 'タイムマシンウィジェットを利用すると、簡単に過去のタイムラインに遡れます', + '投稿の ... をクリックして、投稿をユーザーページにピン留めできます', + 'ドライブの容量は(デフォルトで)1GBです', + '投稿に添付したファイルは全てドライブに保存されます', + 'ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます', + 'タイムライン上部にもウィジェットを設置できます', + '投稿をダブルクリックすると詳細が見れます', + '「**」でテキストを囲むと**強調表示**されます', + 'チャンネルウィジェットを利用すると、よく利用するチャンネルを素早く確認できます', + 'いくつかのウィンドウはブラウザの外に切り離すことができます', + 'カレンダーウィジェットのパーセンテージは、経過の割合を示しています', + 'APIを利用してbotの開発なども行えます', + 'MisskeyはLINEを通じてでも利用できます', + 'まゆかわいいよまゆ', + 'Misskeyは2014年にサービスを開始しました', + '対応ブラウザではMisskeyを開いていなくても通知を受け取れます' +] + +export default define({ + name: 'tips' +}).extend({ + data() { + return { + tip: null, + clock: null + }; + }, + mounted() { + Vue.nextTick(() => { + this.set(); + }); + + this.clock = setInterval(this.change, 20000); + }, + beforeDestroy() { + clearInterval(this.clock); + }, + methods: { + set() { + this.tip = tips[Math.floor(Math.random() * tips.length)]; + }, + change() { + anime({ + targets: this.$refs.tip, + opacity: 0, + duration: 500, + easing: 'linear', + complete: this.set + }); + + setTimeout(() => { + anime({ + targets: this.$refs.tip, + opacity: 1, + duration: 500, + easing: 'linear' + }); + }, 500); + } + } +}); +</script> + +<style lang="stylus" scoped> +.mkw-tips + overflow visible !important + + > p + display block + margin 0 + padding 0 12px + text-align center + font-size 0.7em + color #999 + + > [data-fa] + margin-right 4px + + kbd + display inline + padding 0 6px + margin 0 2px + font-size 1em + font-family inherit + border solid 1px #999 + border-radius 2px + +</style> diff --git a/src/web/app/desktop/-tags/home-widgets/calendar.tag b/src/web/app/desktop/-tags/home-widgets/calendar.tag deleted file mode 100644 index 46d47662b9..0000000000 --- a/src/web/app/desktop/-tags/home-widgets/calendar.tag +++ /dev/null @@ -1,167 +0,0 @@ -<mk-calendar-home-widget data-melt={ data.design == 1 } data-special={ special }> - <div class="calendar" data-is-holiday={ isHoliday }> - <p class="month-and-year"><span class="year">{ year }年</span><span class="month">{ month }月</span></p> - <p class="day">{ day }日</p> - <p class="week-day">{ weekDay }曜日</p> - </div> - <div class="info"> - <div> - <p>今日:<b>{ dayP.toFixed(1) }%</b></p> - <div class="meter"> - <div class="val" style={ 'width:' + dayP + '%' }></div> - </div> - </div> - <div> - <p>今月:<b>{ monthP.toFixed(1) }%</b></p> - <div class="meter"> - <div class="val" style={ 'width:' + monthP + '%' }></div> - </div> - </div> - <div> - <p>今年:<b>{ yearP.toFixed(1) }%</b></p> - <div class="meter"> - <div class="val" style={ 'width:' + yearP + '%' }></div> - </div> - </div> - </div> - <style lang="stylus" scoped> - :scope - display block - padding 16px 0 - color #777 - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &[data-special='on-new-years-day'] - border-color #ef95a0 - - &[data-melt] - background transparent - border none - - &:after - content "" - display block - clear both - - > .calendar - float left - width 60% - text-align center - - &[data-is-holiday] - > .day - color #ef95a0 - - > p - margin 0 - line-height 18px - font-size 14px - - > span - margin 0 4px - - > .day - margin 10px 0 - line-height 32px - font-size 28px - - > .info - display block - float left - width 40% - padding 0 16px 0 0 - - > div - margin-bottom 8px - - &:last-child - margin-bottom 4px - - > p - margin 0 0 2px 0 - font-size 12px - line-height 18px - color #888 - - > b - margin-left 2px - - > .meter - width 100% - overflow hidden - background #eee - border-radius 8px - - > .val - height 4px - background $theme-color - - &:nth-child(1) - > .meter > .val - background #f7796c - - &:nth-child(2) - > .meter > .val - background #a1de41 - - &:nth-child(3) - > .meter > .val - background #41ddde - - </style> - <script lang="typescript"> - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.draw = () => { - const now = new Date(); - const nd = now.getDate(); - const nm = now.getMonth(); - const ny = now.getFullYear(); - - this.year = ny; - this.month = nm + 1; - this.day = nd; - this.weekDay = ['日', '月', '火', '水', '木', '金', '土'][now.getDay()]; - - this.dayNumer = now - new Date(ny, nm, nd); - this.dayDenom = 1000/*ms*/ * 60/*s*/ * 60/*m*/ * 24/*h*/; - this.monthNumer = now - new Date(ny, nm, 1); - this.monthDenom = new Date(ny, nm + 1, 1) - new Date(ny, nm, 1); - this.yearNumer = now - new Date(ny, 0, 1); - this.yearDenom = new Date(ny + 1, 0, 1) - new Date(ny, 0, 1); - - this.dayP = this.dayNumer / this.dayDenom * 100; - this.monthP = this.monthNumer / this.monthDenom * 100; - this.yearP = this.yearNumer / this.yearDenom * 100; - - this.isHoliday = now.getDay() == 0 || now.getDay() == 6; - - this.special = - nm == 0 && nd == 1 ? 'on-new-years-day' : - false; - - this.update(); - }; - - this.draw(); - - this.on('mount', () => { - this.clock = setInterval(this.draw, 1000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - - this.func = () => { - if (++this.data.design == 2) this.data.design = 0; - this.save(); - }; - </script> -</mk-calendar-home-widget> diff --git a/src/web/app/desktop/-tags/home-widgets/donation.tag b/src/web/app/desktop/-tags/home-widgets/donation.tag deleted file mode 100644 index 5ed5c137b5..0000000000 --- a/src/web/app/desktop/-tags/home-widgets/donation.tag +++ /dev/null @@ -1,36 +0,0 @@ -<mk-donation-home-widget> - <article> - <h1>%fa:heart%%i18n:desktop.tags.mk-donation-home-widget.title%</h1> - <p>{'%i18n:desktop.tags.mk-donation-home-widget.text%'.substr(0, '%i18n:desktop.tags.mk-donation-home-widget.text%'.indexOf('{'))}<a href="/syuilo" data-user-preview="@syuilo">@syuilo</a>{'%i18n:desktop.tags.mk-donation-home-widget.text%'.substr('%i18n:desktop.tags.mk-donation-home-widget.text%'.indexOf('}') + 1)}</p> - </article> - <style lang="stylus" scoped> - :scope - display block - background #fff - border solid 1px #ead8bb - border-radius 6px - - > article - padding 20px - - > h1 - margin 0 0 5px 0 - font-size 1em - color #888 - - > [data-fa] - margin-right 0.25em - - > p - display block - z-index 1 - margin 0 - font-size 0.8em - color #999 - - </style> - <script lang="typescript"> - this.mixin('widget'); - this.mixin('user-preview'); - </script> -</mk-donation-home-widget> diff --git a/src/web/app/desktop/-tags/home-widgets/messaging.tag b/src/web/app/desktop/-tags/home-widgets/messaging.tag deleted file mode 100644 index d3b77b58cc..0000000000 --- a/src/web/app/desktop/-tags/home-widgets/messaging.tag +++ /dev/null @@ -1,52 +0,0 @@ -<mk-messaging-home-widget> - <template v-if="data.design == 0"> - <p class="title">%fa:comments%%i18n:desktop.tags.mk-messaging-home-widget.title%</p> - </template> - <mk-messaging ref="index" compact={ true }/> - <style lang="stylus" scoped> - :scope - display block - overflow hidden - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - > .title - z-index 2 - 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 - - > mk-messaging - max-height 250px - overflow auto - - </style> - <script lang="typescript"> - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.on('mount', () => { - this.$refs.index.on('navigate-user', user => { - riot.mount(document.body.appendChild(document.createElement('mk-messaging-room-window')), { - user: user - }); - }); - }); - - this.func = () => { - if (++this.data.design == 2) this.data.design = 0; - this.save(); - }; - </script> -</mk-messaging-home-widget> diff --git a/src/web/app/desktop/-tags/home-widgets/nav.tag b/src/web/app/desktop/-tags/home-widgets/nav.tag deleted file mode 100644 index 890fb4d8f7..0000000000 --- a/src/web/app/desktop/-tags/home-widgets/nav.tag +++ /dev/null @@ -1,23 +0,0 @@ -<mk-nav-home-widget> - <mk-nav-links/> - <style lang="stylus" scoped> - :scope - display block - padding 16px - font-size 12px - color #aaa - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - a - color #999 - - i - color #ccc - - </style> - <script lang="typescript"> - this.mixin('widget'); - </script> -</mk-nav-home-widget> diff --git a/src/web/app/desktop/-tags/home-widgets/photo-stream.tag b/src/web/app/desktop/-tags/home-widgets/photo-stream.tag deleted file mode 100644 index a2d95dede3..0000000000 --- a/src/web/app/desktop/-tags/home-widgets/photo-stream.tag +++ /dev/null @@ -1,118 +0,0 @@ -<mk-photo-stream-home-widget data-melt={ data.design == 2 }> - <template v-if="data.design == 0"> - <p class="title">%fa:camera%%i18n:desktop.tags.mk-photo-stream-home-widget.title%</p> - </template> - <p class="initializing" v-if="initializing">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p> - <div class="stream" v-if="!initializing && images.length > 0"> - <template each={ image in images }> - <div class="img" style={ 'background-image: url(' + image.url + '?thumbnail&size=256)' }></div> - </template> - </div> - <p class="empty" v-if="!initializing && images.length == 0">%i18n:desktop.tags.mk-photo-stream-home-widget.no-photos%</p> - <style lang="stylus" scoped> - :scope - display block - 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 - - > .initializing - > .empty - margin 0 - padding 16px - text-align center - color #aaa - - > [data-fa] - margin-right 4px - - </style> - <script lang="typescript"> - this.data = { - design: 0 - }; - - this.mixin('widget'); - - this.mixin('stream'); - this.connection = this.stream.getConnection(); - this.connectionId = this.stream.use(); - - this.images = []; - this.initializing = true; - - this.on('mount', () => { - this.connection.on('drive_file_created', this.onStreamDriveFileCreated); - - this.api('drive/stream', { - type: 'image/*', - limit: 9 - }).then(images => { - this.update({ - initializing: false, - images: images - }); - }); - }); - - this.on('unmount', () => { - this.connection.off('drive_file_created', this.onStreamDriveFileCreated); - this.stream.dispose(this.connectionId); - }); - - this.onStreamDriveFileCreated = file => { - if (/^image\/.+$/.test(file.type)) { - this.images.unshift(file); - if (this.images.length > 9) this.images.pop(); - this.update(); - } - }; - - this.func = () => { - if (++this.data.design == 3) this.data.design = 0; - this.save(); - }; - </script> -</mk-photo-stream-home-widget> diff --git a/src/web/app/desktop/-tags/home-widgets/slideshow.tag b/src/web/app/desktop/-tags/home-widgets/slideshow.tag deleted file mode 100644 index a69ab74b70..0000000000 --- a/src/web/app/desktop/-tags/home-widgets/slideshow.tag +++ /dev/null @@ -1,151 +0,0 @@ -<mk-slideshow-home-widget> - <div @click="choose"> - <p v-if="data.folder === undefined">クリックしてフォルダを指定してください</p> - <p v-if="data.folder !== undefined && images.length == 0 && !fetching">このフォルダには画像がありません</p> - <div ref="slideA" class="slide a"></div> - <div ref="slideB" class="slide b"></div> - </div> - <button @click="resize">%fa:expand%</button> - <style lang="stylus" scoped> - :scope - display block - overflow hidden - background #fff - border solid 1px rgba(0, 0, 0, 0.075) - border-radius 6px - - &:hover > button - display block - - > button - position absolute - left 0 - bottom 0 - display none - padding 4px - font-size 24px - color #fff - text-shadow 0 0 8px #000 - - > div - width 100% - height 100% - cursor pointer - - > * - pointer-events none - - > .slide - position absolute - top 0 - left 0 - width 100% - height 100% - background-size cover - background-position center - - &.b - opacity 0 - - </style> - <script lang="typescript"> - import * as anime from 'animejs'; - - this.data = { - folder: undefined, - size: 0 - }; - - this.mixin('widget'); - - this.images = []; - this.fetching = true; - - this.on('mount', () => { - this.applySize(); - - if (this.data.folder !== undefined) { - this.fetch(); - } - - this.clock = setInterval(this.change, 10000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - - this.applySize = () => { - let h; - - if (this.data.size == 1) { - h = 250; - } else { - h = 170; - } - - this.root.style.height = `${h}px`; - }; - - this.resize = () => { - this.data.size++; - if (this.data.size == 2) this.data.size = 0; - - this.applySize(); - this.save(); - }; - - this.change = () => { - if (this.images.length == 0) return; - - const index = Math.floor(Math.random() * this.images.length); - const img = `url(${ this.images[index].url }?thumbnail&size=1024)`; - - this.$refs.slideB.style.backgroundImage = img; - - anime({ - targets: this.$refs.slideB, - opacity: 1, - duration: 1000, - easing: 'linear', - complete: () => { - this.$refs.slideA.style.backgroundImage = img; - anime({ - targets: this.$refs.slideB, - opacity: 0, - duration: 0 - }); - } - }); - }; - - this.fetch = () => { - this.update({ - fetching: true - }); - - this.api('drive/files', { - folder_id: this.data.folder, - type: 'image/*', - limit: 100 - }).then(images => { - this.update({ - fetching: false, - images: images - }); - this.$refs.slideA.style.backgroundImage = ''; - this.$refs.slideB.style.backgroundImage = ''; - this.change(); - }); - }; - - this.choose = () => { - const i = riot.mount(document.body.appendChild(document.createElement('mk-select-folder-from-drive-window')))[0]; - i.one('selected', folder => { - this.data.folder = folder ? folder.id : null; - this.fetch(); - this.save(); - }); - }; - </script> -</mk-slideshow-home-widget> diff --git a/src/web/app/desktop/-tags/home-widgets/tips.tag b/src/web/app/desktop/-tags/home-widgets/tips.tag deleted file mode 100644 index efe9c90fc2..0000000000 --- a/src/web/app/desktop/-tags/home-widgets/tips.tag +++ /dev/null @@ -1,94 +0,0 @@ -<mk-tips-home-widget> - <p ref="tip">%fa:R lightbulb%<span ref="text"></span></p> - <style lang="stylus" scoped> - :scope - display block - overflow visible !important - - > p - display block - margin 0 - padding 0 12px - text-align center - font-size 0.7em - color #999 - - > [data-fa] - margin-right 4px - - kbd - display inline - padding 0 6px - margin 0 2px - font-size 1em - font-family inherit - border solid 1px #999 - border-radius 2px - - </style> - <script lang="typescript"> - import * as anime from 'animejs'; - - this.mixin('widget'); - - this.tips = [ - '<kbd>t</kbd>でタイムラインにフォーカスできます', - '<kbd>p</kbd>または<kbd>n</kbd>で投稿フォームを開きます', - '投稿フォームにはファイルをドラッグ&ドロップできます', - '投稿フォームにクリップボードにある画像データをペーストできます', - 'ドライブにファイルをドラッグ&ドロップしてアップロードできます', - 'ドライブでファイルをドラッグしてフォルダ移動できます', - 'ドライブでフォルダをドラッグしてフォルダ移動できます', - 'ホームは設定からカスタマイズできます', - 'MisskeyはMIT Licenseです', - 'タイムマシンウィジェットを利用すると、簡単に過去のタイムラインに遡れます', - '投稿の ... をクリックして、投稿をユーザーページにピン留めできます', - 'ドライブの容量は(デフォルトで)1GBです', - '投稿に添付したファイルは全てドライブに保存されます', - 'ホームのカスタマイズ中、ウィジェットを右クリックしてデザインを変更できます', - 'タイムライン上部にもウィジェットを設置できます', - '投稿をダブルクリックすると詳細が見れます', - '「**」でテキストを囲むと**強調表示**されます', - 'チャンネルウィジェットを利用すると、よく利用するチャンネルを素早く確認できます', - 'いくつかのウィンドウはブラウザの外に切り離すことができます', - 'カレンダーウィジェットのパーセンテージは、経過の割合を示しています', - 'APIを利用してbotの開発なども行えます', - 'MisskeyはLINEを通じてでも利用できます', - 'まゆかわいいよまゆ', - 'Misskeyは2014年にサービスを開始しました', - '対応ブラウザではMisskeyを開いていなくても通知を受け取れます' - ] - - this.on('mount', () => { - this.set(); - this.clock = setInterval(this.change, 20000); - }); - - this.on('unmount', () => { - clearInterval(this.clock); - }); - - this.set = () => { - this.$refs.text.innerHTML = this.tips[Math.floor(Math.random() * this.tips.length)]; - }; - - this.change = () => { - anime({ - targets: this.$refs.tip, - opacity: 0, - duration: 500, - easing: 'linear', - complete: this.set - }); - - setTimeout(() => { - anime({ - targets: this.$refs.tip, - opacity: 1, - duration: 500, - easing: 'linear' - }); - }, 500); - }; - </script> -</mk-tips-home-widget> diff --git a/webpack/plugins/index.ts b/webpack/plugins/index.ts index 9850db485c..d97f781558 100644 --- a/webpack/plugins/index.ts +++ b/webpack/plugins/index.ts @@ -11,11 +11,11 @@ const isProduction = env === 'production'; export default (version, lang) => { const plugins = [ consts(lang), - new StringReplacePlugin(), - hoist() + new StringReplacePlugin() ]; if (isProduction) { + plugins.push(hoist()); plugins.push(minify()); }