mirror of
https://activitypub.software/TransFem-org/Sharkey.git
synced 2024-12-15 21:45:45 +01:00
wip
This commit is contained in:
parent
f5637205cb
commit
7a97924d01
15 changed files with 265 additions and 195 deletions
|
@ -18,6 +18,7 @@ import messaging from './messaging.vue';
|
||||||
import messagingRoom from './messaging-room.vue';
|
import messagingRoom from './messaging-room.vue';
|
||||||
import urlPreview from './url-preview.vue';
|
import urlPreview from './url-preview.vue';
|
||||||
import twitterSetting from './twitter-setting.vue';
|
import twitterSetting from './twitter-setting.vue';
|
||||||
|
import fileTypeIcon from './file-type-icon.vue';
|
||||||
|
|
||||||
Vue.component('mk-signin', signin);
|
Vue.component('mk-signin', signin);
|
||||||
Vue.component('mk-signup', signup);
|
Vue.component('mk-signup', signup);
|
||||||
|
@ -37,3 +38,4 @@ Vue.component('mk-messaging', messaging);
|
||||||
Vue.component('mk-messaging-room', messagingRoom);
|
Vue.component('mk-messaging-room', messagingRoom);
|
||||||
Vue.component('mk-url-preview', urlPreview);
|
Vue.component('mk-url-preview', urlPreview);
|
||||||
Vue.component('mk-twitter-setting', twitterSetting);
|
Vue.component('mk-twitter-setting', twitterSetting);
|
||||||
|
Vue.component('mk-file-type-icon', fileTypeIcon);
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
<footer>
|
<footer>
|
||||||
<div ref="notifications"></div>
|
<div ref="notifications" class="notifications"></div>
|
||||||
<div class="grippie" title="%i18n:common.tags.mk-messaging-room.resize-form%"></div>
|
<div class="grippie" title="%i18n:common.tags.mk-messaging-room.resize-form%"></div>
|
||||||
<x-form :user="user"/>
|
<x-form :user="user"/>
|
||||||
</footer>
|
</footer>
|
||||||
|
@ -278,7 +278,7 @@ export default Vue.extend({
|
||||||
background rgba(255, 255, 255, 0.95)
|
background rgba(255, 255, 255, 0.95)
|
||||||
background-clip content-box
|
background-clip content-box
|
||||||
|
|
||||||
> [ref='notifications']
|
> .notifications
|
||||||
position absolute
|
position absolute
|
||||||
top -48px
|
top -48px
|
||||||
width 100%
|
width 100%
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="mk-post-menu">
|
<div class="mk-post-menu">
|
||||||
<div class="backdrop" ref="backdrop" @click="close"></div>
|
<div class="backdrop" ref="backdrop" @click="close"></div>
|
||||||
<div class="popover { compact: opts.compact }" ref="popover">
|
<div class="popover" :class="{ compact }" ref="popover">
|
||||||
<button v-if="post.user_id === I.id" @click="pin">%i18n:common.tags.mk-post-menu.pin%</button>
|
<button v-if="post.user_id == os.i.id" @click="pin">%i18n:common.tags.mk-post-menu.pin%</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
@ -14,36 +14,38 @@ import * as anime from 'animejs';
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
props: ['post', 'source', 'compact'],
|
props: ['post', 'source', 'compact'],
|
||||||
mounted() {
|
mounted() {
|
||||||
const popover = this.$refs.popover as any;
|
this.$nextTick(() => {
|
||||||
|
const popover = this.$refs.popover as any;
|
||||||
|
|
||||||
const rect = this.source.getBoundingClientRect();
|
const rect = this.source.getBoundingClientRect();
|
||||||
const width = popover.offsetWidth;
|
const width = popover.offsetWidth;
|
||||||
const height = popover.offsetHeight;
|
const height = popover.offsetHeight;
|
||||||
|
|
||||||
if (this.compact) {
|
if (this.compact) {
|
||||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||||
const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2);
|
const y = rect.top + window.pageYOffset + (this.source.offsetHeight / 2);
|
||||||
popover.style.left = (x - (width / 2)) + 'px';
|
popover.style.left = (x - (width / 2)) + 'px';
|
||||||
popover.style.top = (y - (height / 2)) + 'px';
|
popover.style.top = (y - (height / 2)) + 'px';
|
||||||
} else {
|
} else {
|
||||||
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
const x = rect.left + window.pageXOffset + (this.source.offsetWidth / 2);
|
||||||
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
|
const y = rect.top + window.pageYOffset + this.source.offsetHeight;
|
||||||
popover.style.left = (x - (width / 2)) + 'px';
|
popover.style.left = (x - (width / 2)) + 'px';
|
||||||
popover.style.top = y + 'px';
|
popover.style.top = y + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
anime({
|
anime({
|
||||||
targets: this.$refs.backdrop,
|
targets: this.$refs.backdrop,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
duration: 100,
|
duration: 100,
|
||||||
easing: 'linear'
|
easing: 'linear'
|
||||||
});
|
});
|
||||||
|
|
||||||
anime({
|
anime({
|
||||||
targets: this.$refs.popover,
|
targets: this.$refs.popover,
|
||||||
opacity: 1,
|
opacity: 1,
|
||||||
scale: [0.5, 1],
|
scale: [0.5, 1],
|
||||||
duration: 500
|
duration: 500
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -134,5 +136,6 @@ $border-color = rgba(27, 31, 35, 0.15)
|
||||||
|
|
||||||
> button
|
> button
|
||||||
display block
|
display block
|
||||||
|
padding 16px
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -8,7 +8,10 @@
|
||||||
<router-link class="avatar-anchor" :to="`/${post.user.username}`" v-user-preview="post.user_id">
|
<router-link class="avatar-anchor" :to="`/${post.user.username}`" v-user-preview="post.user_id">
|
||||||
<img class="avatar" :src="`${post.user.avatar_url}?thumbnail&size=32`" alt="avatar"/>
|
<img class="avatar" :src="`${post.user.avatar_url}?thumbnail&size=32`" alt="avatar"/>
|
||||||
</router-link>
|
</router-link>
|
||||||
%fa:retweet%{{'%i18n:desktop.tags.mk-timeline-post.reposted-by%'.substr(0, '%i18n:desktop.tags.mk-timeline-post.reposted-by%'.indexOf('{'))}}<a class="name" :href="`/${post.user.username}`" v-user-preview="post.user_id">{{ post.user.name }}</a>{{'%i18n:desktop.tags.mk-timeline-post.reposted-by%'.substr('%i18n:desktop.tags.mk-timeline-post.reposted-by%'.indexOf('}') + 1)}}
|
%fa:retweet%
|
||||||
|
{{ '%i18n:desktop.tags.mk-timeline-post.reposted-by%'.substr(0, '%i18n:desktop.tags.mk-timeline-post.reposted-by%'.indexOf('{')) }}
|
||||||
|
<a class="name" :href="`/${post.user.username}`" v-user-preview="post.user_id">{{ post.user.name }}</a>
|
||||||
|
{{ '%i18n:desktop.tags.mk-timeline-post.reposted-by%'.substr('%i18n:desktop.tags.mk-timeline-post.reposted-by%'.indexOf('}') + 1) }}
|
||||||
</p>
|
</p>
|
||||||
<mk-time :time="post.created_at"/>
|
<mk-time :time="post.created_at"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
<header>
|
<header>
|
||||||
<h1>%i18n:mobile.tags.mk-drive-selector.select-file%<span class="count" v-if="files.length > 0">({{ files.length }})</span></h1>
|
<h1>%i18n:mobile.tags.mk-drive-selector.select-file%<span class="count" v-if="files.length > 0">({{ files.length }})</span></h1>
|
||||||
<button class="close" @click="cancel">%fa:times%</button>
|
<button class="close" @click="cancel">%fa:times%</button>
|
||||||
<button v-if="opts.multiple" class="ok" @click="ok">%fa:check%</button>
|
<button v-if="multiple" class="ok" @click="ok">%fa:check%</button>
|
||||||
</header>
|
</header>
|
||||||
<mk-drive ref="browser"
|
<mk-drive ref="browser"
|
||||||
select-file
|
:select-file="true"
|
||||||
:multiple="multiple"
|
:multiple="multiple"
|
||||||
@change-selection="onChangeSelection"
|
@change-selection="onChangeSelection"
|
||||||
@selected="onSelected"
|
@selected="onSelected"
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<a class="folder" @click.prevent="onClick" :href="`/i/drive/folder/${ folder.id }`">
|
<a class="root folder" @click.prevent="onClick" :href="`/i/drive/folder/${ folder.id }`">
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<p class="name">%fa:folder%{{ folder.name }}</p>%fa:angle-right%
|
<p class="name">%fa:folder%{{ folder.name }}</p>%fa:angle-right%
|
||||||
</div>
|
</div>
|
||||||
|
@ -24,7 +24,7 @@ export default Vue.extend({
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
.folder
|
.root.folder
|
||||||
display block
|
display block
|
||||||
color #777
|
color #777
|
||||||
text-decoration none !important
|
text-decoration none !important
|
||||||
|
|
|
@ -26,11 +26,11 @@
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="folders" v-if="folders.length > 0">
|
<div class="folders" v-if="folders.length > 0">
|
||||||
<mk-drive-folder v-for="folder in folders" :key="folder.id" :folder="folder"/>
|
<x-folder v-for="folder in folders" :key="folder.id" :folder="folder"/>
|
||||||
<p v-if="moreFolders">%i18n:mobile.tags.mk-drive.load-more%</p>
|
<p v-if="moreFolders">%i18n:mobile.tags.mk-drive.load-more%</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="files" v-if="files.length > 0">
|
<div class="files" v-if="files.length > 0">
|
||||||
<mk-drive-file v-for="file in files" :key="file.id" :file="file"/>
|
<x-file v-for="file in files" :key="file.id" :file="file"/>
|
||||||
<button class="more" v-if="moreFiles" @click="fetchMoreFiles">
|
<button class="more" v-if="moreFiles" @click="fetchMoreFiles">
|
||||||
{{ fetchingMoreFiles ? '%i18n:common.loading%' : '%i18n:mobile.tags.mk-drive.load-more%' }}
|
{{ fetchingMoreFiles ? '%i18n:common.loading%' : '%i18n:mobile.tags.mk-drive.load-more%' }}
|
||||||
</button>
|
</button>
|
||||||
|
@ -46,15 +46,23 @@
|
||||||
<div class="dot2"></div>
|
<div class="dot2"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input ref="file" type="file" multiple="multiple" @change="onChangeLocalFile"/>
|
<input ref="file" class="file" type="file" multiple="multiple" @change="onChangeLocalFile"/>
|
||||||
<mk-drive-file-detail v-if="file != null" :file="file"/>
|
<x-file-detail v-if="file != null" :file="file"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import XFolder from './drive.folder.vue';
|
||||||
|
import XFile from './drive.file.vue';
|
||||||
|
import XFileDetail from './drive.file-detail.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XFolder,
|
||||||
|
XFile,
|
||||||
|
XFileDetail
|
||||||
|
},
|
||||||
props: ['initFolder', 'initFile', 'selectFile', 'multiple', 'isNaked', 'top'],
|
props: ['initFolder', 'initFile', 'selectFile', 'multiple', 'isNaked', 'top'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -423,8 +431,7 @@ export default Vue.extend({
|
||||||
alert('現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。');
|
alert('現在いる場所はルートで、フォルダではないため移動はできません。移動したいフォルダに移動してからやってください。');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dialog = riot.mount(document.body.appendChild(document.createElement('mk-drive-folder-selector')))[0];
|
(this as any).apis.chooseDriveFolder().then(folder => {
|
||||||
dialog.one('selected', folder => {
|
|
||||||
(this as any).api('drive/folders/update', {
|
(this as any).api('drive/folders/update', {
|
||||||
parent_id: folder ? folder.id : null,
|
parent_id: folder ? folder.id : null,
|
||||||
folder_id: this.folder.id
|
folder_id: this.folder.id
|
||||||
|
@ -510,11 +517,11 @@ export default Vue.extend({
|
||||||
color #777
|
color #777
|
||||||
|
|
||||||
> .folders
|
> .folders
|
||||||
> .mk-drive-folder
|
> .folder
|
||||||
border-bottom solid 1px #eee
|
border-bottom solid 1px #eee
|
||||||
|
|
||||||
> .files
|
> .files
|
||||||
> .mk-drive-file
|
> .file
|
||||||
border-bottom solid 1px #eee
|
border-bottom solid 1px #eee
|
||||||
|
|
||||||
> .more
|
> .more
|
||||||
|
@ -568,7 +575,7 @@ export default Vue.extend({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
> [ref='file']
|
> .file
|
||||||
display none
|
display none
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -5,9 +5,11 @@ import home from './home.vue';
|
||||||
import timeline from './timeline.vue';
|
import timeline from './timeline.vue';
|
||||||
import posts from './posts.vue';
|
import posts from './posts.vue';
|
||||||
import imagesImage from './images-image.vue';
|
import imagesImage from './images-image.vue';
|
||||||
|
import drive from './drive.vue';
|
||||||
|
|
||||||
Vue.component('mk-ui', ui);
|
Vue.component('mk-ui', ui);
|
||||||
Vue.component('mk-home', home);
|
Vue.component('mk-home', home);
|
||||||
Vue.component('mk-timeline', timeline);
|
Vue.component('mk-timeline', timeline);
|
||||||
Vue.component('mk-posts', posts);
|
Vue.component('mk-posts', posts);
|
||||||
Vue.component('mk-images-image', imagesImage);
|
Vue.component('mk-images-image', imagesImage);
|
||||||
|
Vue.component('mk-drive', drive);
|
||||||
|
|
|
@ -3,37 +3,40 @@
|
||||||
<header>
|
<header>
|
||||||
<button class="cancel" @click="cancel">%fa:times%</button>
|
<button class="cancel" @click="cancel">%fa:times%</button>
|
||||||
<div>
|
<div>
|
||||||
<span v-if="refs.text" class="text-count" :class="{ over: refs.text.value.length > 1000 }">{{ 1000 - refs.text.value.length }}</span>
|
<span class="text-count" :class="{ over: text.length > 1000 }">{{ 1000 - text.length }}</span>
|
||||||
<button class="submit" @click="post">%i18n:mobile.tags.mk-post-form.submit%</button>
|
<button class="submit" :disabled="posting" @click="post">%i18n:mobile.tags.mk-post-form.submit%</button>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div class="form">
|
<div class="form">
|
||||||
<mk-post-preview v-if="reply" :post="reply"/>
|
<mk-post-preview v-if="reply" :post="reply"/>
|
||||||
<textarea v-model="text" :disabled="wait" :placeholder="reply ? '%i18n:mobile.tags.mk-post-form.reply-placeholder%' : '%i18n:mobile.tags.mk-post-form.post-placeholder%'"></textarea>
|
<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>
|
||||||
<div class="attaches" v-show="files.length != 0">
|
<div class="attaches" v-show="files.length != 0">
|
||||||
<ul class="files" ref="attaches">
|
<x-draggable class="files" :list="files" :options="{ animation: 150 }">
|
||||||
<li class="file" v-for="file in files">
|
<div class="file" v-for="file in files" :key="file.id">
|
||||||
<div class="img" :style="`background-image: url(${file.url}?thumbnail&size=128)`" @click="removeFile(file)"></div>
|
<div class="img" :style="`background-image: url(${file.url}?thumbnail&size=128)`" @click="detachMedia(file)"></div>
|
||||||
</li>
|
</div>
|
||||||
</ul>
|
</x-draggable>
|
||||||
</div>
|
</div>
|
||||||
<mk-poll-editor v-if="poll" ref="poll"/>
|
<mk-poll-editor v-if="poll" ref="poll"/>
|
||||||
<mk-uploader @uploaded="attachMedia" @change="onChangeUploadings"/>
|
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
|
||||||
<button ref="upload" @click="selectFile">%fa:upload%</button>
|
<button class="upload" @click="chooseFile">%fa:upload%</button>
|
||||||
<button ref="drive" @click="selectFileFromDrive">%fa:cloud%</button>
|
<button class="drive" @click="chooseFileFromDrive">%fa:cloud%</button>
|
||||||
<button class="kao" @click="kao">%fa:R smile%</button>
|
<button class="kao" @click="kao">%fa:R smile%</button>
|
||||||
<button class="poll" @click="addPoll">%fa:chart-pie%</button>
|
<button class="poll" @click="poll = true">%fa:chart-pie%</button>
|
||||||
<input ref="file" type="file" accept="image/*" multiple="multiple" @change="onChangeFile"/>
|
<input ref="file" class="file" type="file" accept="image/*" multiple="multiple" @change="onChangeFile"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import Sortable from 'sortablejs';
|
import * as XDraggable from 'vuedraggable';
|
||||||
import getKao from '../../../common/scripts/get-kao';
|
import getKao from '../../../common/scripts/get-kao';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XDraggable
|
||||||
|
},
|
||||||
props: ['reply'],
|
props: ['reply'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -45,19 +48,27 @@ export default Vue.extend({
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
(this.$refs.text as any).focus();
|
this.$nextTick(() => {
|
||||||
|
(this.$refs.text as any).focus();
|
||||||
new Sortable(this.$refs.attaches, {
|
|
||||||
animation: 150
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
chooseFile() {
|
||||||
|
(this.$refs.file as any).click();
|
||||||
|
},
|
||||||
|
chooseFileFromDrive() {
|
||||||
|
(this as any).apis.chooseDriveFile({
|
||||||
|
multiple: true
|
||||||
|
}).then(files => {
|
||||||
|
files.forEach(this.attachMedia);
|
||||||
|
});
|
||||||
|
},
|
||||||
attachMedia(driveFile) {
|
attachMedia(driveFile) {
|
||||||
this.files.push(driveFile);
|
this.files.push(driveFile);
|
||||||
this.$emit('change-attached-media', this.files);
|
this.$emit('change-attached-media', this.files);
|
||||||
},
|
},
|
||||||
detachMedia(id) {
|
detachMedia(file) {
|
||||||
this.files = this.files.filter(x => x.id != id);
|
this.files = this.files.filter(x => x.id != file.id);
|
||||||
this.$emit('change-attached-media', this.files);
|
this.$emit('change-attached-media', this.files);
|
||||||
},
|
},
|
||||||
onChangeFile() {
|
onChangeFile() {
|
||||||
|
@ -75,6 +86,20 @@ export default Vue.extend({
|
||||||
this.poll = false;
|
this.poll = false;
|
||||||
this.$emit('change-attached-media');
|
this.$emit('change-attached-media');
|
||||||
},
|
},
|
||||||
|
post() {
|
||||||
|
this.posting = true;
|
||||||
|
(this as any).api('posts/create', {
|
||||||
|
text: this.text == '' ? undefined : this.text,
|
||||||
|
media_ids: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
||||||
|
reply_id: this.reply ? this.reply.id : undefined,
|
||||||
|
poll: this.poll ? (this.$refs.poll as any).get() : undefined
|
||||||
|
}).then(data => {
|
||||||
|
this.$emit('post');
|
||||||
|
this.$destroy();
|
||||||
|
}).catch(err => {
|
||||||
|
this.posting = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
cancel() {
|
cancel() {
|
||||||
this.$emit('cancel');
|
this.$emit('cancel');
|
||||||
this.$destroy();
|
this.$destroy();
|
||||||
|
@ -167,10 +192,10 @@ export default Vue.extend({
|
||||||
margin 8px 0 0 0
|
margin 8px 0 0 0
|
||||||
padding 8px
|
padding 8px
|
||||||
|
|
||||||
> [ref='file']
|
> .file
|
||||||
display none
|
display none
|
||||||
|
|
||||||
> [ref='text']
|
> textarea
|
||||||
display block
|
display block
|
||||||
padding 12px
|
padding 12px
|
||||||
margin 0
|
margin 0
|
||||||
|
@ -187,8 +212,8 @@ export default Vue.extend({
|
||||||
&:disabled
|
&:disabled
|
||||||
opacity 0.5
|
opacity 0.5
|
||||||
|
|
||||||
> [ref='upload']
|
> .upload
|
||||||
> [ref='drive']
|
> .drive
|
||||||
.kao
|
.kao
|
||||||
.poll
|
.poll
|
||||||
display inline-block
|
display inline-block
|
||||||
|
|
|
@ -1,117 +0,0 @@
|
||||||
<template>
|
|
||||||
<div class="mk-posts-post-sub">
|
|
||||||
<article>
|
|
||||||
<a class="avatar-anchor" href={ '/' + post.user.username }>
|
|
||||||
<img class="avatar" src={ post.user.avatar_url + '?thumbnail&size=96' } alt="avatar"/>
|
|
||||||
</a>
|
|
||||||
<div class="main">
|
|
||||||
<header>
|
|
||||||
<a class="name" href={ '/' + post.user.username }>{ post.user.name }</a>
|
|
||||||
<span class="username">@{ post.user.username }</span>
|
|
||||||
<a class="created-at" href={ '/' + post.user.username + '/' + post.id }>
|
|
||||||
<mk-time time={ post.created_at }/>
|
|
||||||
</a>
|
|
||||||
</header>
|
|
||||||
<div class="body">
|
|
||||||
<mk-sub-post-content class="text" post={ post }/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</article>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
export default Vue.extend({
|
|
||||||
props: ['post']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.mk-posts-post-sub
|
|
||||||
font-size 0.9em
|
|
||||||
|
|
||||||
> article
|
|
||||||
padding 16px
|
|
||||||
|
|
||||||
&:after
|
|
||||||
content ""
|
|
||||||
display block
|
|
||||||
clear both
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
> .main > footer > button
|
|
||||||
color #888
|
|
||||||
|
|
||||||
> .avatar-anchor
|
|
||||||
display block
|
|
||||||
float left
|
|
||||||
margin 0 10px 0 0
|
|
||||||
|
|
||||||
@media (min-width 500px)
|
|
||||||
margin-right 16px
|
|
||||||
|
|
||||||
> .avatar
|
|
||||||
display block
|
|
||||||
width 44px
|
|
||||||
height 44px
|
|
||||||
margin 0
|
|
||||||
border-radius 8px
|
|
||||||
vertical-align bottom
|
|
||||||
|
|
||||||
@media (min-width 500px)
|
|
||||||
width 52px
|
|
||||||
height 52px
|
|
||||||
|
|
||||||
> .main
|
|
||||||
float left
|
|
||||||
width calc(100% - 54px)
|
|
||||||
|
|
||||||
@media (min-width 500px)
|
|
||||||
width calc(100% - 68px)
|
|
||||||
|
|
||||||
> header
|
|
||||||
display flex
|
|
||||||
margin-bottom 2px
|
|
||||||
white-space nowrap
|
|
||||||
|
|
||||||
> .name
|
|
||||||
display block
|
|
||||||
margin 0 0.5em 0 0
|
|
||||||
padding 0
|
|
||||||
overflow hidden
|
|
||||||
color #607073
|
|
||||||
font-size 1em
|
|
||||||
font-weight 700
|
|
||||||
text-align left
|
|
||||||
text-decoration none
|
|
||||||
text-overflow ellipsis
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
text-decoration underline
|
|
||||||
|
|
||||||
> .username
|
|
||||||
text-align left
|
|
||||||
margin 0
|
|
||||||
color #d1d8da
|
|
||||||
|
|
||||||
> .created-at
|
|
||||||
margin-left auto
|
|
||||||
color #b2b8bb
|
|
||||||
|
|
||||||
> .body
|
|
||||||
|
|
||||||
> .text
|
|
||||||
cursor default
|
|
||||||
margin 0
|
|
||||||
padding 0
|
|
||||||
font-size 1.1em
|
|
||||||
color #717171
|
|
||||||
|
|
||||||
pre
|
|
||||||
max-height 120px
|
|
||||||
font-size 80%
|
|
||||||
|
|
||||||
</style>
|
|
||||||
|
|
108
src/web/app/mobile/views/components/posts.post.sub.vue
Normal file
108
src/web/app/mobile/views/components/posts.post.sub.vue
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
<template>
|
||||||
|
<div class="sub">
|
||||||
|
<router-link class="avatar-anchor" :to="`/${post.user.username}`">
|
||||||
|
<img class="avatar" :src="`${post.user.avatar_url}?thumbnail&size=96`" alt="avatar"/>
|
||||||
|
</router-link>
|
||||||
|
<div class="main">
|
||||||
|
<header>
|
||||||
|
<router-link class="name" :to="`/${post.user.username}`">{{ post.user.name }}</router-link>
|
||||||
|
<span class="username">@{{ post.user.username }}</span>
|
||||||
|
<router-link class="created-at" :href="`/${post.user.username}/${post.id}`">
|
||||||
|
<mk-time :time="post.created_at"/>
|
||||||
|
</router-link>
|
||||||
|
</header>
|
||||||
|
<div class="body">
|
||||||
|
<mk-sub-post-content class="text" :post="post"/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['post']
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.sub
|
||||||
|
font-size 0.9em
|
||||||
|
padding 16px
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
display block
|
||||||
|
clear both
|
||||||
|
|
||||||
|
> .avatar-anchor
|
||||||
|
display block
|
||||||
|
float left
|
||||||
|
margin 0 10px 0 0
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
margin-right 16px
|
||||||
|
|
||||||
|
> .avatar
|
||||||
|
display block
|
||||||
|
width 44px
|
||||||
|
height 44px
|
||||||
|
margin 0
|
||||||
|
border-radius 8px
|
||||||
|
vertical-align bottom
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
width 52px
|
||||||
|
height 52px
|
||||||
|
|
||||||
|
> .main
|
||||||
|
float left
|
||||||
|
width calc(100% - 54px)
|
||||||
|
|
||||||
|
@media (min-width 500px)
|
||||||
|
width calc(100% - 68px)
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
margin-bottom 2px
|
||||||
|
white-space nowrap
|
||||||
|
|
||||||
|
> .name
|
||||||
|
display block
|
||||||
|
margin 0 0.5em 0 0
|
||||||
|
padding 0
|
||||||
|
overflow hidden
|
||||||
|
color #607073
|
||||||
|
font-size 1em
|
||||||
|
font-weight 700
|
||||||
|
text-align left
|
||||||
|
text-decoration none
|
||||||
|
text-overflow ellipsis
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
text-decoration underline
|
||||||
|
|
||||||
|
> .username
|
||||||
|
text-align left
|
||||||
|
margin 0
|
||||||
|
color #d1d8da
|
||||||
|
|
||||||
|
> .created-at
|
||||||
|
margin-left auto
|
||||||
|
color #b2b8bb
|
||||||
|
|
||||||
|
> .body
|
||||||
|
|
||||||
|
> .text
|
||||||
|
cursor default
|
||||||
|
margin 0
|
||||||
|
padding 0
|
||||||
|
font-size 1.1em
|
||||||
|
color #717171
|
||||||
|
|
||||||
|
pre
|
||||||
|
max-height 120px
|
||||||
|
font-size 80%
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
|
@ -69,8 +69,14 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import MkPostMenu from '../../../common/views/components/post-menu.vue';
|
||||||
|
import MkReactionPicker from '../../../common/views/components/reaction-picker.vue';
|
||||||
|
import XSub from './posts.post.sub.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XSub
|
||||||
|
},
|
||||||
props: ['post'],
|
props: ['post'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
@ -152,6 +158,34 @@ export default Vue.extend({
|
||||||
this.$emit('update:post', post);
|
this.$emit('update:post', post);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
reply() {
|
||||||
|
(this as any).apis.post({
|
||||||
|
reply: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
repost() {
|
||||||
|
(this as any).apis.post({
|
||||||
|
repost: this.p
|
||||||
|
});
|
||||||
|
},
|
||||||
|
react() {
|
||||||
|
document.body.appendChild(new MkReactionPicker({
|
||||||
|
propsData: {
|
||||||
|
source: this.$refs.reactButton,
|
||||||
|
post: this.p,
|
||||||
|
compact: true
|
||||||
|
}
|
||||||
|
}).$mount().$el);
|
||||||
|
},
|
||||||
|
menu() {
|
||||||
|
document.body.appendChild(new MkPostMenu({
|
||||||
|
propsData: {
|
||||||
|
source: this.$refs.menuButton,
|
||||||
|
post: this.p,
|
||||||
|
compact: true
|
||||||
|
}
|
||||||
|
}).$mount().$el);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
%fa:R comments%
|
%fa:R comments%
|
||||||
%i18n:mobile.tags.mk-home-timeline.empty-timeline%
|
%i18n:mobile.tags.mk-home-timeline.empty-timeline%
|
||||||
</div>
|
</div>
|
||||||
<button v-if="canFetchMore" @click="more" :disabled="fetching" slot="tail">
|
<button @click="more" :disabled="fetching" slot="tail">
|
||||||
<span v-if="!fetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
|
<span v-if="!fetching">%i18n:mobile.tags.mk-timeline.load-more%</span>
|
||||||
<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
|
<span v-if="fetching">%i18n:common.loading%<mk-ellipsis/></span>
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -24,7 +24,6 @@ export default Vue.extend({
|
||||||
props: ['func'],
|
props: ['func'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
func: null,
|
|
||||||
hasUnreadNotifications: false,
|
hasUnreadNotifications: false,
|
||||||
hasUnreadMessagingMessages: false,
|
hasUnreadMessagingMessages: false,
|
||||||
connection: null,
|
connection: null,
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="nav" :style="{ display: isOpen ? 'block' : 'none' }">
|
<div class="nav" :style="{ display: isOpen ? 'block' : 'none' }">
|
||||||
<div class="backdrop" @click="parent.toggleDrawer"></div>
|
<div class="backdrop" @click="$parent.isDrawerOpening = false"></div>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<router-link class="me" v-if="os.isSignedIn" :to="`/${os.i.username}`">
|
<router-link class="me" v-if="os.isSignedIn" :to="`/${os.i.username}`">
|
||||||
<img class="avatar" :src="`${os.i.avatar_url}?thumbnail&size=128`" alt="avatar"/>
|
<img class="avatar" :src="`${os.i.avatar_url}?thumbnail&size=128`" alt="avatar"/>
|
||||||
|
@ -8,36 +8,40 @@
|
||||||
</router-link>
|
</router-link>
|
||||||
<div class="links">
|
<div class="links">
|
||||||
<ul>
|
<ul>
|
||||||
<li><router-link href="/">%fa:home%%i18n:mobile.tags.mk-ui-nav.home%%fa:angle-right%</router-link></li>
|
<li><router-link to="/">%fa:home%%i18n:mobile.tags.mk-ui-nav.home%%fa:angle-right%</router-link></li>
|
||||||
<li><router-link href="/i/notifications">%fa:R bell%%i18n:mobile.tags.mk-ui-nav.notifications%<template v-if="hasUnreadNotifications">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/notifications">%fa:R bell%%i18n:mobile.tags.mk-ui-nav.notifications%<template v-if="hasUnreadNotifications">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
<li><router-link href="/i/messaging">%fa:R comments%%i18n:mobile.tags.mk-ui-nav.messaging%<template v-if="hasUnreadMessagingMessages">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/messaging">%fa:R comments%%i18n:mobile.tags.mk-ui-nav.messaging%<template v-if="hasUnreadMessagingMessages">%fa:circle%</template>%fa:angle-right%</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a :href="chUrl" target="_blank">%fa:tv%%i18n:mobile.tags.mk-ui-nav.ch%%fa:angle-right%</a></li>
|
<li><a :href="chUrl" target="_blank">%fa:tv%%i18n:mobile.tags.mk-ui-nav.ch%%fa:angle-right%</a></li>
|
||||||
<li><router-link href="/i/drive">%fa:cloud%%i18n:mobile.tags.mk-ui-nav.drive%%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/drive">%fa:cloud%%i18n:mobile.tags.mk-ui-nav.drive%%fa:angle-right%</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a @click="search">%fa:search%%i18n:mobile.tags.mk-ui-nav.search%%fa:angle-right%</a></li>
|
<li><a @click="search">%fa:search%%i18n:mobile.tags.mk-ui-nav.search%%fa:angle-right%</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul>
|
<ul>
|
||||||
<li><router-link href="/i/settings">%fa:cog%%i18n:mobile.tags.mk-ui-nav.settings%%fa:angle-right%</router-link></li>
|
<li><router-link to="/i/settings">%fa:cog%%i18n:mobile.tags.mk-ui-nav.settings%%fa:angle-right%</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<a :href="aboutUrl"><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a>
|
<a :href="docsUrl"><p class="about">%i18n:mobile.tags.mk-ui-nav.about%</p></a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
import { docsUrl, chUrl } from '../../../config';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
|
props: ['isOpen'],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
hasUnreadNotifications: false,
|
hasUnreadNotifications: false,
|
||||||
hasUnreadMessagingMessages: false,
|
hasUnreadMessagingMessages: false,
|
||||||
connection: null,
|
connection: null,
|
||||||
connectionId: null
|
connectionId: null,
|
||||||
|
docsUrl,
|
||||||
|
chUrl
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
Loading…
Reference in a new issue