This commit is contained in:
syuilo 2018-02-19 18:26:20 +09:00
parent 4705c935ab
commit 0c9a7cf643
8 changed files with 161 additions and 162 deletions

17
.eslintrc Normal file
View file

@ -0,0 +1,17 @@
{
"parserOptions": {
"parser": "typescript-eslint-parser"
},
"extends": [
"eslint:recommended",
"plugin:vue/recommended"
],
"rules": {
"vue/require-v-for-key": false,
"vue/max-attributes-per-line": false,
"vue/html-indent": false,
"vue/html-self-closing": false,
"vue/no-unused-vars": false,
"no-console": 0
}
}

View file

@ -99,6 +99,8 @@
"diskusage": "0.2.4", "diskusage": "0.2.4",
"elasticsearch": "14.1.0", "elasticsearch": "14.1.0",
"escape-regexp": "0.0.1", "escape-regexp": "0.0.1",
"eslint": "^4.18.0",
"eslint-plugin-vue": "^4.2.2",
"eventemitter3": "3.0.0", "eventemitter3": "3.0.0",
"exif-js": "2.3.0", "exif-js": "2.3.0",
"express": "4.16.2", "express": "4.16.2",
@ -174,6 +176,7 @@
"ts-node": "4.1.0", "ts-node": "4.1.0",
"tslint": "5.9.1", "tslint": "5.9.1",
"typescript": "2.7.1", "typescript": "2.7.1",
"typescript-eslint-parser": "^13.0.0",
"uglify-es": "3.3.9", "uglify-es": "3.3.9",
"uglifyjs-webpack-plugin": "1.1.8", "uglifyjs-webpack-plugin": "1.1.8",
"uuid": "3.2.1", "uuid": "3.2.1",

View file

@ -1,23 +0,0 @@
<mk-timemachine-home-widget>
<mk-calendar-widget design={ data.design } warp={ warp }/>
<style lang="stylus" scoped>
:scope
display block
</style>
<script lang="typescript">
this.data = {
design: 0
};
this.mixin('widget');
this.warp = date => {
this.opts.tl.warp(date);
};
this.func = () => {
if (++this.data.design == 6) this.data.design = 0;
this.save();
};
</script>
</mk-timemachine-home-widget>

View file

@ -7,16 +7,15 @@
</template> </template>
<div class="calendar"> <div class="calendar">
<template v-if="design == 0 || design == 2 || design == 4">
<div class="weekday" <div class="weekday"
v-if="design == 0 || design == 2 || design == 4"
v-for="(day, i) in Array(7).fill(0)" v-for="(day, i) in Array(7).fill(0)"
:key="i"
:data-today="year == today.getFullYear() && month == today.getMonth() + 1 && today.getDay() == i" :data-today="year == today.getFullYear() && month == today.getMonth() + 1 && today.getDay() == i"
:data-is-donichi="i == 0 || i == 6" :data-is-donichi="i == 0 || i == 6"
>{{ weekdayText[i] }}</div> >{{ weekdayText[i] }}</div>
<div each={ day, i in Array(paddingDays).fill(0) }></div> </template>
<div class="day" v-for="(day, i) in Array(days).fill(0)" <div v-for="n in paddingDays"></div>
:key="i" <div class="day" v-for="(day, i) in days"
:data-today="isToday(i + 1)" :data-today="isToday(i + 1)"
:data-selected="isSelected(i + 1)" :data-selected="isSelected(i + 1)"
:data-is-out-of-range="isOutOfRange(i + 1)" :data-is-out-of-range="isOutOfRange(i + 1)"

View file

@ -37,43 +37,21 @@
</div> </div>
</div> </div>
<div class="main"> <div class="main">
<div class="left"> <div v-for="place in ['left', 'main', 'right']" :class="place" :ref="place" :data-place="place">
<div ref="left" data-place="left"> <template v-if="place != 'main'">
<template v-for="widget in leftWidgets"> <template v-for="widget in widgets[place]">
<div class="customize-container" v-if="customize" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)"> <div class="customize-container" v-if="customize" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)">
<component :is="'mkw-' + widget.name" :widget="widget" :ref="widget.id"/> <component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id"/>
</div> </div>
<template v-else> <template v-else>
<component :is="'mkw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/> <component :is="`mkw-${widget.name}`" :key="widget.id" :widget="widget" :ref="widget.id" @chosen="warp"/>
</template> </template>
</template> </template>
</div> </template>
</div> <template v-else>
<main ref="main"> <mk-timeline ref="tl" @loaded="onTlLoaded" v-if="place == 'main' && mode == 'timeline'"/>
<div class="maintop" ref="maintop" data-place="main" v-if="customize"> <mk-mentions @loaded="onTlLoaded" v-if="place == 'main' && mode == 'mentions'"/>
<template v-for="widget in centerWidgets"> </template>
<div class="customize-container" v-if="customize" :key="widget.id" @contextmenu.stop.prevent="onWidgetContextmenu(widget.id)">
<component :is="'mkw-' + widget.name" :widget="widget" :ref="widget.id"/>
</div>
<template v-else>
<component :is="'mkw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/>
</template>
</template>
</div>
<mk-timeline ref="tl" @loaded="onTlLoaded" v-if="mode == 'timeline'"/>
<mk-mentions ref="tl" @loaded="onTlLoaded" v-if="mode == 'mentions'"/>
</main>
<div class="right">
<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="'mkw-' + widget.name" :widget="widget" :ref="widget.id"/>
</div>
<template v-else>
<component :is="'mkw-' + widget.name" :key="widget.id" :widget="widget" :ref="widget.id"/>
</template>
</template>
</div>
</div> </div>
</div> </div>
</div> </div>
@ -99,6 +77,85 @@ export default Vue.extend({
widgetAdderSelected: null widgetAdderSelected: null
}; };
}, },
computed: {
leftWidgets(): any {
return (this as any).os.i.client_settings.home.filter(w => w.place == 'left');
},
rightWidgets(): any {
return (this as any).os.i.client_settings.home.filter(w => w.place == 'right');
},
widgets(): any {
return {
left: this.leftWidgets,
right: this.rightWidgets,
};
},
leftEl(): Element {
return (this.$refs.left as Element[])[0];
},
rightEl(): Element {
return (this.$refs.right as Element[])[0];
}
},
created() {
this.bakedHomeData = this.bakeHomeData();
},
mounted() {
(this as any).os.i.on('refreshed', this.onMeRefreshed);
this.home = (this as any).os.i.client_settings.home;
this.$nextTick(() => {
if (!this.customize) {
if (this.leftEl.children.length == 0) {
this.leftEl.parentNode.removeChild(this.leftEl);
}
if (this.rightEl.children.length == 0) {
this.rightEl.parentNode.removeChild(this.rightEl);
}
}
if (this.customize) {
(this as any).apis.dialog({
title: '%fa:info-circle%カスタマイズのヒント',
text: '<p>ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。</p>' +
'<p>一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。</p>' +
'<p>ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。</p>' +
'<p>カスタマイズを終了するには、右上の「完了」をクリックします。</p>',
actions: [{
text: 'Got it!'
}]
});
const sortableOption = {
group: 'kyoppie',
animation: 150,
onMove: evt => {
const id = evt.dragged.getAttribute('data-widget-id');
this.home.find(tag => tag.id == id).widget.place = evt.to.getAttribute('data-place');
},
onSort: () => {
this.saveHome();
}
};
new Sortable(this.leftEl, sortableOption);
new Sortable(this.rightEl, sortableOption);
new Sortable(this.$refs.trash, Object.assign({}, sortableOption, {
onAdd: evt => {
const el = evt.item;
const id = el.getAttribute('data-widget-id');
el.parentNode.removeChild(el);
(this as any).os.i.client_settings.home = (this as any).os.i.client_settings.home.filter(w => w.id != id);
this.saveHome();
}
}));
}
});
},
beforeDestroy() {
(this as any).os.i.off('refreshed', this.onMeRefreshed);
},
methods: { methods: {
bakeHomeData() { bakeHomeData() {
return JSON.stringify((this as any).os.i.client_settings.home); return JSON.stringify((this as any).os.i.client_settings.home);
@ -130,102 +187,27 @@ export default Vue.extend({
saveHome() { saveHome() {
const data = []; const data = [];
Array.from((this.$refs.left as Element).children).forEach(el => { Array.from(this.leftEl.children).forEach(el => {
const id = el.getAttribute('data-widget-id'); const id = el.getAttribute('data-widget-id');
const widget = (this as any).os.i.client_settings.home.find(w => w.id == id); const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
widget.place = 'left'; widget.place = 'left';
data.push(widget); data.push(widget);
}); });
Array.from((this.$refs.right as Element).children).forEach(el => { Array.from(this.rightEl.children).forEach(el => {
const id = el.getAttribute('data-widget-id'); const id = el.getAttribute('data-widget-id');
const widget = (this as any).os.i.client_settings.home.find(w => w.id == id); const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
widget.place = 'right'; widget.place = 'right';
data.push(widget); data.push(widget);
}); });
Array.from((this.$refs.maintop as Element).children).forEach(el => {
const id = el.getAttribute('data-widget-id');
const widget = (this as any).os.i.client_settings.home.find(w => w.id == id);
widget.place = 'main';
data.push(widget);
});
(this as any).api('i/update_home', { (this as any).api('i/update_home', {
home: data home: data
}); });
}
},
computed: {
leftWidgets(): any {
return (this as any).os.i.client_settings.home.filter(w => w.place == 'left');
}, },
centerWidgets(): any { warp(date) {
return (this as any).os.i.client_settings.home.filter(w => w.place == 'center'); (this.$refs.tl as any)[0].warp(date);
},
rightWidgets(): any {
return (this as any).os.i.client_settings.home.filter(w => w.place == 'right');
} }
},
created() {
this.bakedHomeData = this.bakeHomeData();
},
mounted() {
(this as any).os.i.on('refreshed', this.onMeRefreshed);
this.home = (this as any).os.i.client_settings.home;
if (!this.customize) {
if ((this.$refs.left as Element).children.length == 0) {
(this.$refs.left as Element).parentNode.removeChild((this.$refs.left as Element));
}
if ((this.$refs.right as Element).children.length == 0) {
(this.$refs.right as Element).parentNode.removeChild((this.$refs.right as Element));
}
}
if (this.customize) {
/*dialog('%fa:info-circle%',
'<p>ホームのカスタマイズでは、ウィジェットを追加/削除したり、ドラッグ&ドロップして並べ替えたりすることができます。</p>' +
'<p>一部のウィジェットは、<strong><strong>右</strong>クリック</strong>することで表示を変更することができます。</p>' +
'<p>ウィジェットを削除するには、ヘッダーの<strong>「ゴミ箱」</strong>と書かれたエリアにウィジェットをドラッグ&ドロップします。</p>' +
'<p>カスタマイズを終了するには、右上の「完了」をクリックします。</p>',
[{
text: 'Got it!'
}]);*/
const sortableOption = {
group: 'kyoppie',
animation: 150,
onMove: evt => {
const id = evt.dragged.getAttribute('data-widget-id');
this.home.find(tag => tag.id == id).update({ place: evt.to.getAttribute('data-place') });
},
onSort: () => {
this.saveHome();
}
};
new Sortable(this.$refs.left, sortableOption);
new Sortable(this.$refs.right, sortableOption);
new Sortable(this.$refs.maintop, sortableOption);
new Sortable(this.$refs.trash, Object.assign({}, sortableOption, {
onAdd: evt => {
const el = evt.item;
const id = el.getAttribute('data-widget-id');
el.parentNode.removeChild(el);
(this as any).os.i.client_settings.home = (this as any).os.i.client_settings.home.filter(w => w.id != id);
this.saveHome();
}
}));
}
},
beforeDestroy() {
(this as any).os.i.off('refreshed', this.onMeRefreshed);
this.home.forEach(widget => {
widget.unmount();
});
} }
}); });
</script> </script>
@ -324,26 +306,16 @@ export default Vue.extend({
> * > *
pointer-events none pointer-events none
> main > .main
padding 16px padding 16px
width calc(100% - 275px * 2) width calc(100% - 275px * 2)
> *:not(.maintop):not(:last-child)
> .maintop > *:not(:last-child)
margin-bottom 16px
> .maintop
min-height 64px
margin-bottom 16px
> *:not(main) > *:not(main)
width 275px width 275px
padding 16px 0 16px 0
> * > *:not(:last-child)
padding 16px 0 16px 0 margin-bottom 16px
> *:not(:last-child)
margin-bottom 16px
> .left > .left
padding-left 16px padding-left 16px
@ -355,7 +327,7 @@ export default Vue.extend({
> *:not(main) > *:not(main)
display none display none
> main > .main
float none float none
width 100% width 100%
max-width 700px max-width 700px

View file

@ -33,6 +33,7 @@ import driveFolder from './drive-folder.vue';
import driveNavFolder from './drive-nav-folder.vue'; import driveNavFolder from './drive-nav-folder.vue';
import postDetail from './post-detail.vue'; import postDetail from './post-detail.vue';
import settings from './settings.vue'; import settings from './settings.vue';
import calendar from './calendar.vue';
import wNav from './widgets/nav.vue'; import wNav from './widgets/nav.vue';
import wCalendar from './widgets/calendar.vue'; import wCalendar from './widgets/calendar.vue';
import wPhotoStream from './widgets/photo-stream.vue'; import wPhotoStream from './widgets/photo-stream.vue';
@ -41,6 +42,7 @@ import wTips from './widgets/tips.vue';
import wDonation from './widgets/donation.vue'; import wDonation from './widgets/donation.vue';
import wNotifications from './widgets/notifications.vue'; import wNotifications from './widgets/notifications.vue';
import wBroadcast from './widgets/broadcast.vue'; import wBroadcast from './widgets/broadcast.vue';
import wTimemachine from './widgets/timemachine.vue';
Vue.component('mk-ui', ui); Vue.component('mk-ui', ui);
Vue.component('mk-ui-header', uiHeader); Vue.component('mk-ui-header', uiHeader);
@ -75,6 +77,7 @@ Vue.component('mk-drive-folder', driveFolder);
Vue.component('mk-drive-nav-folder', driveNavFolder); Vue.component('mk-drive-nav-folder', driveNavFolder);
Vue.component('mk-post-detail', postDetail); Vue.component('mk-post-detail', postDetail);
Vue.component('mk-settings', settings); Vue.component('mk-settings', settings);
Vue.component('mk-calendar', calendar);
Vue.component('mkw-nav', wNav); Vue.component('mkw-nav', wNav);
Vue.component('mkw-calendar', wCalendar); Vue.component('mkw-calendar', wCalendar);
Vue.component('mkw-photo-stream', wPhotoStream); Vue.component('mkw-photo-stream', wPhotoStream);
@ -83,3 +86,4 @@ Vue.component('mkw-tips', wTips);
Vue.component('mkw-donation', wDonation); Vue.component('mkw-donation', wDonation);
Vue.component('mkw-notifications', wNotifications); Vue.component('mkw-notifications', wNotifications);
Vue.component('mkw-broadcast', wBroadcast); Vue.component('mkw-broadcast', wBroadcast);
Vue.component('mkw-timemachine', wTimemachine);

View file

@ -13,19 +13,14 @@
import Vue from 'vue'; import Vue from 'vue';
export default Vue.extend({ export default Vue.extend({
props: {
date: {
type: Date,
required: false
}
},
data() { data() {
return { return {
fetching: true, fetching: true,
moreFetching: false, moreFetching: false,
posts: [], posts: [],
connection: null, connection: null,
connectionId: null connectionId: null,
date: null
}; };
}, },
computed: { computed: {
@ -60,7 +55,7 @@ export default Vue.extend({
this.fetching = true; this.fetching = true;
(this as any).api('posts/timeline', { (this as any).api('posts/timeline', {
until_date: this.date ? (this.date as any).getTime() : undefined until_date: this.date ? this.date.getTime() : undefined
}).then(posts => { }).then(posts => {
this.fetching = false; this.fetching = false;
this.posts = posts; this.posts = posts;
@ -93,6 +88,10 @@ export default Vue.extend({
(this.$refs.timeline as any).focus(); (this.$refs.timeline as any).focus();
} }
} }
},
warp(date) {
this.date = date;
this.fetch();
} }
} }
}); });

View file

@ -0,0 +1,28 @@
<template>
<div class="mkw-timemachine">
<mk-calendar :design="props.design" @chosen="chosen"/>
</div>
</template>
<script lang="ts">
import define from '../../../../common/define-widget';
export default define({
name: 'timemachine',
props: {
design: 0
}
}).extend({
methods: {
chosen(date) {
this.$emit('chosen', date);
},
func() {
if (this.props.design == 5) {
this.props.design = 0;
} else {
this.props.design++;
}
}
}
});
</script>