This commit is contained in:
syuilo 2018-02-15 00:32:13 +09:00
parent df136a7fb2
commit c70e56dd1d
9 changed files with 749 additions and 879 deletions

View file

@ -0,0 +1,79 @@
<template>
<div class="mk-user-followers-you-know">
<p class="title">%fa:users%%i18n:desktop.tags.mk-user.followers-you-know.title%</p>
<p class="initializing" v-if="initializing">%fa:spinner .pulse .fw%%i18n:desktop.tags.mk-user.followers-you-know.loading%<mk-ellipsis/></p>
<div v-if="!initializing && users.length > 0">
<template each={ user in users }>
<a href={ '/' + user.username }><img src={ user.avatar_url + '?thumbnail&size=64' } alt={ user.name }/></a>
</template>
</div>
<p class="empty" v-if="!initializing && users.length == 0">%i18n:desktop.tags.mk-user.followers-you-know.no-users%</p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: ['user'],
data() {
return {
users: [],
fetching: true
};
},
mounted() {
this.$root.$data.os.api('users/followers', {
user_id: this.user.id,
iknow: true,
limit: 16
}).then(x => {
this.fetching = false;
this.users = x.users;
});
}
});
</script>
<style lang="stylus" scoped>
.mk-user-followers-you-know
background #fff
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
> .title
z-index 1
margin 0
padding 0 16px
line-height 42px
font-size 0.9em
font-weight bold
color #888
box-shadow 0 1px rgba(0, 0, 0, 0.07)
> i
margin-right 4px
> div
padding 8px
> a
display inline-block
margin 4px
> img
width 48px
height 48px
vertical-align bottom
border-radius 100%
> .initializing
> .empty
margin 0
padding 16px
text-align center
color #aaa
> i
margin-right 4px
</style>

View file

@ -0,0 +1,117 @@
<template>
<div class="mk-user-friends">
<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>
<div class="user" v-if="!fetching && users.length != 0" each={ _user in users }>
<a class="avatar-anchor" href={ '/' + _user.username }>
<img class="avatar" src={ _user.avatar_url + '?thumbnail&size=42' } alt="" data-user-preview={ _user.id }/>
</a>
<div class="body">
<a class="name" href={ '/' + _user.username } data-user-preview={ _user.id }>{ _user.name }</a>
<p class="username">@{ _user.username }</p>
</div>
<mk-follow-button user={ _user }/>
</div>
<p class="empty" v-if="!fetching && users.length == 0">%i18n:desktop.tags.mk-user.frequently-replied-users.no-users%</p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: ['user'],
data() {
return {
users: [],
fetching: true
};
},
mounted() {
this.$root.$data.os.api('users/get_frequently_replied_users', {
user_id: this.user.id,
limit: 4
}).then(docs => {
this.fetching = false;
this.users = docs.map(doc => doc.user);
});
}
});
</script>
<style lang="stylus" scoped>
.mk-user-friends
background #fff
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
> .title
z-index 1
margin 0
padding 0 16px
line-height 42px
font-size 0.9em
font-weight bold
color #888
box-shadow 0 1px rgba(0, 0, 0, 0.07)
> i
margin-right 4px
> .initializing
> .empty
margin 0
padding 16px
text-align center
color #aaa
> i
margin-right 4px
> .user
padding 16px
border-bottom solid 1px #eee
&:last-child
border-bottom none
&:after
content ""
display block
clear both
> .avatar-anchor
display block
float left
margin 0 12px 0 0
> .avatar
display block
width 42px
height 42px
margin 0
border-radius 8px
vertical-align bottom
> .body
float left
width calc(100% - 54px)
> .name
margin 0
font-size 16px
line-height 24px
color #555
> .username
display block
margin 0
font-size 15px
line-height 16px
color #ccc
> mk-follow-button
position absolute
top 16px
right 16px
</style>

View file

@ -0,0 +1,189 @@
<template>
<div class="mk-user-header" :data-is-dark-background="user.banner_url != null">
<div class="banner-container" :style="user.banner_url ? `background-image: url(${user.banner_url}?thumbnail&size=2048)` : ''">
<div class="banner" ref="banner" :style="user.banner_url ? `background-image: url(${user.banner_url}?thumbnail&size=2048)` : ''" @click="onBannerClick"></div>
</div>
<div class="fade"></div>
<div class="container">
<img class="avatar" :src="`${user.avatar_url}?thumbnail&size=150`" alt="avatar"/>
<div class="title">
<p class="name">{{ user.name }}</p>
<p class="username">@{{ user.username }}</p>
<p class="location" v-if="user.profile.location">%fa:map-marker%{{ user.profile.location }}</p>
</div>
<footer>
<a :href="`/${user.username}`" :data-active="$parent.page == 'home'">%fa:home%概要</a>
<a :href="`/${user.username}/media`" :data-active="$parent.page == 'media'">%fa:image%メディア</a>
<a :href="`/${user.username}/graphs`" :data-active="$parent.page == 'graphs'">%fa:chart-bar%グラフ</a>
</footer>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import updateBanner from '../../../scripts/update-banner';
export default Vue.extend({
props: ['user'],
mounted() {
window.addEventListener('load', this.onScroll);
window.addEventListener('scroll', this.onScroll);
window.addEventListener('resize', this.onScroll);
},
beforeDestroy() {
window.removeEventListener('load', this.onScroll);
window.removeEventListener('scroll', this.onScroll);
window.removeEventListener('resize', this.onScroll);
},
methods: {
onScroll() {
const banner = this.$refs.banner as any;
const top = window.scrollY;
const z = 1.25; // ()
const pos = -(top / z);
banner.style.backgroundPosition = `center calc(50% - ${pos}px)`;
const blur = top / 32
if (blur <= 10) banner.style.filter = `blur(${blur}px)`;
},
onBannerClick() {
if (!this.$root.$data.os.isSignedIn || this.$root.$data.os.i.id != this.user.id) return;
updateBanner(this.$root.$data.os.i, i => {
this.user.banner_url = i.banner_url;
});
}
}
});
</script>
<style lang="stylus" scoped>
.mk-user-header
$banner-height = 320px
$footer-height = 58px
overflow hidden
background #f7f7f7
box-shadow 0 1px 1px rgba(0, 0, 0, 0.075)
&[data-is-dark-background]
> .banner-container
> .banner
background-color #383838
> .fade
background linear-gradient(transparent, rgba(0, 0, 0, 0.7))
> .container
> .title
color #fff
> .name
text-shadow 0 0 8px #000
> .banner-container
height $banner-height
overflow hidden
background-size cover
background-position center
> .banner
height 100%
background-color #f5f5f5
background-size cover
background-position center
> .fade
$fade-hight = 78px
position absolute
top ($banner-height - $fade-hight)
left 0
width 100%
height $fade-hight
> .container
max-width 1200px
margin 0 auto
> .avatar
display block
position absolute
bottom 16px
left 16px
z-index 2
width 160px
height 160px
margin 0
border solid 3px #fff
border-radius 8px
box-shadow 1px 1px 3px rgba(0, 0, 0, 0.2)
> .title
position absolute
bottom $footer-height
left 0
width 100%
padding 0 0 8px 195px
color #656565
font-family '游ゴシック', 'YuGothic', 'ヒラギノ角ゴ ProN W3', 'Hiragino Kaku Gothic ProN', 'Meiryo', 'メイリオ', sans-serif
> .name
display block
margin 0
line-height 40px
font-weight bold
font-size 2em
> .username
> .location
display inline-block
margin 0 16px 0 0
line-height 20px
opacity 0.8
> i
margin-right 4px
> footer
z-index 1
height $footer-height
padding-left 195px
> a
display inline-block
margin 0
padding 0 16px
height $footer-height
line-height $footer-height
color #555
&[data-active]
border-bottom solid 4px $theme-color
> i
margin-right 6px
> button
display block
position absolute
top 0
right 0
margin 8px
padding 0
width $footer-height - 16px
line-height $footer-height - 16px - 2px
font-size 1.2em
color #777
border solid 1px #eee
border-radius 4px
&:hover
color #555
border solid 1px #ddd
</style>

View file

@ -0,0 +1,90 @@
<template>
<div class="mk-user-home">
<div>
<div ref="left">
<mk-user-profile :user="user"/>
<mk-user-photos :user="user"/>
<mk-user-followers-you-know v-if="$root.$data.os.isSignedIn && $root.$data.os.i.id != user.id" :user="user"/>
<p>%i18n:desktop.tags.mk-user.last-used-at%: <b><mk-time :time="user.last_used_at"/></b></p>
</div>
</div>
<main>
<mk-post-detail v-if="user.pinned_post" :post="user.pinned_post" compact/>
<mk-user-timeline ref="tl" :user="user"/>
</main>
<div>
<div ref="right">
<mk-calendar-widget @warp="warp" :start="new Date(user.created_at)"/>
<mk-activity-widget :user="user"/>
<mk-user-friends :user="user"/>
<div class="nav"><mk-nav-links/></div>
</div>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: ['user'],
methods: {
warp(date) {
(this.$refs.tl as any).warp(date);
}
}
});
</script>
<style lang="stylus" scoped>
.mk-user-home
display flex
justify-content center
margin 0 auto
max-width 1200px
> main
> div > div
> *:not(:last-child)
margin-bottom 16px
> main
padding 16px
width calc(100% - 275px * 2)
> mk-user-timeline
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
> div
width 275px
margin 0
&:first-child > div
padding 16px 0 16px 16px
> p
display block
margin 0
padding 0 12px
text-align center
font-size 0.8em
color #aaa
&:last-child > div
padding 16px 16px 16px 0
> .nav
padding 16px
font-size 12px
color #aaa
background #fff
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
a
color #999
i
color #ccc
</style>

View file

@ -0,0 +1,89 @@
<template>
<div class="mk-user-photos">
<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>
<div class="stream" v-if="!fetching && images.length > 0">
<div v-for="image in images" :key="image.id"
class="img"
:style="`background-image: url(${image.url}?thumbnail&size=256)`"
></div>
</div>
<p class="empty" v-if="!fetching && images.length == 0">%i18n:desktop.tags.mk-user.photos.no-photos%</p>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: ['user'],
data() {
return {
images: [],
fetching: true
};
},
mounted() {
this.$root.$data.os.api('users/posts', {
user_id: this.user.id,
with_media: true,
limit: 9
}).then(posts => {
this.fetching = false;
posts.forEach(post => {
post.media.forEach(media => {
if (this.images.length < 9) this.images.push(media);
});
});
});
}
});
</script>
<style lang="stylus" scoped>
.mk-user-photos
background #fff
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
> .title
z-index 1
margin 0
padding 0 16px
line-height 42px
font-size 0.9em
font-weight bold
color #888
box-shadow 0 1px rgba(0, 0, 0, 0.07)
> i
margin-right 4px
> .stream
display -webkit-flex
display -moz-flex
display -ms-flex
display flex
justify-content center
flex-wrap wrap
padding 8px
> .img
flex 1 1 33%
width 33%
height 80px
background-position center center
background-size cover
background-clip content-box
border solid 2px transparent
> .initializing
> .empty
margin 0
padding 16px
text-align center
color #aaa
> i
margin-right 4px
</style>

View file

@ -0,0 +1,142 @@
<template>
<div class="mk-user-profile">
<div class="friend-form" v-if="$root.$data.os.isSignedIn && $root.$data.os.i.id != user.id">
<mk-follow-button :user="user" size="big"/>
<p class="followed" v-if="user.is_followed">%i18n:desktop.tags.mk-user.follows-you%</p>
<p v-if="user.is_muted">%i18n:desktop.tags.mk-user.muted% <a @click="unmute">%i18n:desktop.tags.mk-user.unmute%</a></p>
<p v-if="!user.is_muted"><a @click="mute">%i18n:desktop.tags.mk-user.mute%</a></p>
</div>
<div class="description" v-if="user.description">{{ user.description }}</div>
<div class="birthday" v-if="user.profile.birthday">
<p>%fa:birthday-cake%{{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' }} ({{ age }})</p>
</div>
<div class="twitter" v-if="user.twitter">
<p>%fa:B twitter%<a :href="`https://twitter.com/${user.twitter.screen_name}`" target="_blank">@{{ user.twitter.screen_name }}</a></p>
</div>
<div class="status">
<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="followers">%fa:angle-right%<a @click="showFollowers">{{ user.followers_count }}</a>人の<b>フォロワー</b></p>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
const age = require('s-age');
export default Vue.extend({
props: ['user'],
computed: {
age(): number {
return age(this.user.profile.birthday);
}
},
methods: {
showFollowing() {
document.body.appendChild(new MkUserFollowingWindow({
parent: this,
propsData: {
user: this.user
}
}).$mount().$el);
},
showFollowers() {
document.body.appendChild(new MkUserFollowersWindow({
parent: this,
propsData: {
user: this.user
}
}).$mount().$el);
},
mute() {
this.$root.$data.os.api('mute/create', {
user_id: this.user.id
}).then(() => {
this.user.is_muted = true;
}, e => {
alert('error');
});
},
unmute() {
this.$root.$data.os.api('mute/delete', {
user_id: this.user.id
}).then(() => {
this.user.is_muted = false;
}, e => {
alert('error');
});
}
}
});
</script>
<style lang="stylus" scoped>
.mk-user-profile
background #fff
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
> *:first-child
border-top none !important
> .friend-form
padding 16px
border-top solid 1px #eee
> mk-big-follow-button
width 100%
> .followed
margin 12px 0 0 0
padding 0
text-align center
line-height 24px
font-size 0.8em
color #71afc7
background #eefaff
border-radius 4px
> .description
padding 16px
color #555
border-top solid 1px #eee
> .birthday
padding 16px
color #555
border-top solid 1px #eee
> p
margin 0
> i
margin-right 8px
> .twitter
padding 16px
color #555
border-top solid 1px #eee
> p
margin 0
> i
margin-right 8px
> .status
padding 16px
color #555
border-top solid 1px #eee
> p
margin 8px 0
> i
margin-left 8px
margin-right 8px
</style>

View file

@ -0,0 +1,43 @@
<template>
<mk-ui>
<div class="user" v-if="!fetching">
<mk-user-header :user="user"/>
<mk-user-home v-if="page == 'home'" :user="user"/>
<mk-user-graphs v-if="page == 'graphs'" :user="user"/>
</div>
</mk-ui>
</template>
<script lang="ts">
import Vue from 'vue';
import Progress from '../../../common/scripts/loading';
export default Vue.extend({
props: {
username: {
type: String
},
page: {
default: 'home'
}
},
data() {
return {
fetching: true,
user: null
};
},
mounted() {
Progress.start();
this.$root.$data.os.api('users/show', {
username: this.username
}).then(user => {
this.fetching = false;
this.user = user;
Progress.done();
document.title = user.name + ' | Misskey';
});
}
});
</script>