This commit is contained in:
syuilo
2018-02-21 15:30:03 +09:00
parent 1eecc1fa3d
commit de6d77d0cb
29 changed files with 422 additions and 426 deletions

View File

@@ -10,10 +10,10 @@
import define from '../../../../common/define-widget';
export default define({
name: 'activity',
props: {
props: () => ({
design: 0,
view: 0
}
})
}).extend({
methods: {
func() {

View File

@@ -25,9 +25,9 @@ import { lang } from '../../../../config';
export default define({
name: 'broadcast',
props: {
props: () => ({
design: 0
}
})
}).extend({
data() {
return {

View File

@@ -38,9 +38,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'calendar',
props: {
props: () => ({
design: 0
}
})
}).extend({
data() {
return {

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -9,9 +9,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'messaging',
props: {
props: () => ({
design: 0
}
})
}).extend({
methods: {
navigate(user) {

View File

@@ -12,9 +12,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'notifications',
props: {
props: () => ({
compact: false
}
})
}).extend({
methods: {
settings() {

View File

@@ -13,9 +13,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'photo-stream',
props: {
props: () => ({
design: 0
}
})
}).extend({
data() {
return {

View File

@@ -18,9 +18,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'polls',
props: {
props: () => ({
compact: false
}
})
}).extend({
data() {
return {

View File

@@ -12,9 +12,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'post-form',
props: {
props: () => ({
design: 0
}
})
}).extend({
data() {
return {

View File

@@ -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() {

View File

@@ -15,9 +15,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'rss',
props: {
props: () => ({
compact: false
}
})
}).extend({
data() {
return {

View File

@@ -27,10 +27,10 @@ import XInfo from './server.info.vue';
export default define({
name: 'server',
props: {
props: () => ({
design: 0,
view: 0
}
})
}).extend({
components: {
XCpuMemory,

View File

@@ -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 {

View File

@@ -8,9 +8,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'timemachine',
props: {
props: () => ({
design: 0
}
})
}).extend({
methods: {
chosen(date) {

View File

@@ -17,9 +17,9 @@
import define from '../../../../common/define-widget';
export default define({
name: 'trends',
props: {
props: () => ({
compact: false
}
})
}).extend({
data() {
return {

View File

@@ -28,9 +28,9 @@ const limit = 3;
export default define({
name: 'users',
props: {
props: () => ({
compact: false
}
})
}).extend({
data() {
return {