This commit is contained in:
syuilo 2018-02-19 23:37:09 +09:00
parent 55273807d2
commit 69a8e4f4b2
40 changed files with 356 additions and 303 deletions

View File

@ -28,7 +28,6 @@
this.$root.$data.os.api('i/authorized_apps').then(apps => { this.$root.$data.os.api('i/authorized_apps').then(apps => {
this.apps = apps; this.apps = apps;
this.fetching = false; this.fetching = false;
this.update();
}); });
}); });
</script> </script>

View File

@ -78,8 +78,8 @@ export default Vue.extend({
this.connection.on('read', this.onRead); this.connection.on('read', this.onRead);
(this as any).api('messaging/history').then(messages => { (this as any).api('messaging/history').then(messages => {
this.fetching = false;
this.messages = messages; this.messages = messages;
this.fetching = false;
}); });
}, },
beforeDestroy() { beforeDestroy() {

View File

@ -1,246 +0,0 @@
<mk-activity-widget data-melt={ design == 2 }>
<template v-if="design == 0">
<p class="title">%fa:chart-bar%%i18n:desktop.tags.mk-activity-widget.title%</p>
<button @click="toggle" title="%i18n:desktop.tags.mk-activity-widget.toggle%">%fa:sort%</button>
</template>
<p class="initializing" v-if="initializing">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<mk-activity-widget-calender v-if="!initializing && view == 0" data={ [].concat(activity) }/>
<mk-activity-widget-chart v-if="!initializing && view == 1" data={ [].concat(activity) }/>
<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
> .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
> .initializing
margin 0
padding 16px
text-align center
color #aaa
> [data-fa]
margin-right 4px
</style>
<script lang="typescript">
this.mixin('api');
this.design = this.opts.design || 0;
this.view = this.opts.view || 0;
this.user = this.opts.user;
this.initializing = true;
this.on('mount', () => {
this.$root.$data.os.api('aggregation/users/activity', {
user_id: this.user.id,
limit: 20 * 7
}).then(activity => {
this.update({
initializing: false,
activity
});
});
});
this.toggle = () => {
this.view++;
if (this.view == 2) this.view = 0;
this.update();
this.$emit('view-changed', this.view);
};
</script>
</mk-activity-widget>
<mk-activity-widget-calender>
<svg viewBox="0 0 21 7" preserveAspectRatio="none">
<rect each={ data } class="day"
width="1" height="1"
riot-x={ x } riot-y={ date.weekday }
rx="1" ry="1"
fill="transparent">
<title>{ date.year }/{ date.month }/{ date.day }<br/>Post: { posts }, Reply: { replies }, Repost: { reposts }</title>
</rect>
<rect each={ data }
riot-width={ v } riot-height={ v }
riot-x={ x + ((1 - v) / 2) } riot-y={ date.weekday + ((1 - v) / 2) }
rx="1" ry="1"
fill={ color }
style="pointer-events: none;"/>
<rect class="today"
width="1" height="1"
riot-x={ data[data.length - 1].x } riot-y={ data[data.length - 1].date.weekday }
rx="1" ry="1"
fill="none"
stroke-width="0.1"
stroke="#f73520"/>
</svg>
<style lang="stylus" scoped>
:scope
display block
> svg
display block
padding 10px
width 100%
> rect
transform-origin center
&.day
&:hover
fill rgba(0, 0, 0, 0.05)
</style>
<script lang="typescript">
this.data = this.opts.data;
this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
const peak = Math.max.apply(null, this.data.map(d => d.total));
let x = 0;
this.data.reverse().forEach(d => {
d.x = x;
d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
d.v = d.total / (peak / 2);
if (d.v > 1) d.v = 1;
const ch = d.date.weekday == 0 || d.date.weekday == 6 ? 275 : 170;
const cs = d.v * 100;
const cl = 15 + ((1 - d.v) * 80);
d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
if (d.date.weekday == 6) x++;
});
</script>
</mk-activity-widget-calender>
<mk-activity-widget-chart>
<svg riot-viewBox="0 0 { viewBoxX } { viewBoxY }" preserveAspectRatio="none" onmousedown={ onMousedown }>
<title>Black ... Total<br/>Blue ... Posts<br/>Red ... Replies<br/>Green ... Reposts</title>
<polyline
riot-points={ pointsPost }
fill="none"
stroke-width="1"
stroke="#41ddde"/>
<polyline
riot-points={ pointsReply }
fill="none"
stroke-width="1"
stroke="#f7796c"/>
<polyline
riot-points={ pointsRepost }
fill="none"
stroke-width="1"
stroke="#a1de41"/>
<polyline
riot-points={ pointsTotal }
fill="none"
stroke-width="1"
stroke="#555"
stroke-dasharray="2 2"/>
</svg>
<style lang="stylus" scoped>
:scope
display block
> svg
display block
padding 10px
width 100%
cursor all-scroll
</style>
<script lang="typescript">
this.viewBoxX = 140;
this.viewBoxY = 60;
this.zoom = 1;
this.pos = 0;
this.data = this.opts.data.reverse();
this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
const peak = Math.max.apply(null, this.data.map(d => d.total));
this.on('mount', () => {
this.render();
});
this.render = () => {
this.update({
pointsPost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.posts / peak)) * this.viewBoxY}`).join(' '),
pointsReply: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' '),
pointsRepost: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.reposts / peak)) * this.viewBoxY}`).join(' '),
pointsTotal: this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ')
});
};
this.onMousedown = e => {
e.preventDefault();
const clickX = e.clientX;
const clickY = e.clientY;
const baseZoom = this.zoom;
const basePos = this.pos;
// 動かした時
dragListen(me => {
let moveLeft = me.clientX - clickX;
let moveTop = me.clientY - clickY;
this.zoom = baseZoom + (-moveTop / 20);
this.pos = basePos + moveLeft;
if (this.zoom < 1) this.zoom = 1;
if (this.pos > 0) this.pos = 0;
if (this.pos < -(((this.data.length - 1) * this.zoom) - this.viewBoxX)) this.pos = -(((this.data.length - 1) * this.zoom) - this.viewBoxX);
this.render();
});
};
function dragListen(fn) {
window.addEventListener('mousemove', fn);
window.addEventListener('mouseleave', dragClear.bind(null, fn));
window.addEventListener('mouseup', dragClear.bind(null, fn));
}
function dragClear(fn) {
window.removeEventListener('mousemove', fn);
window.removeEventListener('mouseleave', dragClear);
window.removeEventListener('mouseup', dragClear);
}
</script>
</mk-activity-widget-chart>

View File

@ -0,0 +1,66 @@
<template>
<svg viewBox="0 0 21 7" preserveAspectRatio="none">
<rect v-for="record in data" class="day"
width="1" height="1"
:x="record.x" :y="record.date.weekday"
rx="1" ry="1"
fill="transparent">
<title>{{ record.date.year }}/{{ record.date.month }}/{{ record.date.day }}</title>
</rect>
<rect v-for="record in data" class="day"
:width="record.v" :height="record.v"
:x="record.x + ((1 - record.v) / 2)" :y="record.date.weekday + ((1 - record.v) / 2)"
rx="1" ry="1"
:fill="record.color"
style="pointer-events: none;"/>
<rect class="today"
width="1" height="1"
:x="data[data.length - 1].x" :y="data[data.length - 1].date.weekday"
rx="1" ry="1"
fill="none"
stroke-width="0.1"
stroke="#f73520"/>
</svg>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: ['data'],
created() {
this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
const peak = Math.max.apply(null, this.data.map(d => d.total));
let x = 0;
this.data.reverse().forEach(d => {
d.x = x;
d.date.weekday = (new Date(d.date.year, d.date.month - 1, d.date.day)).getDay();
d.v = d.total / (peak / 2);
if (d.v > 1) d.v = 1;
const ch = d.date.weekday == 0 || d.date.weekday == 6 ? 275 : 170;
const cs = d.v * 100;
const cl = 15 + ((1 - d.v) * 80);
d.color = `hsl(${ch}, ${cs}%, ${cl}%)`;
if (d.date.weekday == 6) x++;
});
}
});
</script>
<style lang="stylus" scoped>
svg
display block
padding 10px
width 100%
> rect
transform-origin center
&.day
&:hover
fill rgba(0, 0, 0, 0.05)
</style>

View File

@ -0,0 +1,101 @@
<template>
<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" preserveAspectRatio="none" @mousedown.prevent="onMousedown">
<title>Black ... Total<br/>Blue ... Posts<br/>Red ... Replies<br/>Green ... Reposts</title>
<polyline
:points="pointsPost"
fill="none"
stroke-width="1"
stroke="#41ddde"/>
<polyline
:points="pointsReply"
fill="none"
stroke-width="1"
stroke="#f7796c"/>
<polyline
:points="pointsRepost"
fill="none"
stroke-width="1"
stroke="#a1de41"/>
<polyline
:points="pointsTotal"
fill="none"
stroke-width="1"
stroke="#555"
stroke-dasharray="2 2"/>
</svg>
</template>
<script lang="ts">
import Vue from 'vue';
function dragListen(fn) {
window.addEventListener('mousemove', fn);
window.addEventListener('mouseleave', dragClear.bind(null, fn));
window.addEventListener('mouseup', dragClear.bind(null, fn));
}
function dragClear(fn) {
window.removeEventListener('mousemove', fn);
window.removeEventListener('mouseleave', dragClear);
window.removeEventListener('mouseup', dragClear);
}
export default Vue.extend({
props: ['data'],
data() {
return {
viewBoxX: 140,
viewBoxY: 60,
zoom: 1,
pos: 0,
pointsPost: null,
pointsReply: null,
pointsRepost: null,
pointsTotal: null
};
},
created() {
this.data.reverse();
this.data.forEach(d => d.total = d.posts + d.replies + d.reposts);
this.render();
},
methods: {
render() {
const peak = Math.max.apply(null, this.data.map(d => d.total));
this.pointsPost = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.posts / peak)) * this.viewBoxY}`).join(' ');
this.pointsReply = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.replies / peak)) * this.viewBoxY}`).join(' ');
this.pointsRepost = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.reposts / peak)) * this.viewBoxY}`).join(' ');
this.pointsTotal = this.data.map((d, i) => `${(i * this.zoom) + this.pos},${(1 - (d.total / peak)) * this.viewBoxY}`).join(' ');
},
onMousedown(e) {
const clickX = e.clientX;
const clickY = e.clientY;
const baseZoom = this.zoom;
const basePos = this.pos;
//
dragListen(me => {
let moveLeft = me.clientX - clickX;
let moveTop = me.clientY - clickY;
this.zoom = baseZoom + (-moveTop / 20);
this.pos = basePos + moveLeft;
if (this.zoom < 1) this.zoom = 1;
if (this.pos > 0) this.pos = 0;
if (this.pos < -(((this.data.length - 1) * this.zoom) - this.viewBoxX)) this.pos = -(((this.data.length - 1) * this.zoom) - this.viewBoxX);
this.render();
});
}
}
});
</script>
<style lang="stylus" scoped>
svg
display block
padding 10px
width 100%
cursor all-scroll
</style>

View File

@ -0,0 +1,116 @@
<template>
<div class="mk-activity">
<template v-if="design == 0">
<p class="title">%fa:chart-bar%%i18n:desktop.tags.mk-activity-widget.title%</p>
<button @click="toggle" title="%i18n:desktop.tags.mk-activity-widget.toggle%">%fa:sort%</button>
</template>
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
<template v-else>
<mk-activity-widget-calender v-show="view == 0" :data="[].concat(activity)"/>
<mk-activity-widget-chart v-show="view == 1" :data="[].concat(activity)"/>
</template>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import Calendar from './activity.calendar.vue';
import Chart from './activity.chart.vue';
export default Vue.extend({
components: {
'mk-activity-widget-calender': Calendar,
'mk-activity-widget-chart': Chart
},
props: {
design: {
default: 0
},
initView: {
default: 0
},
user: {
type: Object,
required: true
}
},
data() {
return {
fetching: true,
activity: null,
view: this.initView
};
},
mounted() {
(this as any).api('aggregation/users/activity', {
user_id: this.user.id,
limit: 20 * 7
}).then(activity => {
this.activity = activity;
this.fetching = false;
});
},
methods: {
toggle() {
if (this.view == 1) {
this.view = 0;
this.$emit('viewChanged', this.view);
} else {
this.view++;
this.$emit('viewChanged', this.view);
}
}
}
});
</script>
<style lang="stylus" scoped>
.mk-activity
background #fff
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
&[data-melt]
background transparent !important
border none !important
> .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
> .fetching
margin 0
padding 16px
text-align center
color #aaa
> [data-fa]
margin-right 4px
</style>

View File

@ -47,7 +47,7 @@ export default Vue.extend({
default: 0 default: 0
}, },
start: { start: {
type: Object, type: Date,
required: false required: false
} }
}, },
@ -94,7 +94,7 @@ export default Vue.extend({
isOutOfRange(day) { isOutOfRange(day) {
const test = (new Date(this.year, this.month - 1, day)).getTime(); const test = (new Date(this.year, this.month - 1, day)).getTime();
return test > this.today.getTime() || return test > this.today.getTime() ||
(this.start ? test < this.start.getTime() : false); (this.start ? test < (this.start as any).getTime() : false);
}, },
isDonichi(day) { isDonichi(day) {

View File

@ -1,9 +1,9 @@
<template> <template>
<mk-window width='400px' height='550px' @closed="$destroy"> <mk-window width="400px" height="550px" @closed="$destroy">
<span slot="header" :class="$style.header"> <span slot="header" :class="$style.header">
<img :src="`${user.avatar_url}?thumbnail&size=64`" alt=""/>{{ user.name }}のフォロワー <img :src="`${user.avatar_url}?thumbnail&size=64`" alt=""/>{{ user.name }}のフォロワー
</span> </span>
<mk-user-followers :user="user"/> <mk-followers-list :user="user"/>
</mk-window> </mk-window>
</template> </template>

View File

@ -1,9 +1,9 @@
<template> <template>
<mk-window width='400px' height='550px' @closed="$destroy"> <mk-window width="400px" height="550px" @closed="$destroy">
<span slot="header" :class="$style.header"> <span slot="header" :class="$style.header">
<img :src="`${user.avatar_url}?thumbnail&size=64`" alt=""/>{{ user.name }}のフォロー <img :src="`${user.avatar_url}?thumbnail&size=64`" alt=""/>{{ user.name }}のフォロー
</span> </span>
<mk-user-following :user="user"/> <mk-following-list :user="user"/>
</mk-window> </mk-window>
</template> </template>

View File

@ -43,8 +43,8 @@ export default Vue.extend({
limit: this.limit, limit: this.limit,
offset: this.limit * this.page offset: this.limit * this.page
}).then(users => { }).then(users => {
this.fetching = false;
this.users = users; this.users = users;
this.fetching = false;
}); });
}, },
refresh() { refresh() {

View File

@ -34,6 +34,7 @@ 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 calendar from './calendar.vue';
import activity from './activity.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';
@ -78,6 +79,7 @@ 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('mk-calendar', calendar);
Vue.component('mk-activity', activity);
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);

View File

@ -23,8 +23,8 @@ export default Vue.extend({
}, },
mounted() { mounted() {
(this as any).api('mute/list').then(x => { (this as any).api('mute/list').then(x => {
this.fetching = false;
this.users = x.users; this.users = x.users;
this.fetching = false;
}); });
} }
}); });

View File

@ -4,7 +4,7 @@
class="read-more" class="read-more"
v-if="p.reply && p.reply.reply_id && context == null" v-if="p.reply && p.reply.reply_id && context == null"
title="会話をもっと読み込む" title="会話をもっと読み込む"
@click="loadContext" @click="fetchContext"
:disabled="contextFetching" :disabled="contextFetching"
> >
<template v-if="!contextFetching">%fa:ellipsis-v%</template> <template v-if="!contextFetching">%fa:ellipsis-v%</template>

View File

@ -57,8 +57,8 @@ export default Vue.extend({
(this as any).api('posts/timeline', { (this as any).api('posts/timeline', {
until_date: this.date ? this.date.getTime() : undefined until_date: this.date ? this.date.getTime() : undefined
}).then(posts => { }).then(posts => {
this.fetching = false;
this.posts = posts; this.posts = posts;
this.fetching = false;
if (cb) cb(); if (cb) cb();
}); });
}, },

View File

@ -45,9 +45,9 @@ export default Vue.extend({
_fetch(cb) { _fetch(cb) {
this.fetching = true; this.fetching = true;
this.fetch(this.mode == 'iknow', this.limit, null, obj => { this.fetch(this.mode == 'iknow', this.limit, null, obj => {
this.fetching = false;
this.users = obj.users; this.users = obj.users;
this.next = obj.next; this.next = obj.next;
this.fetching = false;
if (cb) cb(); if (cb) cb();
}); });
}, },

View File

@ -46,8 +46,8 @@ export default define({
} }
}); });
} }
this.fetching = false;
this.broadcasts = broadcasts; this.broadcasts = broadcasts;
this.fetching = false;
}); });
}, },
methods: { methods: {

View File

@ -35,8 +35,8 @@ export default define({
type: 'image/*', type: 'image/*',
limit: 9 limit: 9
}).then(images => { }).then(images => {
this.fetching = false;
this.images = images; this.images = images;
this.fetching = false;
}); });
}, },
beforeDestroy() { beforeDestroy() {

View File

@ -93,8 +93,8 @@ export default define({
type: 'image/*', type: 'image/*',
limit: 100 limit: 100
}).then(images => { }).then(images => {
this.fetching = false;
this.images = images; this.images = images;
this.fetching = false;
(this.$refs.slideA as any).style.backgroundImage = ''; (this.$refs.slideA as any).style.backgroundImage = '';
(this.$refs.slideB as any).style.backgroundImage = ''; (this.$refs.slideB as any).style.backgroundImage = '';
this.change(); this.change();

View File

@ -24,8 +24,8 @@ export default Vue.extend({
(this as any).api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false;
this.user = user; this.user = user;
this.fetching = false;
document.title = 'メッセージ: ' + this.user.name; document.title = 'メッセージ: ' + this.user.name;

View File

@ -26,8 +26,8 @@ export default Vue.extend({
(this as any).api('posts/show', { (this as any).api('posts/show', {
post_id: this.postId post_id: this.postId
}).then(post => { }).then(post => {
this.fetching = false;
this.post = post; this.post = post;
this.fetching = false;
Progress.done(); Progress.done();
}); });

View File

@ -45,8 +45,8 @@ export default Vue.extend({
window.addEventListener('scroll', this.onScroll); window.addEventListener('scroll', this.onScroll);
(this as any).api('posts/search', parse(this.query)).then(posts => { (this as any).api('posts/search', parse(this.query)).then(posts => {
this.fetching = false;
this.posts = posts; this.posts = posts;
this.fetching = false;
}); });
}, },
beforeDestroy() { beforeDestroy() {

View File

@ -27,8 +27,8 @@ export default Vue.extend({
iknow: true, iknow: true,
limit: 16 limit: 16
}).then(x => { }).then(x => {
this.fetching = false;
this.users = x.users; this.users = x.users;
this.fetching = false;
}); });
} }
}); });

View File

@ -2,16 +2,18 @@
<div class="mk-user-friends"> <div class="mk-user-friends">
<p class="title">%fa:users%%i18n:desktop.tags.mk-user.frequently-replied-users.title%</p> <p class="title">%fa:users%%i18n:desktop.tags.mk-user.frequently-replied-users.title%</p>
<p class="initializing" v-if="fetching">%fa:spinner .pulse .fw%%i18n:desktop.tags.mk-user.frequently-replied-users.loading%<mk-ellipsis/></p> <p class="initializing" v-if="fetching">%fa:spinner .pulse .fw%%i18n:desktop.tags.mk-user.frequently-replied-users.loading%<mk-ellipsis/></p>
<div class="user" v-if="!fetching && users.length != 0" each={ _user in users }> <template v-if="!fetching && users.length != 0">
<a class="avatar-anchor" href={ '/' + _user.username }> <div class="user" v-for="friend in users">
<img class="avatar" src={ _user.avatar_url + '?thumbnail&size=42' } alt="" v-user-preview={ _user.id }/> <router-link class="avatar-anchor" to="`/${friend.username}`">
</a> <img class="avatar" :src="`${friend.avatar_url}?thumbnail&size=42`" alt="" v-user-preview="friend.id"/>
<div class="body"> </router-link>
<a class="name" href={ '/' + _user.username } v-user-preview={ _user.id }>{ _user.name }</a> <div class="body">
<p class="username">@{ _user.username }</p> <router-link class="name" to="`/${friend.username}`" v-user-preview="friend.id">{{ friend.name }}</router-link>
<p class="username">@{{ friend.username }}</p>
</div>
<mk-follow-button :user="friend"/>
</div> </div>
<mk-follow-button user={ _user }/> </template>
</div>
<p class="empty" v-if="!fetching && users.length == 0">%i18n:desktop.tags.mk-user.frequently-replied-users.no-users%</p> <p class="empty" v-if="!fetching && users.length == 0">%i18n:desktop.tags.mk-user.frequently-replied-users.no-users%</p>
</div> </div>
</template> </template>
@ -31,8 +33,8 @@ export default Vue.extend({
user_id: this.user.id, user_id: this.user.id,
limit: 4 limit: 4
}).then(docs => { }).then(docs => {
this.fetching = false;
this.users = docs.map(doc => doc.user); this.users = docs.map(doc => doc.user);
this.fetching = false;
}); });
} }
}); });

View File

@ -14,8 +14,8 @@
</main> </main>
<div> <div>
<div ref="right"> <div ref="right">
<mk-calendar-widget @warp="warp" :start="new Date(user.created_at)"/> <mk-calendar @chosen="warp" :start="new Date(user.created_at)"/>
<mk-activity-widget :user="user"/> <mk-activity :user="user"/>
<mk-user-friends :user="user"/> <mk-user-friends :user="user"/>
<div class="nav"><mk-nav/></div> <div class="nav"><mk-nav/></div>
</div> </div>
@ -25,7 +25,20 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
import MkUserTimeline from './user-timeline.vue';
import MkUserProfile from './user-profile.vue';
import MkUserPhotos from './user-photos.vue';
import MkUserFollowersYouKnow from './user-followers-you-know.vue';
import MkUserFriends from './user-friends.vue';
export default Vue.extend({ export default Vue.extend({
components: {
'mk-user-timeline': MkUserTimeline,
'mk-user-profile': MkUserProfile,
'mk-user-photos': MkUserPhotos,
'mk-user-followers-you-know': MkUserFollowersYouKnow,
'mk-user-friends': MkUserFriends
},
props: ['user'], props: ['user'],
methods: { methods: {
warp(date) { warp(date) {

View File

@ -3,8 +3,7 @@
<p class="title">%fa:camera%%i18n:desktop.tags.mk-user.photos.title%</p> <p class="title">%fa:camera%%i18n:desktop.tags.mk-user.photos.title%</p>
<p class="initializing" v-if="fetching">%fa:spinner .pulse .fw%%i18n:desktop.tags.mk-user.photos.loading%<mk-ellipsis/></p> <p class="initializing" v-if="fetching">%fa:spinner .pulse .fw%%i18n:desktop.tags.mk-user.photos.loading%<mk-ellipsis/></p>
<div class="stream" v-if="!fetching && images.length > 0"> <div class="stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" :key="image.id" <div v-for="image in images" class="img"
class="img"
:style="`background-image: url(${image.url}?thumbnail&size=256)`" :style="`background-image: url(${image.url}?thumbnail&size=256)`"
></div> ></div>
</div> </div>
@ -28,12 +27,12 @@ export default Vue.extend({
with_media: true, with_media: true,
limit: 9 limit: 9
}).then(posts => { }).then(posts => {
this.fetching = false;
posts.forEach(post => { posts.forEach(post => {
post.media.forEach(media => { post.media.forEach(media => {
if (this.images.length < 9) this.images.push(media); if (this.images.length < 9) this.images.push(media);
}); });
}); });
this.fetching = false;
}); });
} }
}); });

View File

@ -14,7 +14,7 @@
<p>%fa:B twitter%<a :href="`https://twitter.com/${user.twitter.screen_name}`" target="_blank">@{{ user.twitter.screen_name }}</a></p> <p>%fa:B twitter%<a :href="`https://twitter.com/${user.twitter.screen_name}`" target="_blank">@{{ user.twitter.screen_name }}</a></p>
</div> </div>
<div class="status"> <div class="status">
<p class="posts-count">%fa:angle-right%<a>{{ user.posts_count }}</a><b>投稿</b></p> <p class="posts-count">%fa:angle-right%<a>{{ user.posts_count }}</a><b>投稿</b></p>
<p class="following">%fa:angle-right%<a @click="showFollowing">{{ user.following_count }}</a>人を<b>フォロー</b></p> <p class="following">%fa:angle-right%<a @click="showFollowing">{{ user.following_count }}</a>人を<b>フォロー</b></p>
<p class="followers">%fa:angle-right%<a @click="showFollowers">{{ user.followers_count }}</a>人の<b>フォロワー</b></p> <p class="followers">%fa:angle-right%<a @click="showFollowers">{{ user.followers_count }}</a>人の<b>フォロワー</b></p>
</div> </div>
@ -23,7 +23,9 @@
<script lang="ts"> <script lang="ts">
import Vue from 'vue'; import Vue from 'vue';
const age = require('s-age'); import age from 's-age';
import MkFollowingWindow from '../../components/following-window.vue';
import MkFollowersWindow from '../../components/followers-window.vue';
export default Vue.extend({ export default Vue.extend({
props: ['user'], props: ['user'],
@ -34,8 +36,7 @@ export default Vue.extend({
}, },
methods: { methods: {
showFollowing() { showFollowing() {
document.body.appendChild(new MkUserFollowingWindow({ document.body.appendChild(new MkFollowingWindow({
propsData: { propsData: {
user: this.user user: this.user
} }
@ -43,8 +44,7 @@ export default Vue.extend({
}, },
showFollowers() { showFollowers() {
document.body.appendChild(new MkUserFollowersWindow({ document.body.appendChild(new MkFollowersWindow({
propsData: { propsData: {
user: this.user user: this.user
} }
@ -56,7 +56,7 @@ export default Vue.extend({
user_id: this.user.id user_id: this.user.id
}).then(() => { }).then(() => {
this.user.is_muted = true; this.user.is_muted = true;
}, e => { }, () => {
alert('error'); alert('error');
}); });
}, },
@ -66,7 +66,7 @@ export default Vue.extend({
user_id: this.user.id user_id: this.user.id
}).then(() => { }).then(() => {
this.user.is_muted = false; this.user.is_muted = false;
}, e => { }, () => {
alert('error'); alert('error');
}); });
} }

View File

@ -65,8 +65,8 @@ export default Vue.extend({
until_date: this.date ? this.date.getTime() : undefined, until_date: this.date ? this.date.getTime() : undefined,
with_replies: this.mode == 'with-replies' with_replies: this.mode == 'with-replies'
}).then(posts => { }).then(posts => {
this.fetching = false;
this.posts = posts; this.posts = posts;
this.fetching = false;
if (cb) cb(); if (cb) cb();
}); });
}, },

View File

@ -35,8 +35,8 @@ export default Vue.extend({
(this as any).api('users/show', { (this as any).api('users/show', {
username: this.$route.params.user username: this.$route.params.user
}).then(user => { }).then(user => {
this.fetching = false;
this.user = user; this.user = user;
this.fetching = false;
Progress.done(); Progress.done();
document.title = user.name + ' | Misskey'; document.title = user.name + ' | Misskey';
}); });

View File

@ -351,13 +351,14 @@ export default Vue.extend({
(this as any).api('drive/files/show', { (this as any).api('drive/files/show', {
file_id: file file_id: file
}).then(file => { }).then(file => {
this.fetching = false;
this.file = file; this.file = file;
this.folder = null; this.folder = null;
this.hierarchyFolders = []; this.hierarchyFolders = [];
if (file.folder) this.dive(file.folder); if (file.folder) this.dive(file.folder);
this.fetching = false;
this.$emit('open-file', this.file, silent); this.$emit('open-file', this.file, silent);
}); });
}, },

View File

@ -36,8 +36,8 @@ export default Vue.extend({
limit: this.limit, limit: this.limit,
offset: this.limit * this.page offset: this.limit * this.page
}).then(users => { }).then(users => {
this.fetching = false;
this.users = users; this.users = users;
this.fetching = false;
}); });
}, },
refresh() { refresh() {

View File

@ -63,8 +63,8 @@ export default Vue.extend({
(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 as any).getTime() : undefined
}).then(posts => { }).then(posts => {
this.fetching = false;
this.posts = posts; this.posts = posts;
this.fetching = false;
if (cb) cb(); if (cb) cb();
}); });
}, },

View File

@ -31,8 +31,8 @@ export default Vue.extend({
user_id: this.user.id, user_id: this.user.id,
with_media: this.withMedia with_media: this.withMedia
}).then(posts => { }).then(posts => {
this.fetching = false;
this.posts = posts; this.posts = posts;
this.fetching = false;
this.$emit('loaded'); this.$emit('loaded');
}); });
} }

View File

@ -41,9 +41,9 @@ export default Vue.extend({
_fetch(cb) { _fetch(cb) {
this.fetching = true; this.fetching = true;
this.fetch(this.mode == 'iknow', this.limit, null, obj => { this.fetch(this.mode == 'iknow', this.limit, null, obj => {
this.fetching = false;
this.users = obj.users; this.users = obj.users;
this.next = obj.next; this.next = obj.next;
this.fetching = false;
if (cb) cb(); if (cb) cb();
}); });
}, },

View File

@ -26,8 +26,8 @@ export default Vue.extend({
(this as any).api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false;
this.user = user; this.user = user;
this.fetching = false;
document.title = '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name) + ' | Misskey'; document.title = '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name) + ' | Misskey';
document.documentElement.style.background = '#313a42'; document.documentElement.style.background = '#313a42';

View File

@ -26,8 +26,8 @@ export default Vue.extend({
(this as any).api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false;
this.user = user; this.user = user;
this.fetching = false;
document.title = '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name) + ' | Misskey'; document.title = '%i18n:mobile.tags.mk-user-followers-page.followers-of%'.replace('{}', user.name) + ' | Misskey';
document.documentElement.style.background = '#313a42'; document.documentElement.style.background = '#313a42';

View File

@ -32,8 +32,8 @@ export default Vue.extend({
(this as any).api('posts/show', { (this as any).api('posts/show', {
post_id: this.postId post_id: this.postId
}).then(post => { }).then(post => {
this.fetching = false;
this.post = post; this.post = post;
this.fetching = false;
Progress.done(); Progress.done();
}); });

View File

@ -88,8 +88,8 @@ export default Vue.extend({
(this as any).api('users/show', { (this as any).api('users/show', {
username: this.username username: this.username
}).then(user => { }).then(user => {
this.fetching = false;
this.user = user; this.user = user;
this.fetching = false;
Progress.done(); Progress.done();
document.title = user.name + ' | Misskey'; document.title = user.name + ' | Misskey';

View File

@ -22,8 +22,8 @@ export default Vue.extend({
(this as any).api('users/get_frequently_replied_users', { (this as any).api('users/get_frequently_replied_users', {
user_id: this.user.id user_id: this.user.id
}).then(res => { }).then(res => {
this.fetching = false;
this.users = res.map(x => x.user); this.users = res.map(x => x.user);
this.fetching = false;
}); });
} }
}); });

View File

@ -28,7 +28,6 @@ export default Vue.extend({
with_media: true, with_media: true,
limit: 6 limit: 6
}).then(posts => { }).then(posts => {
this.fetching = false;
posts.forEach(post => { posts.forEach(post => {
post.media.forEach(media => { post.media.forEach(media => {
if (this.images.length < 9) this.images.push({ if (this.images.length < 9) this.images.push({
@ -37,6 +36,7 @@ export default Vue.extend({
}); });
}); });
}); });
this.fetching = false;
}); });
} }
}); });

View File

@ -22,8 +22,8 @@ export default Vue.extend({
(this as any).api('users/posts', { (this as any).api('users/posts', {
user_id: this.user.id user_id: this.user.id
}).then(posts => { }).then(posts => {
this.fetching = false;
this.posts = posts; this.posts = posts;
this.fetching = false;
}); });
} }
}); });