Post --> Note

Closes #1411
This commit is contained in:
syuilo
2018-04-08 02:30:37 +09:00
parent c7106d250c
commit a1b490afa7
167 changed files with 4440 additions and 1762 deletions

View File

@@ -1,24 +1,24 @@
import PostForm from '../views/components/post-form.vue';
//import RepostForm from '../views/components/repost-form.vue';
import getPostSummary from '../../../../renderers/get-post-summary';
//import RenoteForm from '../views/components/renote-form.vue';
import getNoteSummary from '../../../../renderers/get-note-summary';
export default (os) => (opts) => {
const o = opts || {};
if (o.repost) {
/*const vm = new RepostForm({
if (o.renote) {
/*const vm = new RenoteForm({
propsData: {
repost: o.repost
renote: o.renote
}
}).$mount();
vm.$once('cancel', recover);
vm.$once('post', recover);
vm.$once('note', recover);
document.body.appendChild(vm.$el);*/
const text = window.prompt(`${getPostSummary(o.repost)}」をRepost`);
const text = window.prompt(`${getNoteSummary(o.renote)}」をRenote`);
if (text == null) return;
os.api('posts/create', {
repostId: o.repost.id,
os.api('notes/create', {
renoteId: o.renote.id,
text: text == '' ? undefined : text
});
} else {
@@ -36,7 +36,7 @@ export default (os) => (opts) => {
}
}).$mount();
vm.$once('cancel', recover);
vm.$once('post', recover);
vm.$once('note', recover);
document.body.appendChild(vm.$el);
(vm as any).focus();
}

View File

@@ -25,7 +25,7 @@ import MkDrive from './views/pages/drive.vue';
import MkNotifications from './views/pages/notifications.vue';
import MkMessaging from './views/pages/messaging.vue';
import MkMessagingRoom from './views/pages/messaging-room.vue';
import MkPost from './views/pages/post.vue';
import MkNote from './views/pages/note.vue';
import MkSearch from './views/pages/search.vue';
import MkFollowers from './views/pages/followers.vue';
import MkFollowing from './views/pages/following.vue';
@@ -68,7 +68,7 @@ init((launch) => {
{ path: '/@:user', component: MkUser },
{ path: '/@:user/followers', component: MkFollowers },
{ path: '/@:user/following', component: MkFollowing },
{ path: '/@:user/:post', component: MkPost }
{ path: '/@:user/:note', component: MkNote }
]
});

View File

@@ -2,14 +2,14 @@
<div class="mk-activity">
<svg v-if="data" ref="canvas" viewBox="0 0 30 1" preserveAspectRatio="none">
<g v-for="(d, i) in data">
<rect width="0.8" :height="d.postsH"
:x="i + 0.1" :y="1 - d.postsH - d.repliesH - d.repostsH"
<rect width="0.8" :height="d.notesH"
:x="i + 0.1" :y="1 - d.notesH - d.repliesH - d.renotesH"
fill="#41ddde"/>
<rect width="0.8" :height="d.repliesH"
:x="i + 0.1" :y="1 - d.repliesH - d.repostsH"
:x="i + 0.1" :y="1 - d.repliesH - d.renotesH"
fill="#f7796c"/>
<rect width="0.8" :height="d.repostsH"
:x="i + 0.1" :y="1 - d.repostsH"
<rect width="0.8" :height="d.renotesH"
:x="i + 0.1" :y="1 - d.renotesH"
fill="#a1de41"/>
</g>
</svg>
@@ -32,12 +32,12 @@ export default Vue.extend({
userId: this.user.id,
limit: 30
}).then(data => {
data.forEach(d => d.total = d.posts + d.replies + d.reposts);
data.forEach(d => d.total = d.notes + d.replies + d.renotes);
this.peak = Math.max.apply(null, data.map(d => d.total));
data.forEach(d => {
d.postsH = d.posts / this.peak;
d.notesH = d.notes / this.peak;
d.repliesH = d.replies / this.peak;
d.repostsH = d.reposts / this.peak;
d.renotesH = d.renotes / this.peak;
});
data.reverse();
this.data = data;

View File

@@ -2,16 +2,16 @@ import Vue from 'vue';
import ui from './ui.vue';
import timeline from './timeline.vue';
import post from './post.vue';
import posts from './posts.vue';
import note from './note.vue';
import notes from './notes.vue';
import mediaImage from './media-image.vue';
import mediaVideo from './media-video.vue';
import drive from './drive.vue';
import postPreview from './post-preview.vue';
import subPostContent from './sub-post-content.vue';
import postCard from './post-card.vue';
import notePreview from './note-preview.vue';
import subNoteContent from './sub-note-content.vue';
import noteCard from './note-card.vue';
import userCard from './user-card.vue';
import postDetail from './post-detail.vue';
import noteDetail from './note-detail.vue';
import followButton from './follow-button.vue';
import friendsMaker from './friends-maker.vue';
import notification from './notification.vue';
@@ -25,16 +25,16 @@ import widgetContainer from './widget-container.vue';
Vue.component('mk-ui', ui);
Vue.component('mk-timeline', timeline);
Vue.component('mk-post', post);
Vue.component('mk-posts', posts);
Vue.component('mk-note', note);
Vue.component('mk-notes', notes);
Vue.component('mk-media-image', mediaImage);
Vue.component('mk-media-video', mediaVideo);
Vue.component('mk-drive', drive);
Vue.component('mk-post-preview', postPreview);
Vue.component('mk-sub-post-content', subPostContent);
Vue.component('mk-post-card', postCard);
Vue.component('mk-note-preview', notePreview);
Vue.component('mk-sub-note-content', subNoteContent);
Vue.component('mk-note-card', noteCard);
Vue.component('mk-user-card', userCard);
Vue.component('mk-post-detail', postDetail);
Vue.component('mk-note-detail', noteDetail);
Vue.component('mk-follow-button', followButton);
Vue.component('mk-friends-maker', friendsMaker);
Vue.component('mk-notification', notification);

View File

@@ -1,41 +1,41 @@
<template>
<div class="mk-post-card">
<a :href="`/@${acct}/${post.id}`">
<div class="mk-note-card">
<a :href="`/@${acct}/${note.id}`">
<header>
<img :src="`${acct}?thumbnail&size=64`" alt="avatar"/><h3>{{ name }}</h3>
</header>
<div>
{{ text }}
</div>
<mk-time :time="post.createdAt"/>
<mk-time :time="note.createdAt"/>
</a>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import summary from '../../../../../renderers/get-post-summary';
import summary from '../../../../../renderers/get-note-summary';
import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
export default Vue.extend({
props: ['post'],
props: ['note'],
computed: {
acct() {
return getAcct(this.post.user);
return getAcct(this.note.user);
},
name() {
return getUserName(this.post.user);
return getUserName(this.note.user);
},
text(): string {
return summary(this.post);
return summary(this.note);
}
}
});
</script>
<style lang="stylus" scoped>
.mk-post-card
.mk-note-card
display inline-block
width 150px
//height 120px

View File

@@ -1,18 +1,18 @@
<template>
<div class="root sub">
<router-link class="avatar-anchor" :to="`/@${acct}`">
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
</router-link>
<div class="main">
<header>
<router-link class="name" :to="`/@${acct}`">{{ getUserName(post.user) }}</router-link>
<router-link class="name" :to="`/@${acct}`">{{ getUserName(note.user) }}</router-link>
<span class="username">@{{ acct }}</span>
<router-link class="time" :to="`/@${acct}/${post.id}`">
<mk-time :time="post.createdAt"/>
<router-link class="time" :to="`/@${acct}/${note.id}`">
<mk-time :time="note.createdAt"/>
</router-link>
</header>
<div class="body">
<mk-sub-post-content class="text" :post="post"/>
<mk-sub-note-content class="text" :note="note"/>
</div>
</div>
</div>
@@ -24,13 +24,13 @@ import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
export default Vue.extend({
props: ['post'],
props: ['note'],
computed: {
acct() {
return getAcct(this.post.user);
return getAcct(this.note.user);
},
name() {
return getUserName(this.post.user);
return getUserName(this.note.user);
}
}
});

View File

@@ -0,0 +1,462 @@
<template>
<div class="mk-note-detail">
<button
class="more"
v-if="p.reply && p.reply.replyId && context == null"
@click="fetchContext"
:disabled="fetchingContext"
>
<template v-if="!contextFetching">%fa:ellipsis-v%</template>
<template v-if="contextFetching">%fa:spinner .pulse%</template>
</button>
<div class="context">
<x-sub v-for="note in context" :key="note.id" :note="note"/>
</div>
<div class="reply-to" v-if="p.reply">
<x-sub :note="p.reply"/>
</div>
<div class="renote" v-if="isRenote">
<p>
<router-link class="avatar-anchor" :to="`/@${acct}`">
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
</router-link>
%fa:retweet%
<router-link class="name" :to="`/@${acct}`">
{{ name }}
</router-link>
がRenote
</p>
</div>
<article>
<header>
<router-link class="avatar-anchor" :to="`/@${pAcct}`">
<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
</router-link>
<div>
<router-link class="name" :to="`/@${pAcct}`">{{ pName }}</router-link>
<span class="username">@{{ pAcct }}</span>
</div>
</header>
<div class="body">
<mk-note-html v-if="p.text" :ast="p.text" :i="os.i" :class="$style.text"/>
<div class="tags" v-if="p.tags && p.tags.length > 0">
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :note="p"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
<div class="map" v-if="p.geo" ref="map"></div>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote"/>
</div>
</div>
<router-link class="time" :to="`/@${pAcct}/${p.id}`">
<mk-time :time="p.createdAt" mode="detail"/>
</router-link>
<footer>
<mk-reactions-viewer :note="p"/>
<button @click="reply" title="%i18n:mobile.tags.mk-note-detail.reply%">
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
</button>
<button @click="renote" title="Renote">
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
</button>
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-note-detail.reaction%">
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
</button>
<button @click="menu" ref="menuButton">
%fa:ellipsis-h%
</button>
</footer>
</article>
<div class="replies" v-if="!compact">
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
</div>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
import parse from '../../../../../text/parse';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
import XSub from './note-detail.sub.vue';
export default Vue.extend({
components: {
XSub
},
props: {
note: {
type: Object,
required: true
},
compact: {
default: false
}
},
data() {
return {
context: [],
contextFetching: false,
replies: []
};
},
computed: {
acct(): string {
return getAcct(this.note.user);
},
name(): string {
return getUserName(this.note.user);
},
pAcct(): string {
return getAcct(this.p.user);
},
pName(): string {
return getUserName(this.p.user);
},
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.mediaIds == null &&
this.note.poll == null);
},
p(): any {
return this.isRenote ? this.note.renote : this.note;
},
reactionsCount(): number {
return this.p.reactionCounts
? Object.keys(this.p.reactionCounts)
.map(key => this.p.reactionCounts[key])
.reduce((a, b) => a + b)
: 0;
},
urls(): string[] {
if (this.p.text) {
const ast = parse(this.p.text);
return ast
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
.map(t => t.url);
} else {
return null;
}
}
},
mounted() {
// Get replies
if (!this.compact) {
(this as any).api('notes/replies', {
noteId: this.p.id,
limit: 8
}).then(replies => {
this.replies = replies;
});
}
// Draw map
if (this.p.geo) {
const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
if (shouldShowMap) {
(this as any).os.getGoogleMaps().then(maps => {
const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
const map = new maps.Map(this.$refs.map, {
center: uluru,
zoom: 15
});
new maps.Marker({
position: uluru,
map: map
});
});
}
}
},
methods: {
fetchContext() {
this.contextFetching = true;
// Fetch context
(this as any).api('notes/context', {
noteId: this.p.replyId
}).then(context => {
this.contextFetching = false;
this.context = context.reverse();
});
},
reply() {
(this as any).apis.post({
reply: this.p
});
},
renote() {
(this as any).apis.post({
renote: this.p
});
},
react() {
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.p,
compact: true
});
},
menu() {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.p,
compact: true
});
}
}
});
</script>
<style lang="stylus" scoped>
@import '~const.styl'
.mk-note-detail
overflow hidden
margin 0 auto
padding 0
width 100%
text-align left
background #fff
border-radius 8px
box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)
> .fetching
padding 64px 0
> .more
display block
margin 0
padding 10px 0
width 100%
font-size 1em
text-align center
color #999
cursor pointer
background #fafafa
outline none
border none
border-bottom solid 1px #eef0f2
border-radius 6px 6px 0 0
box-shadow none
&:hover
background #f6f6f6
&:active
background #f0f0f0
&:disabled
color #ccc
> .context
> *
border-bottom 1px solid #eef0f2
> .renote
color #9dbb00
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
> p
margin 0
padding 16px 32px
.avatar-anchor
display inline-block
.avatar
vertical-align bottom
min-width 28px
min-height 28px
max-width 28px
max-height 28px
margin 0 8px 0 0
border-radius 6px
[data-fa]
margin-right 4px
.name
font-weight bold
& + article
padding-top 8px
> .reply-to
border-bottom 1px solid #eef0f2
> article
padding 14px 16px 9px 16px
@media (min-width 500px)
padding 28px 32px 18px 32px
&:after
content ""
display block
clear both
&:hover
> .main > footer > button
color #888
> header
display flex
line-height 1.1
> .avatar-anchor
display block
padding 0 .5em 0 0
> .avatar
display block
width 54px
height 54px
margin 0
border-radius 8px
vertical-align bottom
@media (min-width 500px)
width 60px
height 60px
> div
> .name
display inline-block
margin .4em 0
color #777
font-size 16px
font-weight bold
text-align left
text-decoration none
&:hover
text-decoration underline
> .username
display block
text-align left
margin 0
color #ccc
> .body
padding 8px 0
> .renote
margin 8px 0
> .mk-note-preview
padding 16px
border dashed 1px #c0dac6
border-radius 8px
> .location
margin 4px 0
font-size 12px
color #ccc
> .map
width 100%
height 200px
&:empty
display none
> .mk-url-preview
margin-top 8px
> .media
> img
display block
max-width 100%
> .tags
margin 4px 0 0 0
> *
display inline-block
margin 0 8px 0 0
padding 2px 8px 2px 16px
font-size 90%
color #8d969e
background #edf0f3
border-radius 4px
&:before
content ""
display block
position absolute
top 0
bottom 0
left 4px
width 8px
height 8px
margin auto 0
background #fff
border-radius 100%
> .time
font-size 16px
color #c0c0c0
> footer
font-size 1.2em
> button
margin 0
padding 8px
background transparent
border none
box-shadow none
font-size 1em
color #ddd
cursor pointer
&:not(:last-child)
margin-right 28px
&:hover
color #666
> .count
display inline
margin 0 0 0 8px
color #999
&.reacted
color $theme-color
> .replies
> *
border-top 1px solid #eef0f2
</style>
<style lang="stylus" module>
.text
display block
margin 0
padding 0
overflow-wrap break-word
font-size 16px
color #717171
@media (min-width 500px)
font-size 24px
</style>

View File

@@ -1,18 +1,18 @@
<template>
<div class="mk-post-preview">
<div class="mk-note-preview">
<router-link class="avatar-anchor" :to="`/@${acct}`">
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
</router-link>
<div class="main">
<header>
<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
<span class="username">@{{ acct }}</span>
<router-link class="time" :to="`/@${acct}/${post.id}`">
<mk-time :time="post.createdAt"/>
<router-link class="time" :to="`/@${acct}/${note.id}`">
<mk-time :time="note.createdAt"/>
</router-link>
</header>
<div class="body">
<mk-sub-post-content class="text" :post="post"/>
<mk-sub-note-content class="text" :note="note"/>
</div>
</div>
</div>
@@ -24,20 +24,20 @@ import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
export default Vue.extend({
props: ['post'],
props: ['note'],
computed: {
acct() {
return getAcct(this.post.user);
return getAcct(this.note.user);
},
name() {
return getUserName(this.post.user);
return getUserName(this.note.user);
}
}
});
</script>
<style lang="stylus" scoped>
.mk-post-preview
.mk-note-preview
margin 0
padding 0
font-size 0.9em

View File

@@ -1,18 +1,18 @@
<template>
<div class="sub">
<router-link class="avatar-anchor" :to="`/@${acct}`">
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
</router-link>
<div class="main">
<header>
<router-link class="name" :to="`/@${acct}`">{{ getUserName(post.user) }}</router-link>
<router-link class="name" :to="`/@${acct}`">{{ getUserName(note.user) }}</router-link>
<span class="username">@{{ acct }}</span>
<router-link class="created-at" :to="`/@${acct}/${post.id}`">
<mk-time :time="post.createdAt"/>
<router-link class="created-at" :to="`/@${acct}/${note.id}`">
<mk-time :time="note.createdAt"/>
</router-link>
</header>
<div class="body">
<mk-sub-post-content class="text" :post="post"/>
<mk-sub-note-content class="text" :note="note"/>
</div>
</div>
</div>
@@ -24,13 +24,13 @@ import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
export default Vue.extend({
props: ['post'],
props: ['note'],
computed: {
acct() {
return getAcct(this.post.user);
return getAcct(this.note.user);
},
name() {
return getUserName(this.post.user);
return getUserName(this.note.user);
}
}
});

View File

@@ -0,0 +1,540 @@
<template>
<div class="note" :class="{ renote: isRenote }">
<div class="reply-to" v-if="p.reply">
<x-sub :note="p.reply"/>
</div>
<div class="renote" v-if="isRenote">
<p>
<router-link class="avatar-anchor" :to="`/@${acct}`">
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
</router-link>
%fa:retweet%
<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
</p>
<mk-time :time="note.createdAt"/>
</div>
<article>
<router-link class="avatar-anchor" :to="`/@${pAcct}`">
<img class="avatar" :src="`${p.user.avatarUrl}?thumbnail&size=96`" alt="avatar"/>
</router-link>
<div class="main">
<header>
<router-link class="name" :to="`/@${pAcct}`">{{ pName }}</router-link>
<span class="is-bot" v-if="p.user.host === null && p.user.account.isBot">bot</span>
<span class="username">@{{ pAcct }}</span>
<div class="info">
<span class="mobile" v-if="p.viaMobile">%fa:mobile-alt%</span>
<router-link class="created-at" :to="url">
<mk-time :time="p.createdAt"/>
</router-link>
</div>
</header>
<div class="body">
<p class="channel" v-if="p.channel != null"><a target="_blank">{{ p.channel.title }}</a>:</p>
<div class="text">
<a class="reply" v-if="p.reply">
%fa:reply%
</a>
<mk-note-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
<a class="rp" v-if="p.renote != null">RP:</a>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
<div class="tags" v-if="p.tags && p.tags.length > 0">
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
</div>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
<div class="map" v-if="p.geo" ref="map"></div>
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote"/>
</div>
</div>
<footer>
<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
<button @click="reply">
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
</button>
<button @click="renote" title="Renote">
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
</button>
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
</button>
<button class="menu" @click="menu" ref="menuButton">
%fa:ellipsis-h%
</button>
</footer>
</div>
</article>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
import parse from '../../../../../text/parse';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
import XSub from './note.sub.vue';
export default Vue.extend({
components: {
XSub
},
props: ['note'],
data() {
return {
connection: null,
connectionId: null
};
},
computed: {
acct(): string {
return getAcct(this.note.user);
},
name(): string {
return getUserName(this.note.user);
},
pAcct(): string {
return getAcct(this.p.user);
},
pName(): string {
return getUserName(this.p.user);
},
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.mediaIds == null &&
this.note.poll == null);
},
p(): any {
return this.isRenote ? this.note.renote : this.note;
},
reactionsCount(): number {
return this.p.reactionCounts
? Object.keys(this.p.reactionCounts)
.map(key => this.p.reactionCounts[key])
.reduce((a, b) => a + b)
: 0;
},
url(): string {
return `/@${this.pAcct}/${this.p.id}`;
},
urls(): string[] {
if (this.p.text) {
const ast = parse(this.p.text);
return ast
.filter(t => (t.type == 'url' || t.type == 'link') && !t.silent)
.map(t => t.url);
} else {
return null;
}
}
},
created() {
if ((this as any).os.isSignedIn) {
this.connection = (this as any).os.stream.getConnection();
this.connectionId = (this as any).os.stream.use();
}
},
mounted() {
this.capture(true);
if ((this as any).os.isSignedIn) {
this.connection.on('_connected_', this.onStreamConnected);
}
// Draw map
if (this.p.geo) {
const shouldShowMap = (this as any).os.isSignedIn ? (this as any).os.i.account.clientSettings.showMaps : true;
if (shouldShowMap) {
(this as any).os.getGoogleMaps().then(maps => {
const uluru = new maps.LatLng(this.p.geo.coordinates[1], this.p.geo.coordinates[0]);
const map = new maps.Map(this.$refs.map, {
center: uluru,
zoom: 15
});
new maps.Marker({
position: uluru,
map: map
});
});
}
}
},
beforeDestroy() {
this.decapture(true);
if ((this as any).os.isSignedIn) {
this.connection.off('_connected_', this.onStreamConnected);
(this as any).os.stream.dispose(this.connectionId);
}
},
methods: {
capture(withHandler = false) {
if ((this as any).os.isSignedIn) {
this.connection.send({
type: 'capture',
id: this.p.id
});
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
}
},
decapture(withHandler = false) {
if ((this as any).os.isSignedIn) {
this.connection.send({
type: 'decapture',
id: this.p.id
});
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
}
},
onStreamConnected() {
this.capture();
},
onStreamNoteUpdated(data) {
const note = data.note;
if (note.id == this.note.id) {
this.$emit('update:note', note);
} else if (note.id == this.note.renoteId) {
this.note.renote = note;
}
},
reply() {
(this as any).apis.post({
reply: this.p
});
},
renote() {
(this as any).apis.post({
renote: this.p
});
},
react() {
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
note: this.p,
compact: true
});
},
menu() {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
note: this.p,
compact: true
});
}
}
});
</script>
<style lang="stylus" scoped>
@import '~const.styl'
.note
font-size 12px
border-bottom solid 1px #eaeaea
&:first-child
border-radius 8px 8px 0 0
> .renote
border-radius 8px 8px 0 0
&:last-of-type
border-bottom none
@media (min-width 350px)
font-size 14px
@media (min-width 500px)
font-size 16px
> .renote
color #9dbb00
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
> p
margin 0
padding 8px 16px
line-height 28px
@media (min-width 500px)
padding 16px
.avatar-anchor
display inline-block
.avatar
vertical-align bottom
width 28px
height 28px
margin 0 8px 0 0
border-radius 6px
[data-fa]
margin-right 4px
.name
font-weight bold
> .mk-time
position absolute
top 8px
right 16px
font-size 0.9em
line-height 28px
@media (min-width 500px)
top 16px
& + article
padding-top 8px
> .reply-to
background rgba(0, 0, 0, 0.0125)
> .mk-note-preview
background transparent
> article
padding 14px 16px 9px 16px
&:after
content ""
display block
clear both
> .avatar-anchor
display block
float left
margin 0 10px 8px 0
position -webkit-sticky
position sticky
top 62px
@media (min-width 500px)
margin-right 16px
> .avatar
display block
width 48px
height 48px
margin 0
border-radius 6px
vertical-align bottom
@media (min-width 500px)
width 58px
height 58px
border-radius 8px
> .main
float left
width calc(100% - 58px)
@media (min-width 500px)
width calc(100% - 74px)
> header
display flex
align-items center
white-space nowrap
@media (min-width 500px)
margin-bottom 2px
> .name
display block
margin 0 0.5em 0 0
padding 0
overflow hidden
color #627079
font-size 1em
font-weight bold
text-decoration none
text-overflow ellipsis
&:hover
text-decoration underline
> .is-bot
margin 0 0.5em 0 0
padding 1px 6px
font-size 12px
color #aaa
border solid 1px #ddd
border-radius 3px
> .username
margin 0 0.5em 0 0
color #ccc
> .info
margin-left auto
font-size 0.9em
> .mobile
margin-right 6px
color #c0c0c0
> .created-at
color #c0c0c0
> .body
> .text
display block
margin 0
padding 0
overflow-wrap break-word
font-size 1.1em
color #717171
>>> .quote
margin 8px
padding 6px 12px
color #aaa
border-left solid 3px #eee
> .reply
margin-right 8px
color #717171
> .rp
margin-left 4px
font-style oblique
color #a0bf46
[data-is-me]:after
content "you"
padding 0 4px
margin-left 4px
font-size 80%
color $theme-color-foreground
background $theme-color
border-radius 4px
.mk-url-preview
margin-top 8px
> .channel
margin 0
> .tags
margin 4px 0 0 0
> *
display inline-block
margin 0 8px 0 0
padding 2px 8px 2px 16px
font-size 90%
color #8d969e
background #edf0f3
border-radius 4px
&:before
content ""
display block
position absolute
top 0
bottom 0
left 4px
width 8px
height 8px
margin auto 0
background #fff
border-radius 100%
> .media
> img
display block
max-width 100%
> .location
margin 4px 0
font-size 12px
color #ccc
> .map
width 100%
height 200px
&:empty
display none
> .app
font-size 12px
color #ccc
> .mk-poll
font-size 80%
> .renote
margin 8px 0
> .mk-note-preview
padding 16px
border dashed 1px #c0dac6
border-radius 8px
> footer
> button
margin 0
padding 8px
background transparent
border none
box-shadow none
font-size 1em
color #ddd
cursor pointer
&:not(:last-child)
margin-right 28px
&:hover
color #666
> .count
display inline
margin 0 0 0 8px
color #999
&.reacted
color $theme-color
&.menu
@media (max-width 350px)
display none
</style>
<style lang="stylus" module>
.text
code
padding 4px 8px
margin 0 0.5em
font-size 80%
color #525252
background #f8f8f8
border-radius 2px
pre > code
padding 16px
margin 0
</style>

View File

@@ -1,12 +1,12 @@
<template>
<div class="mk-posts">
<div class="mk-notes">
<slot name="head"></slot>
<slot></slot>
<template v-for="(post, i) in _posts">
<mk-post :post="post" :key="post.id" @update:post="onPostUpdated(i, $event)"/>
<p class="date" v-if="i != posts.length - 1 && post._date != _posts[i + 1]._date">
<span>%fa:angle-up%{{ post._datetext }}</span>
<span>%fa:angle-down%{{ _posts[i + 1]._datetext }}</span>
<template v-for="(note, i) in _notes">
<mk-note :note="note" :key="note.id" @update:note="onNoteUpdated(i, $event)"/>
<p class="date" v-if="i != notes.length - 1 && note._date != _notes[i + 1]._date">
<span>%fa:angle-up%{{ note._datetext }}</span>
<span>%fa:angle-down%{{ _notes[i + 1]._datetext }}</span>
</p>
</template>
<footer>
@@ -20,25 +20,25 @@ import Vue from 'vue';
export default Vue.extend({
props: {
posts: {
notes: {
type: Array,
default: () => []
}
},
computed: {
_posts(): any[] {
return (this.posts as any).map(post => {
const date = new Date(post.createdAt).getDate();
const month = new Date(post.createdAt).getMonth() + 1;
post._date = date;
post._datetext = `${month}${date}`;
return post;
_notes(): any[] {
return (this.notes as any).map(note => {
const date = new Date(note.createdAt).getDate();
const month = new Date(note.createdAt).getMonth() + 1;
note._date = date;
note._datetext = `${month}${date}`;
return note;
});
}
},
methods: {
onPostUpdated(i, post) {
Vue.set((this as any).posts, i, post);
onNoteUpdated(i, note) {
Vue.set((this as any).notes, i, note);
}
}
});
@@ -47,7 +47,7 @@ export default Vue.extend({
<style lang="stylus" scoped>
@import '~const.styl'
.mk-posts
.mk-notes
background #fff
border-radius 8px
box-shadow 0 0 0 1px rgba(0, 0, 0, 0.2)

View File

@@ -4,23 +4,23 @@
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<div class="text">
<p><mk-reaction-icon :reaction="notification.reaction"/>{{ name }}</p>
<p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%</p>
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%</p>
</div>
</template>
<template v-if="notification.type == 'repost'">
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<template v-if="notification.type == 'renote'">
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<div class="text">
<p>%fa:retweet%{{ posterName }}</p>
<p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post.repost) }}%fa:quote-right%</p>
<p>%fa:retweet%{{ noteerName }}</p>
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note.renote) }}%fa:quote-right%</p>
</div>
</template>
<template v-if="notification.type == 'quote'">
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<div class="text">
<p>%fa:quote-left%{{ posterName }}</p>
<p class="post-preview">{{ getPostSummary(notification.post) }}</p>
<p>%fa:quote-left%{{ noteerName }}</p>
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
</div>
</template>
@@ -32,18 +32,18 @@
</template>
<template v-if="notification.type == 'reply'">
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<div class="text">
<p>%fa:reply%{{ posterName }}</p>
<p class="post-preview">{{ getPostSummary(notification.post) }}</p>
<p>%fa:reply%{{ noteerName }}</p>
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
</div>
</template>
<template v-if="notification.type == 'mention'">
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<div class="text">
<p>%fa:at%{{ posterName }}</p>
<p class="post-preview">{{ getPostSummary(notification.post) }}</p>
<p>%fa:at%{{ noteerName }}</p>
<p class="note-preview">{{ getNoteSummary(notification.note) }}</p>
</div>
</template>
@@ -51,7 +51,7 @@
<img class="avatar" :src="`${notification.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<div class="text">
<p>%fa:chart-pie%{{ name }}</p>
<p class="post-ref">%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%</p>
<p class="note-ref">%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%</p>
</div>
</template>
</div>
@@ -59,7 +59,7 @@
<script lang="ts">
import Vue from 'vue';
import getPostSummary from '../../../../../renderers/get-post-summary';
import getNoteSummary from '../../../../../renderers/get-note-summary';
import getUserName from '../../../../../renderers/get-user-name';
export default Vue.extend({
@@ -68,13 +68,13 @@ export default Vue.extend({
name() {
return getUserName(this.notification.user);
},
posterName() {
return getUserName(this.notification.post.user);
noteerName() {
return getUserName(this.notification.note.user);
}
},
data() {
return {
getPostSummary
getNoteSummary
};
}
});
@@ -112,7 +112,7 @@ export default Vue.extend({
i, mk-reaction-icon
margin-right 4px
.post-ref
.note-ref
[data-fa]
font-size 1em
@@ -121,7 +121,7 @@ export default Vue.extend({
display inline-block
margin-right 3px
&.repost, &.quote
&.renote, &.quote
.text p i
color #77B255

View File

@@ -10,31 +10,31 @@
<mk-reaction-icon :reaction="notification.reaction"/>
<router-link :to="`/@${acct}`">{{ getUserName(notification.user) }}</router-link>
</p>
<router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
%fa:quote-left%{{ getPostSummary(notification.post) }}
<router-link class="note-ref" :to="`/@${acct}/${notification.note.id}`">
%fa:quote-left%{{ getNoteSummary(notification.note) }}
%fa:quote-right%
</router-link>
</div>
</div>
<div class="notification repost" v-if="notification.type == 'repost'">
<div class="notification renote" v-if="notification.type == 'renote'">
<mk-time :time="notification.createdAt"/>
<router-link class="avatar-anchor" :to="`/@${acct}`">
<img class="avatar" :src="`${notification.post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<img class="avatar" :src="`${notification.note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
</router-link>
<div class="text">
<p>
%fa:retweet%
<router-link :to="`/@${acct}`">{{ getUserName(notification.post.user) }}</router-link>
<router-link :to="`/@${acct}`">{{ getUserName(notification.note.user) }}</router-link>
</p>
<router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
%fa:quote-left%{{ getPostSummary(notification.post.repost) }}%fa:quote-right%
<router-link class="note-ref" :to="`/@${acct}/${notification.note.id}`">
%fa:quote-left%{{ getNoteSummary(notification.note.renote) }}%fa:quote-right%
</router-link>
</div>
</div>
<template v-if="notification.type == 'quote'">
<mk-post :post="notification.post"/>
<mk-note :note="notification.note"/>
</template>
<div class="notification follow" v-if="notification.type == 'follow'">
@@ -51,11 +51,11 @@
</div>
<template v-if="notification.type == 'reply'">
<mk-post :post="notification.post"/>
<mk-note :note="notification.note"/>
</template>
<template v-if="notification.type == 'mention'">
<mk-post :post="notification.post"/>
<mk-note :note="notification.note"/>
</template>
<div class="notification poll_vote" v-if="notification.type == 'poll_vote'">
@@ -68,8 +68,8 @@
%fa:chart-pie%
<router-link :to="`/@${acct}`">{{ getUserName(notification.user) }}</router-link>
</p>
<router-link class="post-ref" :to="`/@${acct}/${notification.post.id}`">
%fa:quote-left%{{ getPostSummary(notification.post) }}%fa:quote-right%
<router-link class="note-ref" :to="`/@${acct}/${notification.note.id}`">
%fa:quote-left%{{ getNoteSummary(notification.note) }}%fa:quote-right%
</router-link>
</div>
</div>
@@ -78,7 +78,7 @@
<script lang="ts">
import Vue from 'vue';
import getPostSummary from '../../../../../renderers/get-post-summary';
import getNoteSummary from '../../../../../renderers/get-note-summary';
import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
@@ -91,13 +91,13 @@ export default Vue.extend({
name() {
return getUserName(this.notification.user);
},
posterName() {
return getUserName(this.notification.post.user);
noteerName() {
return getUserName(this.notification.note.user);
}
},
data() {
return {
getPostSummary
getNoteSummary
};
}
});
@@ -146,10 +146,10 @@ export default Vue.extend({
i, .mk-reaction-icon
margin-right 4px
> .post-preview
> .note-preview
color rgba(0, 0, 0, 0.7)
> .post-ref
> .note-ref
color rgba(0, 0, 0, 0.7)
[data-fa]
@@ -159,7 +159,7 @@ export default Vue.extend({
display inline-block
margin-right 3px
&.repost
&.renote
.text p i
color #77B255

View File

@@ -1,5 +1,5 @@
<template>
<div class="mk-post-detail">
<div class="mk-note-detail">
<button
class="more"
v-if="p.reply && p.reply.replyId && context == null"
@@ -10,21 +10,21 @@
<template v-if="contextFetching">%fa:spinner .pulse%</template>
</button>
<div class="context">
<x-sub v-for="post in context" :key="post.id" :post="post"/>
<x-sub v-for="note in context" :key="note.id" :note="note"/>
</div>
<div class="reply-to" v-if="p.reply">
<x-sub :post="p.reply"/>
<x-sub :note="p.reply"/>
</div>
<div class="repost" v-if="isRepost">
<div class="renote" v-if="isRenote">
<p>
<router-link class="avatar-anchor" :to="`/@${acct}`">
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=32`" alt="avatar"/>
</router-link>
%fa:retweet%
<router-link class="name" :to="`/@${acct}`">
{{ name }}
</router-link>
がRepost
がRenote
</p>
</div>
<article>
@@ -38,33 +38,33 @@
</div>
</header>
<div class="body">
<mk-post-html v-if="p.text" :ast="p.text" :i="os.i" :class="$style.text"/>
<mk-note-html v-if="p.text" :ast="p.text" :i="os.i" :class="$style.text"/>
<div class="tags" v-if="p.tags && p.tags.length > 0">
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :post="p"/>
<mk-poll v-if="p.poll" :note="p"/>
<mk-url-preview v-for="url in urls" :url="url" :key="url"/>
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
<div class="map" v-if="p.geo" ref="map"></div>
<div class="repost" v-if="p.repost">
<mk-post-preview :post="p.repost"/>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote"/>
</div>
</div>
<router-link class="time" :to="`/@${pAcct}/${p.id}`">
<mk-time :time="p.createdAt" mode="detail"/>
</router-link>
<footer>
<mk-reactions-viewer :post="p"/>
<button @click="reply" title="%i18n:mobile.tags.mk-post-detail.reply%">
<mk-reactions-viewer :note="p"/>
<button @click="reply" title="%i18n:mobile.tags.mk-note-detail.reply%">
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
</button>
<button @click="repost" title="Repost">
%fa:retweet%<p class="count" v-if="p.repostCount > 0">{{ p.repostCount }}</p>
<button @click="renote" title="Renote">
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
</button>
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-post-detail.reaction%">
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton" title="%i18n:mobile.tags.mk-note-detail.reaction%">
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
</button>
<button @click="menu" ref="menuButton">
@@ -73,7 +73,7 @@
</footer>
</article>
<div class="replies" v-if="!compact">
<x-sub v-for="post in replies" :key="post.id" :post="post"/>
<x-sub v-for="note in replies" :key="note.id" :note="note"/>
</div>
</div>
</template>
@@ -84,9 +84,9 @@ import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
import parse from '../../../../../text/parse';
import MkPostMenu from '../../../common/views/components/post-menu.vue';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
import XSub from './post-detail.sub.vue';
import XSub from './note-detail.sub.vue';
export default Vue.extend({
components: {
@@ -94,7 +94,7 @@ export default Vue.extend({
},
props: {
post: {
note: {
type: Object,
required: true
},
@@ -113,10 +113,10 @@ export default Vue.extend({
computed: {
acct(): string {
return getAcct(this.post.user);
return getAcct(this.note.user);
},
name(): string {
return getUserName(this.post.user);
return getUserName(this.note.user);
},
pAcct(): string {
return getAcct(this.p.user);
@@ -124,14 +124,14 @@ export default Vue.extend({
pName(): string {
return getUserName(this.p.user);
},
isRepost(): boolean {
return (this.post.repost &&
this.post.text == null &&
this.post.mediaIds == null &&
this.post.poll == null);
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.mediaIds == null &&
this.note.poll == null);
},
p(): any {
return this.isRepost ? this.post.repost : this.post;
return this.isRenote ? this.note.renote : this.note;
},
reactionsCount(): number {
return this.p.reactionCounts
@@ -155,8 +155,8 @@ export default Vue.extend({
mounted() {
// Get replies
if (!this.compact) {
(this as any).api('posts/replies', {
postId: this.p.id,
(this as any).api('notes/replies', {
noteId: this.p.id,
limit: 8
}).then(replies => {
this.replies = replies;
@@ -187,8 +187,8 @@ export default Vue.extend({
this.contextFetching = true;
// Fetch context
(this as any).api('posts/context', {
postId: this.p.replyId
(this as any).api('notes/context', {
noteId: this.p.replyId
}).then(context => {
this.contextFetching = false;
this.context = context.reverse();
@@ -199,22 +199,22 @@ export default Vue.extend({
reply: this.p
});
},
repost() {
renote() {
(this as any).apis.post({
repost: this.p
renote: this.p
});
},
react() {
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
post: this.p,
note: this.p,
compact: true
});
},
menu() {
(this as any).os.new(MkPostMenu, {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
post: this.p,
note: this.p,
compact: true
});
}
@@ -225,7 +225,7 @@ export default Vue.extend({
<style lang="stylus" scoped>
@import '~const.styl'
.mk-post-detail
.mk-note-detail
overflow hidden
margin 0 auto
padding 0
@@ -267,7 +267,7 @@ export default Vue.extend({
> *
border-bottom 1px solid #eef0f2
> .repost
> .renote
color #9dbb00
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
@@ -357,10 +357,10 @@ export default Vue.extend({
> .body
padding 8px 0
> .repost
> .renote
margin 8px 0
> .mk-post-preview
> .mk-note-preview
padding 16px
border dashed 1px #c0dac6
border-radius 8px

View File

@@ -9,8 +9,8 @@
</div>
</header>
<div class="form">
<mk-post-preview v-if="reply" :post="reply"/>
<textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:mobile.tags.mk-post-form.reply-placeholder%' : '%i18n:mobile.tags.mk-post-form.post-placeholder%'"></textarea>
<mk-note-preview v-if="reply" :note="reply"/>
<textarea v-model="text" ref="text" :disabled="posting" :placeholder="reply ? '%i18n:mobile.tags.mk-post-form.reply-placeholder%' : '%i18n:mobile.tags.mk-post-form.note-placeholder%'"></textarea>
<div class="attaches" v-show="files.length != 0">
<x-draggable class="files" :list="files" :options="{ animation: 150 }">
<div class="file" v-for="file in files" :key="file.id">
@@ -112,7 +112,7 @@ export default Vue.extend({
post() {
this.posting = true;
const viaMobile = (this as any).os.i.account.clientSettings.disableViaMobile !== true;
(this as any).api('posts/create', {
(this as any).api('notes/create', {
text: this.text == '' ? undefined : this.text,
mediaIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
replyId: this.reply ? this.reply.id : undefined,
@@ -127,7 +127,7 @@ export default Vue.extend({
} : null,
viaMobile: viaMobile
}).then(data => {
this.$emit('post');
this.$emit('note');
this.$destroy();
}).catch(err => {
this.posting = false;
@@ -200,7 +200,7 @@ export default Vue.extend({
max-width 500px
margin 0 auto
> .mk-post-preview
> .mk-note-preview
padding 16px
> .attaches

View File

@@ -1,19 +1,19 @@
<template>
<div class="post" :class="{ repost: isRepost }">
<div class="note" :class="{ renote: isRenote }">
<div class="reply-to" v-if="p.reply">
<x-sub :post="p.reply"/>
<x-sub :note="p.reply"/>
</div>
<div class="repost" v-if="isRepost">
<div class="renote" v-if="isRenote">
<p>
<router-link class="avatar-anchor" :to="`/@${acct}`">
<img class="avatar" :src="`${post.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
<img class="avatar" :src="`${note.user.avatarUrl}?thumbnail&size=64`" alt="avatar"/>
</router-link>
%fa:retweet%
<span>{{ '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.indexOf('{')) }}</span>
<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr(0, '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('{')) }}</span>
<router-link class="name" :to="`/@${acct}`">{{ name }}</router-link>
<span>{{ '%i18n:mobile.tags.mk-timeline-post.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-post.reposted-by%'.indexOf('}') + 1) }}</span>
<span>{{ '%i18n:mobile.tags.mk-timeline-note.reposted-by%'.substr('%i18n:mobile.tags.mk-timeline-note.reposted-by%'.indexOf('}') + 1) }}</span>
</p>
<mk-time :time="post.createdAt"/>
<mk-time :time="note.createdAt"/>
</div>
<article>
<router-link class="avatar-anchor" :to="`/@${pAcct}`">
@@ -37,13 +37,13 @@
<a class="reply" v-if="p.reply">
%fa:reply%
</a>
<mk-post-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
<a class="rp" v-if="p.repost != null">RP:</a>
<mk-note-html v-if="p.text" :text="p.text" :i="os.i" :class="$style.text"/>
<a class="rp" v-if="p.renote != null">RP:</a>
</div>
<div class="media" v-if="p.media.length > 0">
<mk-media-list :media-list="p.media"/>
</div>
<mk-poll v-if="p.poll" :post="p" ref="pollViewer"/>
<mk-poll v-if="p.poll" :note="p" ref="pollViewer"/>
<div class="tags" v-if="p.tags && p.tags.length > 0">
<router-link v-for="tag in p.tags" :key="tag" :to="`/search?q=#${tag}`">{{ tag }}</router-link>
</div>
@@ -51,17 +51,17 @@
<a class="location" v-if="p.geo" :href="`http://maps.google.com/maps?q=${p.geo.coordinates[1]},${p.geo.coordinates[0]}`" target="_blank">%fa:map-marker-alt% 位置情報</a>
<div class="map" v-if="p.geo" ref="map"></div>
<span class="app" v-if="p.app">via <b>{{ p.app.name }}</b></span>
<div class="repost" v-if="p.repost">
<mk-post-preview :post="p.repost"/>
<div class="renote" v-if="p.renote">
<mk-note-preview :note="p.renote"/>
</div>
</div>
<footer>
<mk-reactions-viewer :post="p" ref="reactionsViewer"/>
<mk-reactions-viewer :note="p" ref="reactionsViewer"/>
<button @click="reply">
%fa:reply%<p class="count" v-if="p.repliesCount > 0">{{ p.repliesCount }}</p>
</button>
<button @click="repost" title="Repost">
%fa:retweet%<p class="count" v-if="p.repostCount > 0">{{ p.repostCount }}</p>
<button @click="renote" title="Renote">
%fa:retweet%<p class="count" v-if="p.renoteCount > 0">{{ p.renoteCount }}</p>
</button>
<button :class="{ reacted: p.myReaction != null }" @click="react" ref="reactButton">
%fa:plus%<p class="count" v-if="p.reactions_count > 0">{{ p.reactions_count }}</p>
@@ -81,16 +81,16 @@ import getAcct from '../../../../../acct/render';
import getUserName from '../../../../../renderers/get-user-name';
import parse from '../../../../../text/parse';
import MkPostMenu from '../../../common/views/components/post-menu.vue';
import MkNoteMenu from '../../../common/views/components/note-menu.vue';
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
import XSub from './post.sub.vue';
import XSub from './note.sub.vue';
export default Vue.extend({
components: {
XSub
},
props: ['post'],
props: ['note'],
data() {
return {
@@ -101,10 +101,10 @@ export default Vue.extend({
computed: {
acct(): string {
return getAcct(this.post.user);
return getAcct(this.note.user);
},
name(): string {
return getUserName(this.post.user);
return getUserName(this.note.user);
},
pAcct(): string {
return getAcct(this.p.user);
@@ -112,14 +112,14 @@ export default Vue.extend({
pName(): string {
return getUserName(this.p.user);
},
isRepost(): boolean {
return (this.post.repost &&
this.post.text == null &&
this.post.mediaIds == null &&
this.post.poll == null);
isRenote(): boolean {
return (this.note.renote &&
this.note.text == null &&
this.note.mediaIds == null &&
this.note.poll == null);
},
p(): any {
return this.isRepost ? this.post.repost : this.post;
return this.isRenote ? this.note.renote : this.note;
},
reactionsCount(): number {
return this.p.reactionCounts
@@ -192,7 +192,7 @@ export default Vue.extend({
type: 'capture',
id: this.p.id
});
if (withHandler) this.connection.on('post-updated', this.onStreamPostUpdated);
if (withHandler) this.connection.on('note-updated', this.onStreamNoteUpdated);
}
},
decapture(withHandler = false) {
@@ -201,18 +201,18 @@ export default Vue.extend({
type: 'decapture',
id: this.p.id
});
if (withHandler) this.connection.off('post-updated', this.onStreamPostUpdated);
if (withHandler) this.connection.off('note-updated', this.onStreamNoteUpdated);
}
},
onStreamConnected() {
this.capture();
},
onStreamPostUpdated(data) {
const post = data.post;
if (post.id == this.post.id) {
this.$emit('update:post', post);
} else if (post.id == this.post.repostId) {
this.post.repost = post;
onStreamNoteUpdated(data) {
const note = data.note;
if (note.id == this.note.id) {
this.$emit('update:note', note);
} else if (note.id == this.note.renoteId) {
this.note.renote = note;
}
},
reply() {
@@ -220,22 +220,22 @@ export default Vue.extend({
reply: this.p
});
},
repost() {
renote() {
(this as any).apis.post({
repost: this.p
renote: this.p
});
},
react() {
(this as any).os.new(MkReactionPicker, {
source: this.$refs.reactButton,
post: this.p,
note: this.p,
compact: true
});
},
menu() {
(this as any).os.new(MkPostMenu, {
(this as any).os.new(MkNoteMenu, {
source: this.$refs.menuButton,
post: this.p,
note: this.p,
compact: true
});
}
@@ -246,14 +246,14 @@ export default Vue.extend({
<style lang="stylus" scoped>
@import '~const.styl'
.post
.note
font-size 12px
border-bottom solid 1px #eaeaea
&:first-child
border-radius 8px 8px 0 0
> .repost
> .renote
border-radius 8px 8px 0 0
&:last-of-type
@@ -265,7 +265,7 @@ export default Vue.extend({
@media (min-width 500px)
font-size 16px
> .repost
> .renote
color #9dbb00
background linear-gradient(to bottom, #edfde2 0%, #fff 100%)
@@ -309,7 +309,7 @@ export default Vue.extend({
> .reply-to
background rgba(0, 0, 0, 0.0125)
> .mk-post-preview
> .mk-note-preview
background transparent
> article
@@ -485,10 +485,10 @@ export default Vue.extend({
> .mk-poll
font-size 80%
> .repost
> .renote
margin 8px 0
> .mk-post-preview
> .mk-note-preview
padding 16px
border dashed 1px #c0dac6
border-radius 8px

View File

@@ -0,0 +1,43 @@
<template>
<div class="mk-sub-note-content">
<div class="body">
<a class="reply" v-if="note.replyId">%fa:reply%</a>
<mk-note-html v-if="note.text" :text="note.text" :i="os.i"/>
<a class="rp" v-if="note.renoteId">RP: ...</a>
</div>
<details v-if="note.media.length > 0">
<summary>({{ note.media.length }}個のメディア)</summary>
<mk-media-list :media-list="note.media"/>
</details>
<details v-if="note.poll">
<summary>%i18n:mobile.tags.mk-sub-note-content.poll%</summary>
<mk-poll :note="note"/>
</details>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: ['note']
});
</script>
<style lang="stylus" scoped>
.mk-sub-note-content
overflow-wrap break-word
> .body
> .reply
margin-right 6px
color #717171
> .rp
margin-left 4px
font-style oblique
color #a0bf46
mk-poll
font-size 80%
</style>

View File

@@ -1,43 +0,0 @@
<template>
<div class="mk-sub-post-content">
<div class="body">
<a class="reply" v-if="post.replyId">%fa:reply%</a>
<mk-post-html v-if="post.text" :text="post.text" :i="os.i"/>
<a class="rp" v-if="post.repostId">RP: ...</a>
</div>
<details v-if="post.media.length > 0">
<summary>({{ post.media.length }}個のメディア)</summary>
<mk-media-list :media-list="post.media"/>
</details>
<details v-if="post.poll">
<summary>%i18n:mobile.tags.mk-sub-post-content.poll%</summary>
<mk-poll :post="post"/>
</details>
</div>
</template>
<script lang="ts">
import Vue from 'vue';
export default Vue.extend({
props: ['post']
});
</script>
<style lang="stylus" scoped>
.mk-sub-post-content
overflow-wrap break-word
> .body
> .reply
margin-right 6px
color #717171
> .rp
margin-left 4px
font-style oblique
color #a0bf46
mk-poll
font-size 80%
</style>

View File

@@ -1,11 +1,11 @@
<template>
<div class="mk-timeline">
<mk-friends-maker v-if="alone"/>
<mk-posts :posts="posts">
<mk-notes :notes="notes">
<div class="init" v-if="fetching">
%fa:spinner .pulse%%i18n:common.loading%
</div>
<div class="empty" v-if="!fetching && posts.length == 0">
<div class="empty" v-if="!fetching && notes.length == 0">
%fa:R comments%
%i18n:mobile.tags.mk-home-timeline.empty-timeline%
</div>
@@ -13,7 +13,7 @@
<span v-if="!moreFetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
<span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
</button>
</mk-posts>
</mk-notes>
</div>
</template>
@@ -33,7 +33,7 @@ export default Vue.extend({
return {
fetching: true,
moreFetching: false,
posts: [],
notes: [],
existMore: false,
connection: null,
connectionId: null
@@ -48,14 +48,14 @@ export default Vue.extend({
this.connection = (this as any).os.stream.getConnection();
this.connectionId = (this as any).os.stream.use();
this.connection.on('post', this.onPost);
this.connection.on('note', this.onNote);
this.connection.on('follow', this.onChangeFollowing);
this.connection.on('unfollow', this.onChangeFollowing);
this.fetch();
},
beforeDestroy() {
this.connection.off('post', this.onPost);
this.connection.off('note', this.onNote);
this.connection.off('follow', this.onChangeFollowing);
this.connection.off('unfollow', this.onChangeFollowing);
(this as any).os.stream.dispose(this.connectionId);
@@ -63,15 +63,15 @@ export default Vue.extend({
methods: {
fetch(cb?) {
this.fetching = true;
(this as any).api('posts/timeline', {
(this as any).api('notes/timeline', {
limit: limit + 1,
untilDate: this.date ? (this.date as any).getTime() : undefined
}).then(posts => {
if (posts.length == limit + 1) {
posts.pop();
}).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
this.existMore = true;
}
this.posts = posts;
this.notes = notes;
this.fetching = false;
this.$emit('loaded');
if (cb) cb();
@@ -79,22 +79,22 @@ export default Vue.extend({
},
more() {
this.moreFetching = true;
(this as any).api('posts/timeline', {
(this as any).api('notes/timeline', {
limit: limit + 1,
untilId: this.posts[this.posts.length - 1].id
}).then(posts => {
if (posts.length == limit + 1) {
posts.pop();
untilId: this.notes[this.notes.length - 1].id
}).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
this.existMore = true;
} else {
this.existMore = false;
}
this.posts = this.posts.concat(posts);
this.notes = this.notes.concat(notes);
this.moreFetching = false;
});
},
onPost(post) {
this.posts.unshift(post);
onNote(note) {
this.notes.unshift(note);
},
onChangeFollowing() {
this.fetch();

View File

@@ -1,18 +1,18 @@
<template>
<div class="mk-user-timeline">
<mk-posts :posts="posts">
<mk-notes :notes="notes">
<div class="init" v-if="fetching">
%fa:spinner .pulse%%i18n:common.loading%
</div>
<div class="empty" v-if="!fetching && posts.length == 0">
<div class="empty" v-if="!fetching && notes.length == 0">
%fa:R comments%
{{ withMedia ? '%i18n:mobile.tags.mk-user-timeline.no-posts-with-media%' : '%i18n:mobile.tags.mk-user-timeline.no-posts%' }}
{{ withMedia ? '%i18n:mobile.tags.mk-user-timeline.no-notes-with-media%' : '%i18n:mobile.tags.mk-user-timeline.no-notes%' }}
</div>
<button v-if="!fetching && existMore" @click="more" :disabled="moreFetching" slot="tail">
<span v-if="!moreFetching">%i18n:mobile.tags.mk-user-timeline.load-more%</span>
<span v-if="moreFetching">%i18n:common.loading%<mk-ellipsis/></span>
</button>
</mk-posts>
</mk-notes>
</div>
</template>
@@ -26,22 +26,22 @@ export default Vue.extend({
data() {
return {
fetching: true,
posts: [],
notes: [],
existMore: false,
moreFetching: false
};
},
mounted() {
(this as any).api('users/posts', {
(this as any).api('users/notes', {
userId: this.user.id,
withMedia: this.withMedia,
limit: limit + 1
}).then(posts => {
if (posts.length == limit + 1) {
posts.pop();
}).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
this.existMore = true;
}
this.posts = posts;
this.notes = notes;
this.fetching = false;
this.$emit('loaded');
});
@@ -49,19 +49,19 @@ export default Vue.extend({
methods: {
more() {
this.moreFetching = true;
(this as any).api('users/posts', {
(this as any).api('users/notes', {
userId: this.user.id,
withMedia: this.withMedia,
limit: limit + 1,
untilId: this.posts[this.posts.length - 1].id
}).then(posts => {
if (posts.length == limit + 1) {
posts.pop();
untilId: this.notes[this.notes.length - 1].id
}).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
this.existMore = true;
} else {
this.existMore = false;
}
this.posts = this.posts.concat(posts);
this.notes = this.notes.concat(notes);
this.moreFetching = false;
});
}

View File

@@ -64,7 +64,7 @@ import Vue from 'vue';
import * as XDraggable from 'vuedraggable';
import * as uuid from 'uuid';
import Progress from '../../../common/scripts/loading';
import getPostSummary from '../../../../../renderers/get-post-summary';
import getNoteSummary from '../../../../../renderers/get-note-summary';
export default Vue.extend({
components: {
@@ -124,14 +124,14 @@ export default Vue.extend({
this.connection = (this as any).os.stream.getConnection();
this.connectionId = (this as any).os.stream.use();
this.connection.on('post', this.onStreamPost);
this.connection.on('note', this.onStreamNote);
this.connection.on('mobile_home_updated', this.onHomeUpdated);
document.addEventListener('visibilitychange', this.onVisibilitychange, false);
Progress.start();
},
beforeDestroy() {
this.connection.off('post', this.onStreamPost);
this.connection.off('note', this.onStreamNote);
this.connection.off('mobile_home_updated', this.onHomeUpdated);
(this as any).os.stream.dispose(this.connectionId);
document.removeEventListener('visibilitychange', this.onVisibilitychange);
@@ -143,10 +143,10 @@ export default Vue.extend({
onLoaded() {
Progress.done();
},
onStreamPost(post) {
if (document.hidden && post.userId !== (this as any).os.i.id) {
onStreamNote(note) {
if (document.hidden && note.userId !== (this as any).os.i.id) {
this.unreadCount++;
document.title = `(${this.unreadCount}) ${getPostSummary(post)}`;
document.title = `(${this.unreadCount}) ${getNoteSummary(note)}`;
}
},
onVisibilitychange() {

View File

@@ -1,12 +1,12 @@
<template>
<mk-ui>
<span slot="header">%fa:R sticky-note%%i18n:mobile.tags.mk-post-page.title%</span>
<span slot="header">%fa:R sticky-note%%i18n:mobile.tags.mk-note-page.title%</span>
<main v-if="!fetching">
<a v-if="post.next" :href="post.next">%fa:angle-up%%i18n:mobile.tags.mk-post-page.next%</a>
<a v-if="note.next" :href="note.next">%fa:angle-up%%i18n:mobile.tags.mk-note-page.next%</a>
<div>
<mk-post-detail :post="post"/>
<mk-note-detail :note="note"/>
</div>
<a v-if="post.prev" :href="post.prev">%fa:angle-down%%i18n:mobile.tags.mk-post-page.prev%</a>
<a v-if="note.prev" :href="note.prev">%fa:angle-down%%i18n:mobile.tags.mk-note-page.prev%</a>
</main>
</mk-ui>
</template>
@@ -19,7 +19,7 @@ export default Vue.extend({
data() {
return {
fetching: true,
post: null
note: null
};
},
watch: {
@@ -37,10 +37,10 @@ export default Vue.extend({
Progress.start();
this.fetching = true;
(this as any).api('posts/show', {
postId: this.$route.params.post
}).then(post => {
this.post = post;
(this as any).api('notes/show', {
noteId: this.$route.params.note
}).then(note => {
this.note = note;
this.fetching = false;
Progress.done();

View File

@@ -2,13 +2,13 @@
<mk-ui>
<span slot="header">%fa:search% {{ q }}</span>
<main v-if="!fetching">
<mk-posts :class="$style.posts" :posts="posts">
<span v-if="posts.length == 0">{{ '%i18n:mobile.tags.mk-search-posts.empty%'.replace('{}', q) }}</span>
<mk-notes :class="$style.notes" :notes="notes">
<span v-if="notes.length == 0">{{ '%i18n:mobile.tags.mk-search-notes.empty%'.replace('{}', q) }}</span>
<button v-if="existMore" @click="more" :disabled="fetching" slot="tail">
<span v-if="!fetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
</button>
</mk-posts>
</mk-notes>
</main>
</mk-ui>
</template>
@@ -25,7 +25,7 @@ export default Vue.extend({
return {
fetching: true,
existMore: false,
posts: [],
notes: [],
offset: 0
};
},
@@ -48,30 +48,30 @@ export default Vue.extend({
this.fetching = true;
Progress.start();
(this as any).api('posts/search', Object.assign({
(this as any).api('notes/search', Object.assign({
limit: limit + 1
}, parse(this.q))).then(posts => {
if (posts.length == limit + 1) {
posts.pop();
}, parse(this.q))).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
this.existMore = true;
}
this.posts = posts;
this.notes = notes;
this.fetching = false;
Progress.done();
});
},
more() {
this.offset += limit;
(this as any).api('posts/search', Object.assign({
(this as any).api('notes/search', Object.assign({
limit: limit + 1,
offset: this.offset
}, parse(this.q))).then(posts => {
if (posts.length == limit + 1) {
posts.pop();
}, parse(this.q))).then(notes => {
if (notes.length == limit + 1) {
notes.pop();
} else {
this.existMore = false;
}
this.posts = this.posts.concat(posts);
this.notes = this.notes.concat(notes);
});
}
}
@@ -79,7 +79,7 @@ export default Vue.extend({
</script>
<style lang="stylus" module>
.posts
.notes
margin 8px auto
max-width 500px
width calc(100% - 16px)

View File

@@ -27,8 +27,8 @@
</div>
<div class="status">
<a>
<b>{{ user.postsCount | number }}</b>
<i>%i18n:mobile.tags.mk-user.posts%</i>
<b>{{ user.notesCount | number }}</b>
<i>%i18n:mobile.tags.mk-user.notes%</i>
</a>
<a :href="`@${acct}/following`">
<b>{{ user.followingCount | number }}</b>
@@ -44,13 +44,13 @@
<nav>
<div class="nav-container">
<a :data-is-active=" page == 'home' " @click="page = 'home'">%i18n:mobile.tags.mk-user.overview%</a>
<a :data-is-active=" page == 'posts' " @click="page = 'posts'">%i18n:mobile.tags.mk-user.timeline%</a>
<a :data-is-active=" page == 'notes' " @click="page = 'notes'">%i18n:mobile.tags.mk-user.timeline%</a>
<a :data-is-active=" page == 'media' " @click="page = 'media'">%i18n:mobile.tags.mk-user.media%</a>
</div>
</nav>
<div class="body">
<x-home v-if="page == 'home'" :user="user"/>
<mk-user-timeline v-if="page == 'posts'" :user="user"/>
<mk-user-timeline v-if="page == 'notes'" :user="user"/>
<mk-user-timeline v-if="page == 'media'" :user="user" with-media/>
</div>
</main>

View File

@@ -1,10 +1,10 @@
<template>
<div class="root posts">
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-posts.loading%<mk-ellipsis/></p>
<div v-if="!fetching && posts.length > 0">
<mk-post-card v-for="post in posts" :key="post.id" :post="post"/>
<div class="root notes">
<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:mobile.tags.mk-user-overview-notes.loading%<mk-ellipsis/></p>
<div v-if="!fetching && notes.length > 0">
<mk-note-card v-for="note in notes" :key="note.id" :note="note"/>
</div>
<p class="empty" v-if="!fetching && posts.length == 0">%i18n:mobile.tags.mk-user-overview-posts.no-posts%</p>
<p class="empty" v-if="!fetching && notes.length == 0">%i18n:mobile.tags.mk-user-overview-notes.no-notes%</p>
</div>
</template>
@@ -15,14 +15,14 @@ export default Vue.extend({
data() {
return {
fetching: true,
posts: []
notes: []
};
},
mounted() {
(this as any).api('users/posts', {
(this as any).api('users/notes', {
userId: this.user.id
}).then(posts => {
this.posts = posts;
}).then(notes => {
this.notes = notes;
this.fetching = false;
});
}
@@ -30,7 +30,7 @@ export default Vue.extend({
</script>
<style lang="stylus" scoped>
.root.posts
.root.notes
> div
overflow-x scroll

View File

@@ -5,7 +5,7 @@
<a v-for="image in images"
class="img"
:style="`background-image: url(${image.media.url}?thumbnail&size=256)`"
:href="`/@${getAcct(image.post.user)}/${image.post.id}`"
:href="`/@${getAcct(image.note.user)}/${image.note.id}`"
></a>
</div>
<p class="empty" v-if="!fetching && images.length == 0">%i18n:mobile.tags.mk-user-overview-photos.no-photos%</p>
@@ -28,15 +28,15 @@ export default Vue.extend({
getAcct
},
mounted() {
(this as any).api('users/posts', {
(this as any).api('users/notes', {
userId: this.user.id,
withMedia: true,
limit: 6
}).then(posts => {
posts.forEach(post => {
post.media.forEach(media => {
}).then(notes => {
notes.forEach(note => {
note.media.forEach(media => {
if (this.images.length < 9) this.images.push({
post,
note,
media
});
});

View File

@@ -1,10 +1,10 @@
<template>
<div class="root home">
<mk-post-detail v-if="user.pinnedPost" :post="user.pinnedPost" :compact="true"/>
<section class="recent-posts">
<h2>%fa:R comments%%i18n:mobile.tags.mk-user-overview.recent-posts%</h2>
<mk-note-detail v-if="user.pinnedNote" :note="user.pinnedNote" :compact="true"/>
<section class="recent-notes">
<h2>%fa:R comments%%i18n:mobile.tags.mk-user-overview.recent-notes%</h2>
<div>
<x-posts :user="user"/>
<x-notes :user="user"/>
</div>
</section>
<section class="images">
@@ -37,14 +37,14 @@
<script lang="ts">
import Vue from 'vue';
import XPosts from './home.posts.vue';
import XNotes from './home.notes.vue';
import XPhotos from './home.photos.vue';
import XFriends from './home.friends.vue';
import XFollowersYouKnow from './home.followers-you-know.vue';
export default Vue.extend({
components: {
XPosts,
XNotes,
XPhotos,
XFriends,
XFollowersYouKnow
@@ -58,7 +58,7 @@ export default Vue.extend({
max-width 600px
margin 0 auto
> .mk-post-detail
> .mk-note-detail
margin 0 0 8px 0
> section