This commit is contained in:
syuilo
2018-02-18 23:51:41 +09:00
parent 2a54802efa
commit e01b9d3f16
22 changed files with 142 additions and 129 deletions

View File

@ -1,9 +1,9 @@
<template>
<mk-window ref="window" @closed="$destroy" width="800px" height="500px" :popout="popout">
<span slot="header" :class="$style.header">
<p class="info" v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:desktop.tags.mk-drive-browser-window.used%</p>
%fa:cloud%%i18n:desktop.tags.mk-drive-browser-window.drive%
</span>
<template slot="header">
<p v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:desktop.tags.mk-drive-browser-window.used%</p>
<span: class="$style.title">%fa:cloud%%i18n:desktop.tags.mk-drive-browser-window.drive%</span>
</template>
<mk-drive-browser multiple :folder="folder" ref="browser"/>
</mk-window>
</template>
@ -38,7 +38,7 @@ export default Vue.extend({
</script>
<style lang="stylus" module>
.header
.title
> [data-fa]
margin-right 4px

View File

@ -1,7 +1,6 @@
<template>
<button class="mk-follow-button"
:class="{ wait, follow: !user.is_following, unfollow: user.is_following }"
v-if="!init"
@click="onClick"
:disabled="wait"
:title="user.is_following ? 'フォロー解除' : 'フォローする'"

View File

@ -41,10 +41,10 @@
<div ref="left" data-place="left">
<template v-for="widget in leftWidgets">
<div class="customize-container" v-if="customize" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)">
<component :is="'mk-hw-' + widget.name" :widget="widget" :ref="widget.id"/>
<component :is="'mkw-' + widget.name" :widget="widget" :ref="widget.id"/>
</div>
<template v-else>
<component :is="'mk-hw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/>
<component :is="'mkw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/>
</template>
</template>
</div>
@ -53,10 +53,10 @@
<div class="maintop" ref="maintop" data-place="main" v-if="customize">
<template v-for="widget in centerWidgets">
<div class="customize-container" v-if="customize" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)">
<component :is="'mk-hw-' + widget.name" :widget="widget" :ref="widget.id"/>
<component :is="'mkw-' + widget.name" :widget="widget" :ref="widget.id"/>
</div>
<template v-else>
<component :is="'mk-hw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/>
<component :is="'mkw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/>
</template>
</template>
</div>
@ -67,10 +67,10 @@
<div ref="right" data-place="right">
<template v-for="widget in rightWidgets">
<div class="customize-container" v-if="customize" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)">
<component :is="'mk-hw-' + widget.name" :widget="widget" :ref="widget.id"/>
<component :is="'mkw-' + widget.name" :widget="widget" :ref="widget.id"/>
</div>
<template v-else>
<component :is="'mk-hw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/>
<component :is="'mkw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/>
</template>
</template>
</div>

View File

@ -32,6 +32,13 @@ import driveFile from './drive-file.vue';
import driveFolder from './drive-folder.vue';
import driveNavFolder from './drive-nav-folder.vue';
import contextMenuMenu from './context-menu-menu.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 wNotifications from './widgets/notifications.vue';
Vue.component('mk-ui', ui);
Vue.component('mk-ui-header', uiHeader);
@ -65,3 +72,10 @@ Vue.component('mk-drive-file', driveFile);
Vue.component('mk-drive-folder', driveFolder);
Vue.component('mk-drive-nav-folder', driveNavFolder);
Vue.component('context-menu-menu', contextMenuMenu);
Vue.component('mkw-nav', wNav);
Vue.component('mkw-calendar', wCalendar);
Vue.component('mkw-photo-stream', wPhotoStream);
Vue.component('mkw-slideshoe', wSlideshow);
Vue.component('mkw-tips', wTips);
Vue.component('mkw-donation', wDonation);
Vue.component('mkw-notifications', wNotifications);

View File

@ -1,5 +1,5 @@
<template>
<mk-window ref="window" width='500px' height='560px' @closed="$destroy">
<mk-window ref="window" width="500px" height="560px" @closed="$destroy">
<span slot="header" :class="$style.header">%fa:comments%メッセージ</span>
<mk-messaging :class="$style.content" @navigate="navigate"/>
</mk-window>
@ -7,6 +7,8 @@
<script lang="ts">
import Vue from 'vue';
import MkMessagingRoomWindow from './messaging-room-window.vue';
export default Vue.extend({
methods: {
navigate(user) {

View File

@ -7,6 +7,11 @@
</mk-window>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({});
</script>
<style lang="stylus" module>
.header
> [data-fa]

View File

@ -33,6 +33,7 @@
<script lang="ts">
import Vue from 'vue';
import MkSettingsWindow from './settings-window.vue';
import MkDriveWindow from './drive-window.vue';
import contains from '../../../common/scripts/contains';
import signout from '../../../common/scripts/signout';
@ -69,8 +70,7 @@ export default Vue.extend({
},
drive() {
this.close();
// TODO
//document.body.appendChild(new MkDriveWindow().$mount().$el);
document.body.appendChild(new MkDriveWindow().$mount().$el);
},
settings() {
this.close();

View File

@ -34,6 +34,7 @@
<script lang="ts">
import Vue from 'vue';
import MkMessagingWindow from './messaging-window.vue';
export default Vue.extend({
data() {
@ -76,8 +77,7 @@ export default Vue.extend({
},
messaging() {
// TODO
//document.body.appendChild(new MkMessagingWindow().$mount().$el);
document.body.appendChild(new MkMessagingWindow().$mount().$el);
}
}
});

View File

@ -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 '../../../../common/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>

View File

@ -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 '../../../../common/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>

View File

@ -6,7 +6,7 @@
</template>
<script lang="ts">
import define from '../../../define-widget';
import define from '../../../../common/define-widget';
export default define({
name: 'messaging',
props: {

View File

@ -0,0 +1,29 @@
<template>
<div class="mkw-nav">
<mk-nav-links/>
</div>
</template>
<script lang="ts">
import define from '../../../../common/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>

View File

@ -0,0 +1,70 @@
<template>
<div class="mkw-notifications">
<template v-if="!props.compact">
<p class="title">%fa:R bell%%i18n:desktop.tags.mk-notifications-home-widget.title%</p>
<button @click="settings" title="%i18n:desktop.tags.mk-notifications-home-widget.settings%">%fa:cog%</button>
</template>
<mk-notifications/>
</div>
</template>
<script lang="ts">
import define from '../../../../common/define-widget';
export default define({
name: 'notifications',
props: {
compact: false
}
}).extend({
methods: {
settings() {
alert('not implemented yet');
},
func() {
this.props.compact = !this.props.compact;
}
}
});
</script>
<style lang="stylus" scoped>
.mkw-notifications
background #fff
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
> .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
> .mk-notifications
max-height 300px
overflow auto
</style>

View File

@ -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 '../../../../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.fetching = false;
this.images = images;
});
},
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>

View File

@ -0,0 +1,125 @@
<template>
<div class="mkw-profile"
: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)' : '' }
title="クリックでバナー編集"
@click="wapi_setBanner"
></div>
<img class="avatar"
src={ I.avatar_url + '?thumbnail&size=96' }
@click="wapi_setAvatar"
alt="avatar"
title="クリックでアバター編集"
v-user-preview={ I.id }
/>
<a class="name" href={ '/' + I.username }>{ I.name }</a>
<p class="username">@{ I.username }</p>
</div>
</template>
<script lang="ts">
import define from '../../../../common/define-widget';
export default define({
name: 'profile',
props: {
design: 0
}
}).extend({
methods: {
func() {
if (this.props.design == 2) {
this.props.design = 0;
} else {
this.props.design++;
}
}
}
});
</script>
<style lang="stylus" scoped>
.mkw-profile
overflow hidden
background #fff
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
&[data-compact]
> .banner:before
content ""
display block
width 100%
height 100%
background rgba(0, 0, 0, 0.5)
> .avatar
top ((100px - 58px) / 2)
left ((100px - 58px) / 2)
border none
border-radius 100%
box-shadow 0 0 16px rgba(0, 0, 0, 0.5)
> .name
position absolute
top 0
left 92px
margin 0
line-height 100px
color #fff
text-shadow 0 0 8px rgba(0, 0, 0, 0.5)
> .username
display none
&[data-melt]
background transparent !important
border none !important
> .banner
visibility hidden
> .avatar
box-shadow none
> .name
color #666
text-shadow none
> .banner
height 100px
background-color #f5f5f5
background-size cover
background-position center
cursor pointer
> .avatar
display block
position absolute
top 76px
left 16px
width 58px
height 58px
margin 0
border solid 3px #fff
border-radius 8px
vertical-align bottom
cursor pointer
> .name
display block
margin 10px 0 0 84px
line-height 16px
font-weight bold
color #555
> .username
display block
margin 4px 0 8px 84px
line-height 16px
font-size 0.9em
color #999
</style>

View File

@ -0,0 +1,153 @@
<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 * as anime from 'animejs';
import define from '../../../../common/define-widget';
export default define({
name: 'slideshow',
props: {
folder: undefined,
size: 0
}
}).extend({
data() {
return {
images: [],
fetching: true,
clock: null
};
},
mounted() {
this.$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 as any).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 as any).apis.chooseDriveFolder().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>

View File

@ -0,0 +1,108 @@
<template>
<div class="mkw-tips">
<p ref="tip">%fa:R lightbulb%<span v-html="tip"></span></p>
</div>
</template>
<script lang="ts">
import * as anime from 'animejs';
import define from '../../../../common/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() {
this.$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>