なんか

This commit is contained in:
syuilo 2017-11-14 08:21:19 +09:00
parent f44e039d9a
commit 107ecfb07f
11 changed files with 727 additions and 672 deletions

View file

@ -46,9 +46,17 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
const [maxId, maxIdErr] = $(params.max_id).optional.id().$;
if (maxIdErr) return rej('invalid max_id param');
// Check if both of since_id and max_id is specified
if (sinceId && maxId) {
return rej('cannot set since_id and max_id');
// Get 'since_date' parameter
const [sinceDate, sinceDateErr] = $(params.since_date).optional.number().$;
if (sinceDateErr) throw 'invalid since_date param';
// Get 'max_date' parameter
const [maxDate, maxDateErr] = $(params.max_date).optional.number().$;
if (maxDateErr) throw 'invalid max_date param';
// Check if only one of since_id, max_id, since_date, max_date specified
if ([sinceId, maxId, sinceDate, maxDate].filter(x => x != null).length > 1) {
throw 'only one of since_id, max_id, since_date, max_date can be specified';
}
const q = userId !== undefined
@ -66,13 +74,15 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
return rej('user not found');
}
// Construct query
//#region Construct query
const sort = {
_id: -1
};
const query = {
user_id: user._id
} as any;
if (sinceId) {
sort._id = 1;
query._id = {
@ -82,6 +92,15 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
query._id = {
$lt: maxId
};
} else if (sinceDate) {
sort._id = 1;
query.created_at = {
$gt: new Date(sinceDate)
};
} else if (maxDate) {
query.created_at = {
$lt: new Date(maxDate)
};
}
if (!includeReplies) {
@ -94,6 +113,7 @@ module.exports = (params, me) => new Promise(async (res, rej) => {
$ne: null
};
}
//#endregion
// Issue query
const posts = await Post

View file

@ -0,0 +1,241 @@
<mk-calendar data-melt={ opts.design == 4 || opts.design == 5 }>
<virtual if={ opts.design == 0 || opts.design == 1 }>
<button onclick={ prev } title="%i18n:desktop.tags.mk-calendar.prev%"><i class="fa fa-chevron-circle-left"></i></button>
<p class="title">{ '%i18n:desktop.tags.mk-calendar.title%'.replace('{1}', year).replace('{2}', month) }</p>
<button onclick={ next } title="%i18n:desktop.tags.mk-calendar.next%"><i class="fa fa-chevron-circle-right"></i></button>
</virtual>
<div class="calendar">
<div class="weekday" if={ opts.design == 0 || opts.design == 2 || opts.design == 4} each={ day, i in Array(7).fill(0) }
data-today={ year == today.getFullYear() && month == today.getMonth() + 1 && today.getDay() == i }
data-is-donichi={ i == 0 || i == 6 }>{ weekdayText[i] }</div>
<div each={ day, i in Array(paddingDays).fill(0) }></div>
<div class="day" each={ day, i in Array(days).fill(0) }
data-today={ isToday(i + 1) }
data-selected={ isSelected(i + 1) }
data-is-out-of-range={ isOutOfRange(i + 1) }
data-is-donichi={ isDonichi(i + 1) }
onclick={ go.bind(null, i + 1) }
title={ isOutOfRange(i + 1) ? null : '%i18n:desktop.tags.mk-calendar.go%' }><div>{ i + 1 }</div></div>
</div>
<style>
:scope
display block
color #777
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
text-align center
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
> button
position absolute
z-index 2
top 0
padding 0
width 42px
font-size 0.9em
line-height 42px
color #ccc
&:hover
color #aaa
&:active
color #999
&:first-of-type
left 0
&:last-of-type
right 0
> .calendar
display flex
flex-wrap wrap
padding 16px
*
user-select none
> div
width calc(100% * (1/7))
text-align center
line-height 32px
font-size 14px
&.weekday
color #19a2a9
&[data-is-donichi]
color #ef95a0
&[data-today]
box-shadow 0 0 0 1px #19a2a9 inset
border-radius 6px
&[data-is-donichi]
box-shadow 0 0 0 1px #ef95a0 inset
&.day
cursor pointer
color #777
> div
border-radius 6px
&:hover > div
background rgba(0, 0, 0, 0.025)
&:active > div
background rgba(0, 0, 0, 0.05)
&[data-is-donichi]
color #ef95a0
&[data-is-out-of-range]
cursor default
color rgba(#777, 0.5)
&[data-is-donichi]
color rgba(#ef95a0, 0.5)
&[data-selected]
font-weight bold
> div
background rgba(0, 0, 0, 0.025)
&:active > div
background rgba(0, 0, 0, 0.05)
&[data-today]
> div
color $theme-color-foreground
background $theme-color
&:hover > div
background lighten($theme-color, 10%)
&:active > div
background darken($theme-color, 10%)
</style>
<script>
if (this.opts.design == null) this.opts.design = 0;
const eachMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
function isLeapYear(year) {
return (year % 400 == 0) ? true :
(year % 100 == 0) ? false :
(year % 4 == 0) ? true :
false;
}
this.today = new Date();
this.year = this.today.getFullYear();
this.month = this.today.getMonth() + 1;
this.selected = this.today;
this.weekdayText = [
'%i18n:common.weekday-short.sunday%',
'%i18n:common.weekday-short.monday%',
'%i18n:common.weekday-short.tuesday%',
'%i18n:common.weekday-short.wednesday%',
'%i18n:common.weekday-short.thursday%',
'%i18n:common.weekday-short.friday%',
'%i18n:common.weekday-short.satruday%'
];
this.on('mount', () => {
this.calc();
});
this.isToday = day => {
return this.year == this.today.getFullYear() && this.month == this.today.getMonth() + 1 && day == this.today.getDate();
};
this.isSelected = day => {
return this.year == this.selected.getFullYear() && this.month == this.selected.getMonth() + 1 && day == this.selected.getDate();
};
this.isOutOfRange = day => {
const test = (new Date(this.year, this.month - 1, day)).getTime();
return test > this.today.getTime() ||
(this.opts.start ? test < this.opts.start.getTime() : false);
};
this.isDonichi = day => {
const weekday = (new Date(this.year, this.month - 1, day)).getDay();
return weekday == 0 || weekday == 6;
};
this.calc = () => {
let days = eachMonthDays[this.month - 1];
// うるう年なら+1日
if (this.month == 2 && isLeapYear(this.year)) days++;
const date = new Date(this.year, this.month - 1, 1);
const weekday = date.getDay();
this.update({
paddingDays: weekday,
days: days
});
};
this.prev = () => {
if (this.month == 1) {
this.update({
year: this.year - 1,
month: 12
});
} else {
this.update({
month: this.month - 1
});
}
this.calc();
};
this.next = () => {
if (this.month == 12) {
this.update({
year: this.year + 1,
month: 1
});
} else {
this.update({
month: this.month + 1
});
}
this.calc();
};
this.go = day => {
if (this.isOutOfRange(day)) return;
const date = new Date(this.year, this.month - 1, day, 23, 59, 59, 999);
this.update({
selected: date
});
this.opts.warp(date);
};
</script>
</mk-calendar>

View file

@ -1,139 +1,10 @@
<mk-timemachine-home-widget data-melt={ data.design == 4 || data.design == 5 }>
<virtual if={ data.design == 0 || data.design == 1 }>
<button onclick={ prev } title="%i18n:desktop.tags.mk-timemachine-home-widget.prev%"><i class="fa fa-chevron-circle-left"></i></button>
<p class="title">{ '%i18n:desktop.tags.mk-timemachine-home-widget.title%'.replace('{1}', year).replace('{2}', month) }</p>
<button onclick={ next } title="%i18n:desktop.tags.mk-timemachine-home-widget.next%"><i class="fa fa-chevron-circle-right"></i></button>
</virtual>
<div class="calendar">
<div class="weekday" if={ data.design == 0 || data.design == 2 || data.design == 4} each={ day, i in Array(7).fill(0) }
data-today={ year == today.getFullYear() && month == today.getMonth() + 1 && today.getDay() == i }
data-is-donichi={ i == 0 || i == 6 }>{ weekdayText[i] }</div>
<div each={ day, i in Array(paddingDays).fill(0) }></div>
<div class="day" each={ day, i in Array(days).fill(0) }
data-today={ isToday(i + 1) }
data-selected={ isSelected(i + 1) }
data-is-future={ isFuture(i + 1) }
data-is-donichi={ isDonichi(i + 1) }
onclick={ go.bind(null, i + 1) }
title={ isFuture(i + 1) ? null : '%i18n:desktop.tags.mk-timemachine-home-widget.go%' }><div>{ i + 1 }</div></div>
</div>
<mk-timemachine-home-widget>
<mk-calendar design={ data.design } warp={ warp }/>
<style>
:scope
display block
color #777
background #fff
&[data-melt]
background transparent !important
border none !important
> .title
z-index 1
margin 0
padding 0 16px
text-align center
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
> button
position absolute
z-index 2
top 0
padding 0
width 42px
font-size 0.9em
line-height 42px
color #ccc
&:hover
color #aaa
&:active
color #999
&:first-of-type
left 0
&:last-of-type
right 0
> .calendar
display flex
flex-wrap wrap
padding 16px
*
user-select none
> div
width calc(100% * (1/7))
text-align center
line-height 32px
font-size 14px
&.weekday
color #19a2a9
&[data-is-donichi]
color #ef95a0
&[data-today]
box-shadow 0 0 0 1px #19a2a9 inset
border-radius 6px
&[data-is-donichi]
box-shadow 0 0 0 1px #ef95a0 inset
&.day
cursor pointer
color #777
> div
border-radius 6px
&:hover > div
background rgba(0, 0, 0, 0.025)
&:active > div
background rgba(0, 0, 0, 0.05)
&[data-is-donichi]
color #ef95a0
&[data-is-future]
cursor default
color rgba(#777, 0.5)
&[data-is-donichi]
color rgba(#ef95a0, 0.5)
&[data-selected]
font-weight bold
> div
background rgba(0, 0, 0, 0.025)
&:active > div
background rgba(0, 0, 0, 0.05)
&[data-today]
> div
color $theme-color-foreground
background $theme-color
&:hover > div
background lighten($theme-color, 10%)
&:active > div
background darken($theme-color, 10%)
background transparent !important
border none !important
</style>
<script>
this.data = {
@ -142,99 +13,7 @@
this.mixin('widget');
const eachMonthDays = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
function isLeapYear(year) {
return (year % 400 == 0) ? true :
(year % 100 == 0) ? false :
(year % 4 == 0) ? true :
false;
}
this.today = new Date();
this.year = this.today.getFullYear();
this.month = this.today.getMonth() + 1;
this.selected = this.today;
this.weekdayText = [
'%i18n:common.weekday-short.sunday%',
'%i18n:common.weekday-short.monday%',
'%i18n:common.weekday-short.tuesday%',
'%i18n:common.weekday-short.wednesday%',
'%i18n:common.weekday-short.thursday%',
'%i18n:common.weekday-short.friday%',
'%i18n:common.weekday-short.satruday%'
];
this.on('mount', () => {
this.calc();
});
this.isToday = day => {
return this.year == this.today.getFullYear() && this.month == this.today.getMonth() + 1 && day == this.today.getDate();
};
this.isSelected = day => {
return this.year == this.selected.getFullYear() && this.month == this.selected.getMonth() + 1 && day == this.selected.getDate();
};
this.isFuture = day => {
return (new Date(this.year, this.month - 1, day)).getTime() > this.today.getTime();
};
this.isDonichi = day => {
const weekday = (new Date(this.year, this.month - 1, day)).getDay();
return weekday == 0 || weekday == 6;
};
this.calc = () => {
let days = eachMonthDays[this.month - 1];
// うるう年なら+1日
if (this.month == 2 && isLeapYear(this.year)) days++;
const date = new Date(this.year, this.month - 1, 1);
const weekday = date.getDay();
this.update({
paddingDays: weekday,
days: days
});
};
this.prev = () => {
if (this.month == 1) {
this.update({
year: this.year - 1,
month: 12
});
} else {
this.update({
month: this.month - 1
});
}
this.calc();
};
this.next = () => {
if (this.month == 12) {
this.update({
year: this.year + 1,
month: 1
});
} else {
this.update({
month: this.month + 1
});
}
this.calc();
};
this.go = day => {
if (this.isFuture(day)) return;
const date = new Date(this.year, this.month - 1, day, 23, 59, 59, 999);
this.update({
selected: date
});
this.warp = date => {
this.opts.tl.warp(date);
};

View file

@ -52,13 +52,8 @@ require('./following-setuper.tag');
require('./ellipsis-icon.tag');
require('./ui.tag');
require('./home.tag');
require('./user-header.tag');
require('./user-profile.tag');
require('./user-timeline.tag');
require('./user.tag');
require('./user-home.tag');
require('./user-graphs.tag');
require('./user-photos.tag');
require('./big-follow-button.tag');
require('./pages/entrance.tag');
require('./pages/entrance/signin.tag');
@ -93,3 +88,4 @@ require('./user-following-window.tag');
require('./user-followers-window.tag');
require('./list-user.tag');
require('./detailed-post-window.tag');
require('./calendar.tag');

View file

@ -1,41 +0,0 @@
<mk-user-graphs>
<section>
<h1>投稿</h1>
<mk-user-posts-graph user={ opts.user }/>
</section>
<section>
<h1>フォロー/フォロワー</h1>
<mk-user-friends-graph user={ opts.user }/>
</section>
<section>
<h1>いいね</h1>
<mk-user-likes-graph user={ opts.user }/>
</section>
<style>
:scope
display block
> section
margin 16px 0
background #fff
border solid 1px rgba(0, 0, 0, 0.1)
border-radius 4px
> h1
margin 0 0 8px 0
padding 0 16px
line-height 40px
font-size 1em
color #666
border-bottom solid 1px #eee
> *:not(h1)
margin 0 auto 16px auto
</style>
<script>
this.on('mount', () => {
this.trigger('loaded');
});
</script>
</mk-user-graphs>

View file

@ -1,147 +0,0 @@
<mk-user-header data-is-dark-background={ user.banner_url != null }>
<div class="banner" ref="banner" style={ user.banner_url ? 'background-image: url(' + user.banner_url + '?thumbnail&size=1024)' : '' } onclick={ onUpdateBanner }></div><img class="avatar" src={ user.avatar_url + '?thumbnail&size=150' } alt="avatar"/>
<div class="title">
<p class="name" href={ '/' + user.username }>{ user.name }</p>
<p class="username">@{ user.username }</p>
<p class="location" if={ user.profile.location }><i class="fa fa-map-marker"></i>{ user.profile.location }</p>
</div>
<footer>
<a href={ '/' + user.username }>投稿</a>
<a href={ '/' + user.username + '/media' }>メディア</a>
<a href={ '/' + user.username + '/graphs' }>グラフ</a>
</footer>
<style>
:scope
$footer-height = 58px
display block
background #fff
&[data-is-dark-background]
> .banner
background-color #383838
> .title
color #fff
background linear-gradient(transparent, rgba(0, 0, 0, 0.7))
> .name
text-shadow 0 0 8px #000
> .banner
height 280px
background-color #f5f5f5
background-size cover
background-position center
> .avatar
display block
position absolute
bottom 16px
left 16px
z-index 2
width 150px
height 150px
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
background #fff
> a
display inline-block
margin 0
width 100px
line-height $footer-height
color #555
> 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>
<script>
import updateBanner from '../scripts/update-banner';
this.mixin('i');
this.user = this.opts.user;
this.on('mount', () => {
window.addEventListener('load', this.scroll);
window.addEventListener('scroll', this.scroll);
window.addEventListener('resize', this.scroll);
});
this.on('unmount', () => {
window.removeEventListener('load', this.scroll);
window.removeEventListener('scroll', this.scroll);
window.removeEventListener('resize', this.scroll);
});
this.scroll = () => {
const top = window.scrollY;
const height = 280/*px*/;
const pos = 50 - ((top / height) * 50);
this.refs.banner.style.backgroundPosition = `center ${pos}%`;
const blur = top / 32
if (blur <= 10) this.refs.banner.style.filter = `blur(${blur}px)`;
};
this.onUpdateBanner = () => {
if (!this.SIGNIN || this.I.id != this.user.id) return;
updateBanner(this.I, i => {
this.user.banner_url = i.banner_url;
this.update();
});
};
</script>
</mk-user-header>

View file

@ -1,46 +0,0 @@
<mk-user-home>
<div class="side">
<mk-user-profile user={ user }/>
<mk-user-photos user={ user }/>
</div>
<main>
<mk-user-timeline ref="tl" user={ user }/>
</main>
<style>
:scope
display flex
justify-content center
> *
> *
display block
//border solid 1px #eaeaea
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
&:not(:last-child)
margin-bottom 16px
> main
flex 1 1 560px
max-width 560px
margin 0
padding 16px 0 16px 16px
> .side
flex 1 1 270px
max-width 270px
margin 0
padding 16px 0 16px 0
</style>
<script>
this.user = this.opts.user;
this.on('mount', () => {
this.refs.tl.on('loaded', () => {
this.trigger('loaded');
});
});
</script>
</mk-user-home>

View file

@ -1,91 +0,0 @@
<mk-user-photos>
<p class="title"><i class="fa fa-camera"></i>フォト</p>
<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます<mk-ellipsis/></p>
<div class="stream" if={ !initializing && images.length > 0 }>
<virtual each={ image in images }>
<div class="img" style={ 'background-image: url(' + image.url + '?thumbnail&size=256)' }></div>
</virtual>
</div>
<p class="empty" if={ !initializing && images.length == 0 }>写真はありません</p>
<style>
:scope
display block
background #fff
> .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>
<script>
import isPromise from '../../common/scripts/is-promise';
this.mixin('api');
this.images = [];
this.initializing = true;
this.user = null;
this.userPromise = isPromise(this.opts.user)
? this.opts.user
: Promise.resolve(this.opts.user);
this.on('mount', () => {
this.userPromise.then(user => {
this.update({
user: user
});
this.api('users/posts', {
user_id: this.user.id,
with_media: true,
limit: 9
}).then(posts => {
this.initializing = false;
posts.forEach(post => {
post.media.forEach(media => {
if (this.images.length < 9) this.images.push(media);
});
});
this.update();
});
});
});
</script>
</mk-user-photos>

View file

@ -1,102 +0,0 @@
<mk-user-profile>
<div class="friend-form" if={ SIGNIN && I.id != user.id }>
<mk-big-follow-button user={ user }/>
<p class="followed" if={ user.is_followed }>フォローされています</p>
</div>
<div class="description" if={ user.description }>{ user.description }</div>
<div class="birthday" if={ user.profile.birthday }>
<p><i class="fa fa-birthday-cake"></i>{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.profile.birthday) }歳)</p>
</div>
<div class="twitter" if={ user.twitter }>
<p><i class="fa fa-twitter"></i><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"><i class="fa fa-angle-right"></i><a>{ user.posts_count }</a><b>ポスト</b></p>
<p class="following"><i class="fa fa-angle-right"></i><a onclick={ showFollowing }>{ user.following_count }</a>人を<b>フォロー</b></p>
<p class="followers"><i class="fa fa-angle-right"></i><a onclick={ showFollowers }>{ user.followers_count }</a>人の<b>フォロワー</b></p>
</div>
<style>
:scope
display block
background #fff
> *: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>
<script>
this.age = require('s-age');
this.mixin('i');
this.user = this.opts.user;
this.showFollowing = () => {
riot.mount(document.body.appendChild(document.createElement('mk-user-following-window')), {
user: this.user
});
};
this.showFollowers = () => {
riot.mount(document.body.appendChild(document.createElement('mk-user-followers-window')), {
user: this.user
});
};
</script>
</mk-user-profile>

View file

@ -91,6 +91,7 @@
this.fetch = cb => {
this.api('users/posts', {
user_id: this.user.id,
max_date: this.date ? this.date.getTime() : undefined,
with_replies: this.mode == 'with-replies'
}).then(posts => {
this.update({
@ -132,5 +133,13 @@
});
this.fetch();
};
this.warp = date => {
this.update({
date: date
});
this.fetch();
};
</script>
</mk-user-timeline>

View file

@ -3,10 +3,8 @@
<header>
<mk-user-header user={ user }/>
</header>
<div class="body">
<mk-user-home if={ page == 'home' } user={ user }/>
<mk-user-graphs if={ page == 'graphs' } user={ user }/>
</div>
<mk-user-home if={ page == 'home' } user={ user }/>
<mk-user-graphs if={ page == 'graphs' } user={ user }/>
</div>
<style>
:scope
@ -14,7 +12,7 @@
> .user
> header
max-width 560px + 270px
max-width 1200px
margin 0 auto
padding 0 16px
@ -24,11 +22,6 @@
border-radius 0 0 6px 6px
overflow hidden
> .body
max-width 560px + 270px
margin 0 auto
padding 0 16px
</style>
<script>
this.mixin('api');
@ -51,3 +44,447 @@
});
</script>
</mk-user>
<mk-user-header data-is-dark-background={ user.banner_url != null }>
<div class="banner" ref="banner" style={ user.banner_url ? 'background-image: url(' + user.banner_url + '?thumbnail&size=1024)' : '' } onclick={ onUpdateBanner }></div>
<img class="avatar" src={ user.avatar_url + '?thumbnail&size=150' } alt="avatar"/>
<div class="title">
<p class="name" href={ '/' + user.username }>{ user.name }</p>
<p class="username">@{ user.username }</p>
<p class="location" if={ user.profile.location }><i class="fa fa-map-marker"></i>{ user.profile.location }</p>
</div>
<footer>
<a href={ '/' + user.username }>投稿</a>
<a href={ '/' + user.username + '/media' }>メディア</a>
<a href={ '/' + user.username + '/graphs' }>グラフ</a>
</footer>
<style>
:scope
$footer-height = 58px
display block
background #fff
&[data-is-dark-background]
> .banner
background-color #383838
> .title
color #fff
background linear-gradient(transparent, rgba(0, 0, 0, 0.7))
> .name
text-shadow 0 0 8px #000
> .banner
height 280px
background-color #f5f5f5
background-size cover
background-position center
> .avatar
display block
position absolute
bottom 16px
left 16px
z-index 2
width 150px
height 150px
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
background #fff
> a
display inline-block
margin 0
width 100px
line-height $footer-height
color #555
> 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>
<script>
import updateBanner from '../scripts/update-banner';
this.mixin('i');
this.user = this.opts.user;
this.on('mount', () => {
window.addEventListener('load', this.scroll);
window.addEventListener('scroll', this.scroll);
window.addEventListener('resize', this.scroll);
});
this.on('unmount', () => {
window.removeEventListener('load', this.scroll);
window.removeEventListener('scroll', this.scroll);
window.removeEventListener('resize', this.scroll);
});
this.scroll = () => {
const top = window.scrollY;
const height = 280/*px*/;
const pos = 50 - ((top / height) * 50);
this.refs.banner.style.backgroundPosition = `center ${pos}%`;
const blur = top / 32
if (blur <= 10) this.refs.banner.style.filter = `blur(${blur}px)`;
};
this.onUpdateBanner = () => {
if (!this.SIGNIN || this.I.id != this.user.id) return;
updateBanner(this.I, i => {
this.user.banner_url = i.banner_url;
this.update();
});
};
</script>
</mk-user-header>
<mk-user-profile>
<div class="friend-form" if={ SIGNIN && I.id != user.id }>
<mk-big-follow-button user={ user }/>
<p class="followed" if={ user.is_followed }>フォローされています</p>
</div>
<div class="description" if={ user.description }>{ user.description }</div>
<div class="birthday" if={ user.profile.birthday }>
<p><i class="fa fa-birthday-cake"></i>{ user.profile.birthday.replace('-', '年').replace('-', '月') + '日' } ({ age(user.profile.birthday) }歳)</p>
</div>
<div class="twitter" if={ user.twitter }>
<p><i class="fa fa-twitter"></i><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"><i class="fa fa-angle-right"></i><a>{ user.posts_count }</a><b>ポスト</b></p>
<p class="following"><i class="fa fa-angle-right"></i><a onclick={ showFollowing }>{ user.following_count }</a>人を<b>フォロー</b></p>
<p class="followers"><i class="fa fa-angle-right"></i><a onclick={ showFollowers }>{ user.followers_count }</a>人の<b>フォロワー</b></p>
</div>
<style>
:scope
display block
background #fff
> *: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>
<script>
this.age = require('s-age');
this.mixin('i');
this.user = this.opts.user;
this.showFollowing = () => {
riot.mount(document.body.appendChild(document.createElement('mk-user-following-window')), {
user: this.user
});
};
this.showFollowers = () => {
riot.mount(document.body.appendChild(document.createElement('mk-user-followers-window')), {
user: this.user
});
};
</script>
</mk-user-profile>
<mk-user-photos>
<p class="title"><i class="fa fa-camera"></i>フォト</p>
<p class="initializing" if={ initializing }><i class="fa fa-spinner fa-pulse fa-fw"></i>読み込んでいます<mk-ellipsis/></p>
<div class="stream" if={ !initializing && images.length > 0 }>
<virtual each={ image in images }>
<div class="img" style={ 'background-image: url(' + image.url + '?thumbnail&size=256)' }></div>
</virtual>
</div>
<p class="empty" if={ !initializing && images.length == 0 }>写真はありません</p>
<style>
:scope
display block
background #fff
> .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>
<script>
import isPromise from '../../common/scripts/is-promise';
this.mixin('api');
this.images = [];
this.initializing = true;
this.user = null;
this.userPromise = isPromise(this.opts.user)
? this.opts.user
: Promise.resolve(this.opts.user);
this.on('mount', () => {
this.userPromise.then(user => {
this.update({
user: user
});
this.api('users/posts', {
user_id: this.user.id,
with_media: true,
limit: 9
}).then(posts => {
this.initializing = false;
posts.forEach(post => {
post.media.forEach(media => {
if (this.images.length < 9) this.images.push(media);
});
});
this.update();
});
});
});
</script>
</mk-user-photos>
<mk-user-home>
<div>
<mk-user-profile user={ user }/>
<mk-user-photos user={ user }/>
</div>
<main>
<mk-user-timeline ref="tl" user={ user }/>
</main>
<div>
<mk-calendar warp={ warp } start={ new Date(user.created_at) }/>
</div>
<style>
:scope
display flex
justify-content center
margin 0 auto
max-width 1200px
> *
> *
display block
//border solid 1px #eaeaea
border solid 1px rgba(0, 0, 0, 0.075)
border-radius 6px
&:not(:last-child)
margin-bottom 16px
> main
padding 16px
width calc(100% - 275px * 2)
> div
width 275px
margin 0
&:first-child
padding 16px 0 16px 16px
&:last-child
padding 16px 16px 16px 0
</style>
<script>
this.user = this.opts.user;
this.on('mount', () => {
this.refs.tl.on('loaded', () => {
this.trigger('loaded');
});
});
this.warp = date => {
this.refs.tl.warp(date);
};
</script>
</mk-user-home>
<mk-user-graphs>
<section>
<h1>投稿</h1>
<mk-user-posts-graph user={ opts.user }/>
</section>
<section>
<h1>フォロー/フォロワー</h1>
<mk-user-friends-graph user={ opts.user }/>
</section>
<section>
<h1>いいね</h1>
<mk-user-likes-graph user={ opts.user }/>
</section>
<style>
:scope
display block
> section
margin 16px 0
background #fff
border solid 1px rgba(0, 0, 0, 0.1)
border-radius 4px
> h1
margin 0 0 8px 0
padding 0 16px
line-height 40px
font-size 1em
color #666
border-bottom solid 1px #eee
> *:not(h1)
margin 0 auto 16px auto
</style>
<script>
this.on('mount', () => {
this.trigger('loaded');
});
</script>
</mk-user-graphs>