summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorsyuilo <syuilotan@yahoo.co.jp>2018-02-21 15:30:03 +0900
committersyuilo <syuilotan@yahoo.co.jp>2018-02-21 15:30:03 +0900
commitde6d77d0cbd2905e021a075b667b6688cd5e06f6 (patch)
tree4f960a3943926295ca1017bdd1cd35bc33dfe223 /src
parentwip (diff)
downloadsharkey-de6d77d0cbd2905e021a075b667b6688cd5e06f6.tar.gz
sharkey-de6d77d0cbd2905e021a075b667b6688cd5e06f6.tar.bz2
sharkey-de6d77d0cbd2905e021a075b667b6688cd5e06f6.zip
wip
Diffstat (limited to 'src')
-rw-r--r--src/web/app/common/define-widget.ts27
-rw-r--r--src/web/app/common/mios.ts19
-rw-r--r--src/web/app/common/scripts/streaming/home-stream.ts4
-rw-r--r--src/web/app/desktop/-tags/home-widgets/channel.tag318
-rw-r--r--src/web/app/desktop/script.ts3
-rw-r--r--src/web/app/desktop/views/components/calendar.vue2
-rw-r--r--src/web/app/desktop/views/components/home.vue47
-rw-r--r--src/web/app/desktop/views/components/index.ts2
-rw-r--r--src/web/app/desktop/views/components/widgets/activity.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/broadcast.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/calendar.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/channel.channel.form.vue67
-rw-r--r--src/web/app/desktop/views/components/widgets/channel.channel.post.vue64
-rw-r--r--src/web/app/desktop/views/components/widgets/channel.channel.vue104
-rw-r--r--src/web/app/desktop/views/components/widgets/channel.vue107
-rw-r--r--src/web/app/desktop/views/components/widgets/messaging.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/notifications.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/photo-stream.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/polls.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/post-form.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/profile.vue18
-rw-r--r--src/web/app/desktop/views/components/widgets/rss.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/server.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/slideshow.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/timemachine.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/trends.vue4
-rw-r--r--src/web/app/desktop/views/components/widgets/users.vue4
-rw-r--r--src/web/app/desktop/views/pages/home-customize.vue (renamed from src/web/app/desktop/views/pages/home-custmize.vue)2
-rw-r--r--src/web/app/init.ts8
29 files changed, 422 insertions, 426 deletions
diff --git a/src/web/app/common/define-widget.ts b/src/web/app/common/define-widget.ts
index 6088efd7e5..930a7c5868 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: {
@@ -17,20 +17,9 @@ export default function<T extends object>(data: {
},
data() {
return {
- props: data.props || {} as T
+ props: data.props ? data.props() : {} as T
};
},
- watch: {
- props(newProps, oldProps) {
- if (JSON.stringify(newProps) == JSON.stringify(oldProps)) return;
- (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;
- });
- }
- },
created() {
if (this.props) {
Object.keys(this.props).forEach(prop => {
@@ -39,6 +28,18 @@ export default function<T extends object>(data: {
}
});
}
+
+ this.$watch('props', newProps => {
+ console.log(this.id, 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;
+ });
+ }, {
+ deep: true
+ });
}
});
}
diff --git a/src/web/app/common/mios.ts b/src/web/app/common/mios.ts
index c4208aa913..4b9375f548 100644
--- a/src/web/app/common/mios.ts
+++ b/src/web/app/common/mios.ts
@@ -1,9 +1,8 @@
import { EventEmitter } from 'eventemitter3';
-import * as riot from 'riot';
+import api from './scripts/api';
import signout from './scripts/signout';
import Progress from './scripts/loading';
import HomeStreamManager from './scripts/streaming/home-stream-manager';
-import api from './scripts/api';
import DriveStreamManager from './scripts/streaming/drive-stream-manager';
import ServerStreamManager from './scripts/streaming/server-stream-manager';
import RequestsStreamManager from './scripts/streaming/requests-stream-manager';
@@ -226,22 +225,8 @@ export default class MiOS extends EventEmitter {
// フェッチが完了したとき
const fetched = me => {
if (me) {
- riot.observable(me);
-
- // この me オブジェクトを更新するメソッド
- me.update = data => {
- if (data) Object.assign(me, data);
- me.trigger('updated');
- };
-
// ローカルストレージにキャッシュ
localStorage.setItem('me', JSON.stringify(me));
-
- // 自分の情報が更新されたとき
- me.on('updated', () => {
- // キャッシュ更新
- localStorage.setItem('me', JSON.stringify(me));
- });
}
this.i = me;
@@ -270,8 +255,6 @@ export default class MiOS extends EventEmitter {
// 後から新鮮なデータをフェッチ
fetchme(cachedMe.token, freshData => {
Object.assign(cachedMe, freshData);
- cachedMe.trigger('updated');
- cachedMe.trigger('refreshed');
});
} else {
// Get token from cookie
diff --git a/src/web/app/common/scripts/streaming/home-stream.ts b/src/web/app/common/scripts/streaming/home-stream.ts
index 11ad754ef0..a92b61caed 100644
--- a/src/web/app/common/scripts/streaming/home-stream.ts
+++ b/src/web/app/common/scripts/streaming/home-stream.ts
@@ -16,7 +16,9 @@ export default class Connection extends Stream {
}, 1000 * 60);
// 自分の情報が更新されたとき
- this.on('i_updated', me.update);
+ this.on('i_updated', i => {
+ Object.assign(me, i);
+ });
// トークンが再生成されたとき
// このままではAPIが利用できないので強制的にサインアウトさせる
diff --git a/src/web/app/desktop/-tags/home-widgets/channel.tag b/src/web/app/desktop/-tags/home-widgets/channel.tag
deleted file mode 100644
index c20a851e79..0000000000
--- a/src/web/app/desktop/-tags/home-widgets/channel.tag
+++ /dev/null
@@ -1,318 +0,0 @@
-<mk-channel-home-widget>
- <template v-if="!data.compact">
- <p class="title">%fa:tv%{
- channel ? channel.title : '%i18n:desktop.tags.mk-channel-home-widget.title%'
- }</p>
- <button @click="settings" title="%i18n:desktop.tags.mk-channel-home-widget.settings%">%fa:cog%</button>
- </template>
- <p class="get-started" v-if="this.data.channel == null">%i18n:desktop.tags.mk-channel-home-widget.get-started%</p>
- <mk-channel ref="channel" show={ this.data.channel }/>
- <style lang="stylus" scoped>
- :scope
- display block
- background #fff
- border solid 1px rgba(0, 0, 0, 0.075)
- border-radius 6px
- overflow hidden
-
- > .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
-
- > 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
-
- > .get-started
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > mk-channel
- height 200px
-
- </style>
- <script lang="typescript">
- this.data = {
- channel: null,
- compact: false
- };
-
- this.mixin('widget');
-
- this.on('mount', () => {
- if (this.data.channel) {
- this.zap();
- }
- });
-
- this.zap = () => {
- this.update({
- fetching: true
- });
-
- this.$root.$data.os.api('channels/show', {
- channel_id: this.data.channel
- }).then(channel => {
- this.update({
- fetching: false,
- channel: channel
- });
-
- this.$refs.channel.zap(channel);
- });
- };
-
- this.settings = () => {
- const id = window.prompt('チャンネルID');
- if (!id) return;
- this.data.channel = id;
- this.zap();
-
- // Save state
- this.save();
- };
-
- this.func = () => {
- this.data.compact = !this.data.compact;
- this.save();
- };
- </script>
-</mk-channel-home-widget>
-
-<mk-channel>
- <p v-if="fetching">読み込み中<mk-ellipsis/></p>
- <div v-if="!fetching" ref="posts">
- <p v-if="posts.length == 0">まだ投稿がありません</p>
- <mk-channel-post each={ post in posts.slice().reverse() } post={ post } form={ parent.refs.form }/>
- </div>
- <mk-channel-form ref="form"/>
- <style lang="stylus" scoped>
- :scope
- display block
-
- > p
- margin 0
- padding 16px
- text-align center
- color #aaa
-
- > div
- height calc(100% - 38px)
- overflow auto
- font-size 0.9em
-
- > mk-channel-post
- border-bottom solid 1px #eee
-
- &:last-child
- border-bottom none
-
- > mk-channel-form
- position absolute
- left 0
- bottom 0
-
- </style>
- <script lang="typescript">
- import ChannelStream from '../../../common/scripts/streaming/channel-stream';
-
- this.mixin('api');
-
- this.fetching = true;
- this.channel = null;
- this.posts = [];
-
- this.on('unmount', () => {
- if (this.connection) {
- this.connection.off('post', this.onPost);
- this.connection.close();
- }
- });
-
- this.zap = channel => {
- this.update({
- fetching: true,
- channel: channel
- });
-
- this.$root.$data.os.api('channels/posts', {
- channel_id: channel.id
- }).then(posts => {
- this.update({
- fetching: false,
- posts: posts
- });
-
- this.scrollToBottom();
-
- if (this.connection) {
- this.connection.off('post', this.onPost);
- this.connection.close();
- }
- this.connection = new ChannelStream(this.channel.id);
- this.connection.on('post', this.onPost);
- });
- };
-
- this.onPost = post => {
- this.posts.unshift(post);
- this.update();
- this.scrollToBottom();
- };
-
- this.scrollToBottom = () => {
- this.$refs.posts.scrollTop = this.$refs.posts.scrollHeight;
- };
- </script>
-</mk-channel>
-
-<mk-channel-post>
- <header>
- <a class="index" @click="reply">{ post.index }:</a>
- <a class="name" href={ _URL_ + '/' + post.user.username }><b>{ post.user.name }</b></a>
- <span>ID:<i>{ post.user.username }</i></span>
- </header>
- <div>
- <a v-if="post.reply">&gt;&gt;{ post.reply.index }</a>
- { post.text }
- <div class="media" v-if="post.media">
- <template each={ file in post.media }>
- <a href={ file.url } target="_blank">
- <img src={ file.url + '?thumbnail&size=512' } alt={ file.name } title={ file.name }/>
- </a>
- </template>
- </div>
- </div>
- <style lang="stylus" scoped>
- :scope
- display block
- margin 0
- padding 0
- color #444
-
- > header
- position -webkit-sticky
- position sticky
- z-index 1
- top 0
- padding 8px 4px 4px 16px
- background rgba(255, 255, 255, 0.9)
-
- > .index
- margin-right 0.25em
-
- > .name
- margin-right 0.5em
- color #008000
-
- > div
- padding 0 16px 16px 16px
-
- > .media
- > a
- display inline-block
-
- > img
- max-width 100%
- vertical-align bottom
-
- </style>
- <script lang="typescript">
- this.post = this.opts.post;
- this.form = this.opts.form;
-
- this.reply = () => {
- this.form.refs.text.value = `>>${ this.post.index } `;
- };
- </script>
-</mk-channel-post>
-
-<mk-channel-form>
- <input ref="text" disabled={ wait } onkeydown={ onkeydown } placeholder="書いて">
- <style lang="stylus" scoped>
- :scope
- display block
- width 100%
- height 38px
- padding 4px
- border-top solid 1px #ddd
-
- > input
- padding 0 8px
- width 100%
- height 100%
- font-size 14px
- color #55595c
- border solid 1px #dadada
- border-radius 4px
-
- &:hover
- &:focus
- border-color #aeaeae
-
- </style>
- <script lang="typescript">
- this.mixin('api');
-
- this.clear = () => {
- this.$refs.text.value = '';
- };
-
- this.onkeydown = e => {
- if (e.which == 10 || e.which == 13) this.post();
- };
-
- this.post = () => {
- this.update({
- wait: true
- });
-
- let text = this.$refs.text.value;
- let reply = null;
-
- if (/^>>([0-9]+) /.test(text)) {
- const index = text.match(/^>>([0-9]+) /)[1];
- reply = this.parent.posts.find(p => p.index.toString() == index);
- text = text.replace(/^>>([0-9]+) /, '');
- }
-
- this.$root.$data.os.api('posts/create', {
- text: text,
- reply_id: reply ? reply.id : undefined,
- channel_id: this.parent.channel.id
- }).then(data => {
- this.clear();
- }).catch(err => {
- alert('失敗した');
- }).then(() => {
- this.update({
- wait: false
- });
- });
- };
- </script>
-</mk-channel-form>
diff --git a/src/web/app/desktop/script.ts b/src/web/app/desktop/script.ts
index b647f4031d..4f2ac61ee2 100644
--- a/src/web/app/desktop/script.ts
+++ b/src/web/app/desktop/script.ts
@@ -23,6 +23,7 @@ import MkIndex from './views/pages/index.vue';
import MkUser from './views/pages/user/user.vue';
import MkSelectDrive from './views/pages/selectdrive.vue';
import MkDrive from './views/pages/drive.vue';
+import MkHomeCustomize from './views/pages/home-customize.vue';
/**
* init
@@ -67,6 +68,8 @@ init(async (launch) => {
app.$router.addRoutes([{
path: '/', name: 'index', component: MkIndex
}, {
+ path: '/i/customize-home', component: MkHomeCustomize
+ }, {
path: '/i/drive', component: MkDrive
}, {
path: '/i/drive/folder/:folder', component: MkDrive
diff --git a/src/web/app/desktop/views/components/calendar.vue b/src/web/app/desktop/views/components/calendar.vue
index a21d3e6148..08b08f8d42 100644
--- a/src/web/app/desktop/views/components/calendar.vue
+++ b/src/web/app/desktop/views/components/calendar.vue
@@ -1,5 +1,5 @@
<template>
-<div class="mk-calendar">
+<div class="mk-calendar" :data-melt="design == 4 || design == 5">
<template v-if="design == 0 || design == 1">
<button @click="prev" title="%i18n:desktop.tags.mk-calendar-widget.prev%">%fa:chevron-circle-left%</button>
<p class="title">{{ '%i18n:desktop.tags.mk-calendar-widget.title%'.replace('{1}', year).replace('{2}', month) }}</p>
diff --git a/src/web/app/desktop/views/components/home.vue b/src/web/app/desktop/views/components/home.vue
index 8e64a2d83d..6ab1512b01 100644
--- a/src/web/app/desktop/views/components/home.vue
+++ b/src/web/app/desktop/views/components/home.vue
@@ -40,7 +40,7 @@
<div v-for="place in ['left', 'main', 'right']" :class="place" :ref="place" :data-place="place">
<template v-if="place != 'main'">
<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)" :data-widget-id="widget.id">
<component :is="`mkw-${widget.name}`" :widget="widget" :ref="widget.id"/>
</div>
<template v-else>
@@ -60,7 +60,7 @@
<script lang="ts">
import Vue from 'vue';
import * as uuid from 'uuid';
-import Sortable from 'sortablejs';
+import * as Sortable from 'sortablejs';
export default Vue.extend({
props: {
@@ -72,7 +72,6 @@ export default Vue.extend({
},
data() {
return {
- home: [],
bakedHomeData: null,
widgetAdderSelected: null
};
@@ -95,16 +94,15 @@ export default Vue.extend({
},
rightEl(): Element {
return (this.$refs.right as Element[])[0];
+ },
+ home(): any {
+ return (this as any).os.i.client_settings.home;
}
},
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) {
@@ -132,7 +130,7 @@ export default Vue.extend({
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');
+ this.home.find(w => w.id == id).place = evt.to.getAttribute('data-place');
},
onSort: () => {
this.saveHome();
@@ -153,24 +151,15 @@ export default Vue.extend({
}
});
},
- beforeDestroy() {
- (this as any).os.i.off('refreshed', this.onMeRefreshed);
- },
methods: {
bakeHomeData() {
- return JSON.stringify((this as any).os.i.client_settings.home);
+ return JSON.stringify(this.home);
},
onTlLoaded() {
this.$emit('loaded');
},
- onMeRefreshed() {
- if (this.bakedHomeData != this.bakeHomeData()) {
- // TODO: i18n
- alert('別の場所でホームが編集されました。ページを再度読み込みすると編集が反映されます。');
- }
- },
onWidgetContextmenu(widgetId) {
- (this.$refs[widgetId] as any).func();
+ (this.$refs[widgetId] as any)[0].func();
},
addWidget() {
const widget = {
@@ -180,29 +169,13 @@ export default Vue.extend({
data: {}
};
- (this as any).os.i.client_settings.home.unshift(widget);
+ this.home.unshift(widget);
this.saveHome();
},
saveHome() {
- const data = [];
-
- Array.from(this.leftEl.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 = 'left';
- data.push(widget);
- });
-
- Array.from(this.rightEl.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 = 'right';
- data.push(widget);
- });
-
(this as any).api('i/update_home', {
- home: data
+ home: this.home
});
},
warp(date) {
diff --git a/src/web/app/desktop/views/components/index.ts b/src/web/app/desktop/views/components/index.ts
index cbe145daf5..86606a14a2 100644
--- a/src/web/app/desktop/views/components/index.ts
+++ b/src/web/app/desktop/views/components/index.ts
@@ -35,6 +35,7 @@ import wDonation from './widgets/donation.vue';
import wNotifications from './widgets/notifications.vue';
import wBroadcast from './widgets/broadcast.vue';
import wTimemachine from './widgets/timemachine.vue';
+import wProfile from './widgets/profile.vue';
Vue.component('mk-ui', ui);
Vue.component('mk-ui-notification', uiNotification);
@@ -71,3 +72,4 @@ Vue.component('mkw-donation', wDonation);
Vue.component('mkw-notifications', wNotifications);
Vue.component('mkw-broadcast', wBroadcast);
Vue.component('mkw-timemachine', wTimemachine);
+Vue.component('mkw-profile', wProfile);
diff --git a/src/web/app/desktop/views/components/widgets/activity.vue b/src/web/app/desktop/views/components/widgets/activity.vue
index 8bf45a5562..2ff5fe4f03 100644
--- a/src/web/app/desktop/views/components/widgets/activity.vue
+++ b/src/web/app/desktop/views/components/widgets/activity.vue
@@ -10,10 +10,10 @@
import define from '../../../../common/define-widget';
export default define({
name: 'activity',
- props: {
+ props: () => ({
design: 0,
view: 0
- }
+ })
}).extend({
methods: {
func() {
diff --git a/src/web/app/desktop/views/components/widgets/broadcast.vue b/src/web/app/desktop/views/components/widgets/broadcast.vue
index 1a0fd9280c..68c9cebfa2 100644
--- a/src/web/app/desktop/views/components/widgets/broadcast.vue
+++ b/src/web/app/desktop/views/components/widgets/broadcast.vue
@@ -25,9 +25,9 @@ import { lang } from '../../../../config';
export default define({
name: 'broadcast',
- props: {
+ props: () => ({
design: 0
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/components/widgets/calendar.vue b/src/web/app/desktop/views/components/widgets/calendar.vue
index 8574bf59f9..c16602db46 100644
--- a/src/web/app/desktop/views/components/widgets/calendar.vue
+++ b/src/web/app/desktop/views/components/widgets/calendar.vue
@@ -38,9 +38,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'calendar',
- props: {
+ props: () => ({
design: 0
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/components/widgets/channel.channel.form.vue b/src/web/app/desktop/views/components/widgets/channel.channel.form.vue
new file mode 100644
index 0000000000..392ba5924b
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/channel.channel.form.vue
@@ -0,0 +1,67 @@
+<template>
+<div class="form">
+ <input v-model="text" :disabled="wait" @keydown="onKeydown" placeholder="書いて">
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+ data() {
+ return {
+ text: '',
+ wait: false
+ };
+ },
+ methods: {
+ onKeydown(e) {
+ if (e.which == 10 || e.which == 13) this.post();
+ },
+ post() {
+ this.wait = true;
+
+ let reply = null;
+
+ if (/^>>([0-9]+) /.test(this.text)) {
+ const index = this.text.match(/^>>([0-9]+) /)[1];
+ reply = (this.$parent as any).posts.find(p => p.index.toString() == index);
+ this.text = this.text.replace(/^>>([0-9]+) /, '');
+ }
+
+ (this as any).api('posts/create', {
+ text: this.text,
+ reply_id: reply ? reply.id : undefined,
+ channel_id: (this.$parent as any).channel.id
+ }).then(data => {
+ this.text = '';
+ }).catch(err => {
+ alert('失敗した');
+ }).then(() => {
+ this.wait = false;
+ });
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.form
+ width 100%
+ height 38px
+ padding 4px
+ border-top solid 1px #ddd
+
+ > input
+ padding 0 8px
+ width 100%
+ height 100%
+ font-size 14px
+ color #55595c
+ border solid 1px #dadada
+ border-radius 4px
+
+ &:hover
+ &:focus
+ border-color #aeaeae
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/channel.channel.post.vue b/src/web/app/desktop/views/components/widgets/channel.channel.post.vue
new file mode 100644
index 0000000000..faaf0fb731
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/channel.channel.post.vue
@@ -0,0 +1,64 @@
+<template>
+<div class="post">
+ <header>
+ <a class="index" @click="reply">{{ post.index }}:</a>
+ <router-link class="name" :to="`/${post.user.username}`" v-user-preview="post.user.id"><b>{{ post.user.name }}</b></router-link>
+ <span>ID:<i>{{ post.user.username }}</i></span>
+ </header>
+ <div>
+ <a v-if="post.reply">&gt;&gt;{{ post.reply.index }}</a>
+ {{ post.text }}
+ <div class="media" v-if="post.media">
+ <a v-for="file in post.media" :href="file.url" target="_blank">
+ <img :src="`${file.url}?thumbnail&size=512`" :alt="file.name" :title="file.name"/>
+ </a>
+ </div>
+ </div>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+export default Vue.extend({
+ props: ['post'],
+ methods: {
+ reply() {
+ this.$emit('reply', this.post);
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.post
+ margin 0
+ padding 0
+ color #444
+
+ > header
+ position -webkit-sticky
+ position sticky
+ z-index 1
+ top 0
+ padding 8px 4px 4px 16px
+ background rgba(255, 255, 255, 0.9)
+
+ > .index
+ margin-right 0.25em
+
+ > .name
+ margin-right 0.5em
+ color #008000
+
+ > div
+ padding 0 16px 16px 16px
+
+ > .media
+ > a
+ display inline-block
+
+ > img
+ max-width 100%
+ vertical-align bottom
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/channel.channel.vue b/src/web/app/desktop/views/components/widgets/channel.channel.vue
new file mode 100644
index 0000000000..5de13aec03
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/channel.channel.vue
@@ -0,0 +1,104 @@
+<template>
+<div class="channel">
+ <p v-if="fetching">読み込み中<mk-ellipsis/></p>
+ <div v-if="!fetching" ref="posts">
+ <p v-if="posts.length == 0">まだ投稿がありません</p>
+ <x-post class="post" v-for="post in posts.slice().reverse()" :post="post" :key="post.id" @reply="reply"/>
+ </div>
+ <x-form class="form" ref="form"/>
+</div>
+</template>
+
+<script lang="ts">
+import Vue from 'vue';
+import ChannelStream from '../../../../common/scripts/streaming/channel-stream';
+import XForm from './channel.channel.form.vue';
+import XPost from './channel.channel.post.vue';
+
+export default Vue.extend({
+ components: {
+ XForm,
+ XPost
+ },
+ props: ['channel'],
+ data() {
+ return {
+ fetching: true,
+ posts: [],
+ connection: null
+ };
+ },
+ watch: {
+ channel() {
+ this.zap();
+ }
+ },
+ mounted() {
+ this.zap();
+ },
+ beforeDestroy() {
+ this.disconnect();
+ },
+ methods: {
+ zap() {
+ this.fetching = true;
+
+ (this as any).api('channels/posts', {
+ channel_id: this.channel.id
+ }).then(posts => {
+ this.posts = posts;
+ this.fetching = false;
+
+ this.scrollToBottom();
+
+ this.disconnect();
+ this.connection = new ChannelStream(this.channel.id);
+ this.connection.on('post', this.onPost);
+ });
+ },
+ disconnect() {
+ if (this.connection) {
+ this.connection.off('post', this.onPost);
+ this.connection.close();
+ }
+ },
+ onPost(post) {
+ this.posts.unshift(post);
+ this.scrollToBottom();
+ },
+ scrollToBottom() {
+ (this.$refs.posts as any).scrollTop = (this.$refs.posts as any).scrollHeight;
+ },
+ reply(post) {
+ (this.$refs.form as any).text = `>>${ post.index } `;
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.channel
+
+ > p
+ margin 0
+ padding 16px
+ text-align center
+ color #aaa
+
+ > div
+ height calc(100% - 38px)
+ overflow auto
+ font-size 0.9em
+
+ > .post
+ border-bottom solid 1px #eee
+
+ &:last-child
+ border-bottom none
+
+ > .form
+ position absolute
+ left 0
+ bottom 0
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/channel.vue b/src/web/app/desktop/views/components/widgets/channel.vue
new file mode 100644
index 0000000000..484dca9f68
--- /dev/null
+++ b/src/web/app/desktop/views/components/widgets/channel.vue
@@ -0,0 +1,107 @@
+<template>
+<div class="mkw-channel">
+ <template v-if="!data.compact">
+ <p class="title">%fa:tv%{{ channel ? channel.title : '%i18n:desktop.tags.mk-channel-home-widget.title%' }}</p>
+ <button @click="settings" title="%i18n:desktop.tags.mk-channel-home-widget.settings%">%fa:cog%</button>
+ </template>
+ <p class="get-started" v-if="props.channel == null">%i18n:desktop.tags.mk-channel-home-widget.get-started%</p>
+ <x-channel class="channel" :channel="channel" v-else/>
+</div>
+</template>
+
+<script lang="ts">
+import define from '../../../../common/define-widget';
+import XChannel from './channel.channel.vue';
+
+export default define({
+ name: 'server',
+ props: () => ({
+ channel: null,
+ compact: false
+ })
+}).extend({
+ components: {
+ XChannel
+ },
+ data() {
+ return {
+ fetching: true,
+ channel: null
+ };
+ },
+ mounted() {
+ if (this.props.channel) {
+ this.zap();
+ }
+ },
+ methods: {
+ func() {
+ this.props.compact = !this.props.compact;
+ },
+ settings() {
+ const id = window.prompt('チャンネルID');
+ if (!id) return;
+ this.props.channel = id;
+ this.zap();
+ },
+ zap() {
+ this.fetching = true;
+
+ (this as any).api('channels/show', {
+ channel_id: this.props.channel
+ }).then(channel => {
+ this.channel = channel;
+ this.fetching = false;
+ });
+ }
+ }
+});
+</script>
+
+<style lang="stylus" scoped>
+.mkw-channel
+ background #fff
+ border solid 1px rgba(0, 0, 0, 0.075)
+ border-radius 6px
+ overflow hidden
+
+ > .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
+
+ > 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
+
+ > .get-started
+ margin 0
+ padding 16px
+ text-align center
+ color #aaa
+
+ > .channel
+ height 200px
+
+</style>
diff --git a/src/web/app/desktop/views/components/widgets/messaging.vue b/src/web/app/desktop/views/components/widgets/messaging.vue
index 733989b782..039a524f50 100644
--- a/src/web/app/desktop/views/components/widgets/messaging.vue
+++ b/src/web/app/desktop/views/components/widgets/messaging.vue
@@ -9,9 +9,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'messaging',
- props: {
+ props: () => ({
design: 0
- }
+ })
}).extend({
methods: {
navigate(user) {
diff --git a/src/web/app/desktop/views/components/widgets/notifications.vue b/src/web/app/desktop/views/components/widgets/notifications.vue
index 2d613fa232..978cf5218e 100644
--- a/src/web/app/desktop/views/components/widgets/notifications.vue
+++ b/src/web/app/desktop/views/components/widgets/notifications.vue
@@ -12,9 +12,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'notifications',
- props: {
+ props: () => ({
compact: false
- }
+ })
}).extend({
methods: {
settings() {
diff --git a/src/web/app/desktop/views/components/widgets/photo-stream.vue b/src/web/app/desktop/views/components/widgets/photo-stream.vue
index 6ad7d2f064..04b71975b3 100644
--- a/src/web/app/desktop/views/components/widgets/photo-stream.vue
+++ b/src/web/app/desktop/views/components/widgets/photo-stream.vue
@@ -13,9 +13,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'photo-stream',
- props: {
+ props: () => ({
design: 0
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/components/widgets/polls.vue b/src/web/app/desktop/views/components/widgets/polls.vue
index 71d5391b10..f1b34ceed0 100644
--- a/src/web/app/desktop/views/components/widgets/polls.vue
+++ b/src/web/app/desktop/views/components/widgets/polls.vue
@@ -18,9 +18,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'polls',
- props: {
+ props: () => ({
compact: false
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/components/widgets/post-form.vue b/src/web/app/desktop/views/components/widgets/post-form.vue
index c32ad5761a..94b03f84a8 100644
--- a/src/web/app/desktop/views/components/widgets/post-form.vue
+++ b/src/web/app/desktop/views/components/widgets/post-form.vue
@@ -12,9 +12,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'post-form',
- props: {
+ props: () => ({
design: 0
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/components/widgets/profile.vue b/src/web/app/desktop/views/components/widgets/profile.vue
index 9a0d40a5c0..68cf469788 100644
--- a/src/web/app/desktop/views/components/widgets/profile.vue
+++ b/src/web/app/desktop/views/components/widgets/profile.vue
@@ -4,19 +4,19 @@
:data-melt="props.design == 2"
>
<div class="banner"
- style={ I.banner_url ? 'background-image: url(' + I.banner_url + '?thumbnail&size=256)' : '' }
+ :style="os.i.banner_url ? `background-image: url(${os.i.banner_url}?thumbnail&size=256)` : ''"
title="クリックでバナー編集"
- @click="wapi_setBanner"
+ @click="os.apis.updateBanner"
></div>
<img class="avatar"
- src={ I.avatar_url + '?thumbnail&size=96' }
- @click="wapi_setAvatar"
+ :src="`${os.i.avatar_url}?thumbnail&size=96`"
+ @click="os.apis.updateAvatar"
alt="avatar"
title="クリックでアバター編集"
- v-user-preview={ I.id }
+ v-user-preview="os.i.id"
/>
- <a class="name" href={ '/' + I.username }>{ I.name }</a>
- <p class="username">@{ I.username }</p>
+ <router-link class="name" :to="`/${os.i.username}`">{{ os.i.name }}</router-link>
+ <p class="username">@{{ os.i.username }}</p>
</div>
</template>
@@ -24,9 +24,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'profile',
- props: {
+ props: () => ({
design: 0
- }
+ })
}).extend({
methods: {
func() {
diff --git a/src/web/app/desktop/views/components/widgets/rss.vue b/src/web/app/desktop/views/components/widgets/rss.vue
index 954edf3c57..3507129716 100644
--- a/src/web/app/desktop/views/components/widgets/rss.vue
+++ b/src/web/app/desktop/views/components/widgets/rss.vue
@@ -15,9 +15,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'rss',
- props: {
+ props: () => ({
compact: false
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/components/widgets/server.vue b/src/web/app/desktop/views/components/widgets/server.vue
index 00e2f8f186..c08056691b 100644
--- a/src/web/app/desktop/views/components/widgets/server.vue
+++ b/src/web/app/desktop/views/components/widgets/server.vue
@@ -27,10 +27,10 @@ import XInfo from './server.info.vue';
export default define({
name: 'server',
- props: {
+ props: () => ({
design: 0,
view: 0
- }
+ })
}).extend({
components: {
XCpuMemory,
diff --git a/src/web/app/desktop/views/components/widgets/slideshow.vue b/src/web/app/desktop/views/components/widgets/slideshow.vue
index 3c2ef6da4f..75af3c0f1d 100644
--- a/src/web/app/desktop/views/components/widgets/slideshow.vue
+++ b/src/web/app/desktop/views/components/widgets/slideshow.vue
@@ -15,10 +15,10 @@ import * as anime from 'animejs';
import define from '../../../../common/define-widget';
export default define({
name: 'slideshow',
- props: {
+ props: () => ({
folder: undefined,
size: 0
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/components/widgets/timemachine.vue b/src/web/app/desktop/views/components/widgets/timemachine.vue
index d484ce6d74..7420482168 100644
--- a/src/web/app/desktop/views/components/widgets/timemachine.vue
+++ b/src/web/app/desktop/views/components/widgets/timemachine.vue
@@ -8,9 +8,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'timemachine',
- props: {
+ props: () => ({
design: 0
- }
+ })
}).extend({
methods: {
chosen(date) {
diff --git a/src/web/app/desktop/views/components/widgets/trends.vue b/src/web/app/desktop/views/components/widgets/trends.vue
index 23d39563f2..a764639ce9 100644
--- a/src/web/app/desktop/views/components/widgets/trends.vue
+++ b/src/web/app/desktop/views/components/widgets/trends.vue
@@ -17,9 +17,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'trends',
- props: {
+ props: () => ({
compact: false
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/components/widgets/users.vue b/src/web/app/desktop/views/components/widgets/users.vue
index 6876d0bf04..4a9ab2aa33 100644
--- a/src/web/app/desktop/views/components/widgets/users.vue
+++ b/src/web/app/desktop/views/components/widgets/users.vue
@@ -28,9 +28,9 @@ const limit = 3;
export default define({
name: 'users',
- props: {
+ props: () => ({
compact: false
- }
+ })
}).extend({
data() {
return {
diff --git a/src/web/app/desktop/views/pages/home-custmize.vue b/src/web/app/desktop/views/pages/home-customize.vue
index 257e83cad6..8aa06be57f 100644
--- a/src/web/app/desktop/views/pages/home-custmize.vue
+++ b/src/web/app/desktop/views/pages/home-customize.vue
@@ -1,5 +1,5 @@
<template>
- <mk-home customize/>
+<mk-home customize/>
</template>
<script lang="ts">
diff --git a/src/web/app/init.ts b/src/web/app/init.ts
index 02c125efef..e4cb8f8bc0 100644
--- a/src/web/app/init.ts
+++ b/src/web/app/init.ts
@@ -103,6 +103,14 @@ export default (callback: (launch: (api: (os: MiOS) => API) => [Vue, MiOS]) => v
router: new VueRouter({
mode: 'history'
}),
+ created() {
+ this.$watch('os.i', i => {
+ // キャッシュ更新
+ localStorage.setItem('me', JSON.stringify(i));
+ }, {
+ deep: true
+ });
+ },
render: createEl => createEl(App)
}).$mount('#app');