Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
adbe0fbcd1 | ||
|
|
7896242f57 | ||
|
|
4a6722b9e9 | ||
|
|
7c9fb5228b | ||
|
|
81805b01cc | ||
|
|
50824a7245 | ||
|
|
6f2953f3a7 | ||
|
|
dd3f007582 | ||
|
|
a4b2b093fc | ||
|
|
0fbf56219f | ||
|
|
0acacf7a8e | ||
|
|
c84500d914 |
@@ -379,6 +379,17 @@ common/views/components/poll-editor.vue:
|
|||||||
common/views/components/reaction-picker.vue:
|
common/views/components/reaction-picker.vue:
|
||||||
choose-reaction: "リアクションを選択"
|
choose-reaction: "リアクションを選択"
|
||||||
|
|
||||||
|
common/views/components/emoji-picker.vue:
|
||||||
|
custom-emoji: "カスタム絵文字"
|
||||||
|
people: "人"
|
||||||
|
animals-and-nature: "動物&自然"
|
||||||
|
food-and-drink: "食べ物&飲み物"
|
||||||
|
activity: "アクティビティ"
|
||||||
|
travel-and-places: "場所"
|
||||||
|
objects: "物"
|
||||||
|
symbols: "記号"
|
||||||
|
flags: "旗"
|
||||||
|
|
||||||
common/views/components/signin.vue:
|
common/views/components/signin.vue:
|
||||||
username: "ユーザー名"
|
username: "ユーザー名"
|
||||||
password: "パスワード"
|
password: "パスワード"
|
||||||
@@ -657,13 +668,6 @@ desktop/views/components/media-video.vue:
|
|||||||
sensitive: "閲覧注意"
|
sensitive: "閲覧注意"
|
||||||
click-to-show: "クリックして表示"
|
click-to-show: "クリックして表示"
|
||||||
|
|
||||||
desktop/views/components/follow-button.vue:
|
|
||||||
following: "フォロー中"
|
|
||||||
follow: "フォロー"
|
|
||||||
request-pending: "フォロー許可待ち"
|
|
||||||
follow-processing: "フォロー処理中"
|
|
||||||
follow-request: "フォロー申請"
|
|
||||||
|
|
||||||
desktop/views/components/followers-window.vue:
|
desktop/views/components/followers-window.vue:
|
||||||
followers: "{} のフォロワー"
|
followers: "{} のフォロワー"
|
||||||
|
|
||||||
@@ -1325,7 +1329,7 @@ mobile/views/components/media-video.vue:
|
|||||||
sensitive: "閲覧注意"
|
sensitive: "閲覧注意"
|
||||||
click-to-show: "クリックして表示"
|
click-to-show: "クリックして表示"
|
||||||
|
|
||||||
mobile/views/components/follow-button.vue:
|
common/views/components/follow-button.vue:
|
||||||
following: "フォロー中"
|
following: "フォロー中"
|
||||||
follow: "フォロー"
|
follow: "フォロー"
|
||||||
request-pending: "フォロー許可待ち"
|
request-pending: "フォロー許可待ち"
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.48.0",
|
"version": "10.49.1",
|
||||||
"clientVersion": "2.0.11733",
|
"clientVersion": "2.0.11745",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"main": "./built/index.js",
|
"main": "./built/index.js",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
|||||||
@@ -66,19 +66,6 @@ export default abstract class Chart<T> {
|
|||||||
} else {
|
} else {
|
||||||
this.collection.createIndex({ span: -1, date: -1 }, { unique: true });
|
this.collection.createIndex({ span: -1, date: -1 }, { unique: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
//#region 後方互換性のため
|
|
||||||
this.collection.find({ span: 'day' }, { fields: { _id: true, date: true } }).then(logs => {
|
|
||||||
logs.forEach(log => {
|
|
||||||
this.collection.update({ _id: log._id }, { $set: { date: utc(log.date).hour(0).toDate() } });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
this.collection.find({ span: 'hour' }, { fields: { _id: true, date: true } }).then(logs => {
|
|
||||||
logs.forEach(log => {
|
|
||||||
this.collection.update({ _id: log._id }, { $set: { date: utc(log.date).toDate() } });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
//#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
|
|||||||
@@ -146,6 +146,8 @@
|
|||||||
function refresh() {
|
function refresh() {
|
||||||
localStorage.setItem('shouldFlush', 'false');
|
localStorage.setItem('shouldFlush', 'false');
|
||||||
|
|
||||||
|
localStorage.removeItem('locale');
|
||||||
|
|
||||||
// Random
|
// Random
|
||||||
localStorage.setItem('salt', Math.random().toString().substr(2, 8));
|
localStorage.setItem('salt', Math.random().toString().substr(2, 8));
|
||||||
|
|
||||||
|
|||||||
200
src/client/app/common/views/components/emoji-picker.vue
Normal file
200
src/client/app/common/views/components/emoji-picker.vue
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<div class="prlncendiewqqkrevzeruhndoakghvtx">
|
||||||
|
<header>
|
||||||
|
<button v-for="category in categories"
|
||||||
|
:title="category.text"
|
||||||
|
@click="go(category.ref)"
|
||||||
|
:class="{ active: category.isActive }"
|
||||||
|
>
|
||||||
|
<fa :icon="category.icon" fixed-width/>
|
||||||
|
</button>
|
||||||
|
</header>
|
||||||
|
<div class="emojis" ref="emojis" @scroll.passive="onScroll">
|
||||||
|
<section v-for="category in categories" :ref="category.ref">
|
||||||
|
<header><fa :icon="category.icon" fixed-width/> {{ category.text }}</header>
|
||||||
|
<div v-if="category.name">
|
||||||
|
<button v-for="emoji in Object.entries(lib).filter(([k, v]) => v.category === category.name)"
|
||||||
|
:title="emoji[0]"
|
||||||
|
@click="chosen(emoji[1].char)"
|
||||||
|
>
|
||||||
|
<mk-emoji :emoji="emoji[1].char"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<button v-for="emoji in customEmojis"
|
||||||
|
:title="emoji.name"
|
||||||
|
@click="chosen(`:${emoji.name}:`)"
|
||||||
|
>
|
||||||
|
<img :src="emoji.url" :alt="emoji.name"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import i18n from '../../../i18n';
|
||||||
|
import { lib } from 'emojilib';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
i18n: i18n('common/views/components/emoji-picker.vue'),
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
lib,
|
||||||
|
customEmojis: [],
|
||||||
|
categories: [{
|
||||||
|
ref: 'customEmojiSection',
|
||||||
|
text: this.$t('custom-emoji'),
|
||||||
|
icon: ['fas', 'asterisk'],
|
||||||
|
isActive: true
|
||||||
|
}, {
|
||||||
|
name: 'people',
|
||||||
|
ref: 'peopleSection',
|
||||||
|
text: this.$t('people'),
|
||||||
|
icon: ['far', 'laugh'],
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'animals_and_nature',
|
||||||
|
ref: 'animalsAndNatureSection',
|
||||||
|
text: this.$t('animals-and-nature'),
|
||||||
|
icon: ['fas', 'leaf'],
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'food_and_drink',
|
||||||
|
ref: 'foodAndDrinkSection',
|
||||||
|
text: this.$t('food-and-drink'),
|
||||||
|
icon: ['fas', 'utensils'],
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'activity',
|
||||||
|
ref: 'activitySection',
|
||||||
|
text: this.$t('activity'),
|
||||||
|
icon: ['fas', 'futbol'],
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'travel_and_places',
|
||||||
|
ref: 'travelAndPlacesSection',
|
||||||
|
text: this.$t('travel-and-places'),
|
||||||
|
icon: ['fas', 'city'],
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'objects',
|
||||||
|
ref: 'objectsSection',
|
||||||
|
text: this.$t('objects'),
|
||||||
|
icon: ['fas', 'poo-storm'],
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'symbols',
|
||||||
|
ref: 'symbolsSection',
|
||||||
|
text: this.$t('symbols'),
|
||||||
|
icon: ['far', 'heart'],
|
||||||
|
isActive: false
|
||||||
|
}, {
|
||||||
|
name: 'flags',
|
||||||
|
ref: 'flagsSection',
|
||||||
|
text: this.$t('flags'),
|
||||||
|
icon: ['far', 'flag'],
|
||||||
|
isActive: false
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
this.customEmojis = (this.$root.getMetaSync() || { emojis: [] }).emojis || [];
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
go(ref) {
|
||||||
|
this.$refs.emojis.scrollTop = this.$refs[ref][0].offsetTop;
|
||||||
|
},
|
||||||
|
|
||||||
|
onScroll(e) {
|
||||||
|
const section = this.categories.forEach(x => {
|
||||||
|
const top = e.target.scrollTop;
|
||||||
|
const el = this.$refs[x.ref][0];
|
||||||
|
x.isActive = el.offsetTop <= top && el.offsetTop + el.offsetHeight > top;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
chosen(emoji) {
|
||||||
|
this.$emit('chosen', emoji);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.prlncendiewqqkrevzeruhndoakghvtx
|
||||||
|
width 350px
|
||||||
|
background var(--face)
|
||||||
|
|
||||||
|
> header
|
||||||
|
display flex
|
||||||
|
|
||||||
|
> button
|
||||||
|
flex 1
|
||||||
|
padding 10px 0
|
||||||
|
font-size 16px
|
||||||
|
color var(--text)
|
||||||
|
transition color 0.2s ease
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color var(--textHighlighted)
|
||||||
|
transition color 0s
|
||||||
|
|
||||||
|
&.active
|
||||||
|
color var(--primary)
|
||||||
|
transition color 0s
|
||||||
|
|
||||||
|
> .emojis
|
||||||
|
height 300px
|
||||||
|
overflow-y auto
|
||||||
|
overflow-x hidden
|
||||||
|
|
||||||
|
> section
|
||||||
|
> header
|
||||||
|
position sticky
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
z-index 1
|
||||||
|
padding 8px
|
||||||
|
background var(--faceHeader)
|
||||||
|
color var(--text)
|
||||||
|
font-size 12px
|
||||||
|
|
||||||
|
> div
|
||||||
|
display grid
|
||||||
|
grid-template-columns 1fr 1fr 1fr 1fr 1fr 1fr 1fr 1fr
|
||||||
|
gap 4px
|
||||||
|
padding 8px
|
||||||
|
|
||||||
|
> button
|
||||||
|
padding 0
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
&:before
|
||||||
|
content ''
|
||||||
|
display block
|
||||||
|
width 1px
|
||||||
|
height 0
|
||||||
|
padding-bottom 100%
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
> *
|
||||||
|
transform scale(1.2)
|
||||||
|
transition transform 0s
|
||||||
|
|
||||||
|
> *
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
width 100%
|
||||||
|
height 100%
|
||||||
|
font-size 28px
|
||||||
|
transition transform 0.2s ease
|
||||||
|
pointer-events none
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -22,7 +22,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
customEmojis: {
|
customEmojis: {
|
||||||
required: false,
|
required: false,
|
||||||
default: []
|
default: () => []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
184
src/client/app/common/views/components/follow-button.vue
Normal file
184
src/client/app/common/views/components/follow-button.vue
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
<template>
|
||||||
|
<button class="wfliddvnhxvyusikowhxozkyxyenqxqr"
|
||||||
|
:class="{ wait, block, mini, active: isFollowing || hasPendingFollowRequestFromYou }"
|
||||||
|
@click="onClick"
|
||||||
|
:disabled="wait"
|
||||||
|
>
|
||||||
|
<template v-if="!wait">
|
||||||
|
<fa :icon="iconAndText[0]"/> <template v-if="!mini">{{ iconAndText[1] }}</template>
|
||||||
|
</template>
|
||||||
|
<template v-else><fa icon="spinner" pulse fixed-width/></template>
|
||||||
|
</button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import i18n from '../../../i18n';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
i18n: i18n('common/views/components/follow-button.vue'),
|
||||||
|
|
||||||
|
props: {
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
block: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
mini: {
|
||||||
|
type: Boolean,
|
||||||
|
required: false,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
isFollowing: this.user.isFollowing,
|
||||||
|
hasPendingFollowRequestFromYou: this.user.hasPendingFollowRequestFromYou,
|
||||||
|
wait: false,
|
||||||
|
connection: null
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
iconAndText(): any[] {
|
||||||
|
return (
|
||||||
|
(this.hasPendingFollowRequestFromYou && this.user.isLocked) ? ['hourglass-half', this.$t('request-pending')] :
|
||||||
|
(this.hasPendingFollowRequestFromYou && !this.user.isLocked) ? ['hourglass-start', this.$t('follow-processing')] :
|
||||||
|
(this.isFollowing) ? ['minus', this.$t('following')] :
|
||||||
|
(!this.isFollowing && this.user.isLocked) ? ['plus', this.$t('follow-request')] :
|
||||||
|
(!this.isFollowing && !this.user.isLocked) ? ['plus', this.$t('follow')] :
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.connection = this.$root.stream.useSharedConnection('main');
|
||||||
|
|
||||||
|
this.connection.on('follow', this.onFollowChange);
|
||||||
|
this.connection.on('unfollow', this.onFollowChange);
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
this.connection.dispose();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onFollowChange(user) {
|
||||||
|
if (user.id == this.user.id) {
|
||||||
|
this.isFollowing = user.isFollowing;
|
||||||
|
this.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async onClick() {
|
||||||
|
this.wait = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (this.isFollowing) {
|
||||||
|
await this.$root.api('following/delete', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (this.hasPendingFollowRequestFromYou) {
|
||||||
|
await this.$root.api('following/requests/cancel', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
} else if (this.user.isLocked) {
|
||||||
|
await this.$root.api('following/create', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
this.hasPendingFollowRequestFromYou = true;
|
||||||
|
} else {
|
||||||
|
await this.$root.api('following/create', {
|
||||||
|
userId: this.user.id
|
||||||
|
});
|
||||||
|
this.hasPendingFollowRequestFromYou = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
} finally {
|
||||||
|
this.wait = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.wfliddvnhxvyusikowhxozkyxyenqxqr
|
||||||
|
display block
|
||||||
|
user-select none
|
||||||
|
cursor pointer
|
||||||
|
padding 0 16px
|
||||||
|
margin 0
|
||||||
|
min-width 100px
|
||||||
|
line-height 36px
|
||||||
|
font-size 14px
|
||||||
|
font-weight bold
|
||||||
|
color var(--primary)
|
||||||
|
background transparent
|
||||||
|
outline none
|
||||||
|
border solid 1px var(--primary)
|
||||||
|
border-radius 36px
|
||||||
|
|
||||||
|
&.mini
|
||||||
|
padding 0
|
||||||
|
min-width 0
|
||||||
|
width 32px
|
||||||
|
height 32px
|
||||||
|
font-size 16px
|
||||||
|
border-radius 4px
|
||||||
|
line-height 32px
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
&:after
|
||||||
|
border-radius 8px
|
||||||
|
|
||||||
|
&.block
|
||||||
|
width 100%
|
||||||
|
|
||||||
|
&:focus
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
|
pointer-events none
|
||||||
|
position absolute
|
||||||
|
top -5px
|
||||||
|
right -5px
|
||||||
|
bottom -5px
|
||||||
|
left -5px
|
||||||
|
border 2px solid var(--primaryAlpha03)
|
||||||
|
border-radius 36px
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background var(--primaryAlpha01)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background var(--primaryAlpha02)
|
||||||
|
|
||||||
|
&.active
|
||||||
|
color var(--primaryForeground)
|
||||||
|
background var(--primary)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background var(--primaryLighten10)
|
||||||
|
border-color var(--primaryLighten10)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
background var(--primaryDarken10)
|
||||||
|
border-color var(--primaryDarken10)
|
||||||
|
|
||||||
|
&.wait
|
||||||
|
cursor wait !important
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
|
*
|
||||||
|
pointer-events none
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
import followButton from './follow-button.vue';
|
||||||
import muteAndBlock from './mute-and-block.vue';
|
import muteAndBlock from './mute-and-block.vue';
|
||||||
import error from './error.vue';
|
import error from './error.vue';
|
||||||
import apiSettings from './api-settings.vue';
|
import apiSettings from './api-settings.vue';
|
||||||
@@ -51,6 +52,7 @@ import uiInfo from './ui/info.vue';
|
|||||||
import formButton from './ui/form/button.vue';
|
import formButton from './ui/form/button.vue';
|
||||||
import formRadio from './ui/form/radio.vue';
|
import formRadio from './ui/form/radio.vue';
|
||||||
|
|
||||||
|
Vue.component('mk-follow-button', followButton);
|
||||||
Vue.component('mk-mute-and-block', muteAndBlock);
|
Vue.component('mk-mute-and-block', muteAndBlock);
|
||||||
Vue.component('mk-error', error);
|
Vue.component('mk-error', error);
|
||||||
Vue.component('mk-api-settings', apiSettings);
|
Vue.component('mk-api-settings', apiSettings);
|
||||||
|
|||||||
@@ -5,7 +5,13 @@
|
|||||||
|
|
||||||
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>{{ $t('@.loading') }}<mk-ellipsis/></p>
|
||||||
<div :class="$style.stream" v-if="!fetching && images.length > 0">
|
<div :class="$style.stream" v-if="!fetching && images.length > 0">
|
||||||
<div v-for="image in images" :class="$style.img" :style="`background-image: url(${image.thumbnailUrl || image.url})`"></div>
|
<div v-for="image in images"
|
||||||
|
:class="$style.img"
|
||||||
|
:style="`background-image: url(${image.thumbnailUrl || image.url})`"
|
||||||
|
draggable="true"
|
||||||
|
@dragstart="onDragstart(image, $event)"
|
||||||
|
@dragend="onDragend"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<p :class="$style.empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
|
<p :class="$style.empty" v-if="!fetching && images.length == 0">{{ $t('no-photos') }}</p>
|
||||||
</mk-widget-container>
|
</mk-widget-container>
|
||||||
@@ -31,6 +37,7 @@ export default define({
|
|||||||
connection: null
|
connection: null
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
mounted() {
|
||||||
this.connection = this.$root.stream.useSharedConnection('main');
|
this.connection = this.$root.stream.useSharedConnection('main');
|
||||||
|
|
||||||
@@ -44,9 +51,11 @@ export default define({
|
|||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.connection.dispose();
|
this.connection.dispose();
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onDriveFileCreated(file) {
|
onDriveFileCreated(file) {
|
||||||
if (/^image\/.+$/.test(file.type)) {
|
if (/^image\/.+$/.test(file.type)) {
|
||||||
@@ -54,6 +63,7 @@ export default define({
|
|||||||
if (this.images.length > 9) this.images.pop();
|
if (this.images.length > 9) this.images.pop();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
if (this.props.design == 2) {
|
if (this.props.design == 2) {
|
||||||
this.props.design = 0;
|
this.props.design = 0;
|
||||||
@@ -62,7 +72,16 @@ export default define({
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
}
|
},
|
||||||
|
|
||||||
|
onDragstart(file, e) {
|
||||||
|
e.dataTransfer.effectAllowed = 'move';
|
||||||
|
e.dataTransfer.setData('mk_drive_file', JSON.stringify(file));
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragend(e) {
|
||||||
|
this.browser.isDragSource = false;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
<template>
|
||||||
|
<div class="gcafiosrssbtbnbzqupfmglvzgiaipyv">
|
||||||
|
<x-picker @chosen="chosen"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import contains from '../../../common/scripts/contains';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XPicker: () => import('../../../common/views/components/emoji-picker.vue').then(m => m.default)
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
x: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
const width = this.$el.offsetWidth;
|
||||||
|
const height = this.$el.offsetHeight;
|
||||||
|
|
||||||
|
let x = this.x;
|
||||||
|
let y = this.y;
|
||||||
|
|
||||||
|
if (x + width - window.pageXOffset > window.innerWidth) {
|
||||||
|
x = window.innerWidth - width + window.pageXOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (y + height - window.pageYOffset > window.innerHeight) {
|
||||||
|
y = window.innerHeight - height + window.pageYOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$el.style.left = x + 'px';
|
||||||
|
this.$el.style.top = y + 'px';
|
||||||
|
|
||||||
|
Array.from(document.querySelectorAll('body *')).forEach(el => {
|
||||||
|
el.addEventListener('mousedown', this.onMousedown);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
onMousedown(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (!contains(this.$el, e.target) && (this.$el != e.target)) this.close();
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
chosen(emoji) {
|
||||||
|
this.$emit('chosen', emoji);
|
||||||
|
this.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
Array.from(document.querySelectorAll('body *')).forEach(el => {
|
||||||
|
el.removeEventListener('mousedown', this.onMousedown);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.$emit('closed');
|
||||||
|
this.destroyDom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.gcafiosrssbtbnbzqupfmglvzgiaipyv
|
||||||
|
position fixed
|
||||||
|
top 0
|
||||||
|
left 0
|
||||||
|
z-index 3000
|
||||||
|
box-shadow 0 2px 12px 0 rgba(0, 0, 0, 0.3)
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
<template>
|
|
||||||
<button class="mk-follow-button"
|
|
||||||
:class="{ wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou, big: size == 'big' }"
|
|
||||||
@click="onClick"
|
|
||||||
:disabled="wait"
|
|
||||||
>
|
|
||||||
<template v-if="!wait">
|
|
||||||
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/><template v-if="size == 'big'"> {{ $t('request-pending') }}</template></template>
|
|
||||||
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/><template v-if="size == 'big'"> {{ $t('follow-processing') }}</template></template>
|
|
||||||
<template v-else-if="u.isFollowing"><fa icon="minus"/><template v-if="size == 'big'"> {{ $t('following') }}</template></template>
|
|
||||||
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> {{ $t('follow-request') }}</template></template>
|
|
||||||
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> {{ $t('follow') }}</template></template>
|
|
||||||
</template>
|
|
||||||
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
import i18n from '../../../i18n';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
i18n: i18n('desktop/views/components/follow-button.vue'),
|
|
||||||
props: {
|
|
||||||
user: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
size: {
|
|
||||||
type: String,
|
|
||||||
default: 'compact'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
u: this.user,
|
|
||||||
wait: false,
|
|
||||||
connection: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.connection = this.$root.stream.useSharedConnection('main');
|
|
||||||
this.connection.on('follow', this.onFollowChange);
|
|
||||||
this.connection.on('unfollow', this.onFollowChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.connection.dispose();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onFollowChange(user) {
|
|
||||||
if (user.id == this.u.id) {
|
|
||||||
this.u.isFollowing = user.isFollowing;
|
|
||||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
|
||||||
this.$forceUpdate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async onClick() {
|
|
||||||
this.wait = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.u.isFollowing) {
|
|
||||||
this.u = await this.$root.api('following/delete', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (this.u.hasPendingFollowRequestFromYou) {
|
|
||||||
this.u = await this.$root.api('following/requests/cancel', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else if (this.u.isLocked) {
|
|
||||||
this.u = await this.$root.api('following/create', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.u = await this.$root.api('following/create', {
|
|
||||||
userId: this.user.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
} finally {
|
|
||||||
this.wait = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.mk-follow-button
|
|
||||||
display block
|
|
||||||
cursor pointer
|
|
||||||
padding 0
|
|
||||||
margin 0
|
|
||||||
width 32px
|
|
||||||
height 32px
|
|
||||||
font-size 1em
|
|
||||||
outline none
|
|
||||||
border-radius 4px
|
|
||||||
|
|
||||||
*
|
|
||||||
pointer-events none
|
|
||||||
|
|
||||||
&:focus
|
|
||||||
&:after
|
|
||||||
content ""
|
|
||||||
pointer-events none
|
|
||||||
position absolute
|
|
||||||
top -5px
|
|
||||||
right -5px
|
|
||||||
bottom -5px
|
|
||||||
left -5px
|
|
||||||
border 2px solid var(--primaryAlpha03)
|
|
||||||
border-radius 8px
|
|
||||||
|
|
||||||
&:not(.active)
|
|
||||||
color var(--primary)
|
|
||||||
border solid 1px var(--primary)
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background var(--primaryAlpha03)
|
|
||||||
|
|
||||||
&:active
|
|
||||||
background var(--primaryAlpha05)
|
|
||||||
|
|
||||||
&.active
|
|
||||||
color var(--primaryForeground)
|
|
||||||
background var(--primary)
|
|
||||||
border solid 1px var(--primary)
|
|
||||||
|
|
||||||
&:not(:disabled)
|
|
||||||
font-weight bold
|
|
||||||
|
|
||||||
&:hover:not(:disabled)
|
|
||||||
background var(--primaryLighten5)
|
|
||||||
border-color var(--primaryLighten5)
|
|
||||||
|
|
||||||
&:active:not(:disabled)
|
|
||||||
background var(--primaryDarken5)
|
|
||||||
border-color var(--primaryDarken5)
|
|
||||||
|
|
||||||
&.wait
|
|
||||||
cursor wait !important
|
|
||||||
opacity 0.7
|
|
||||||
|
|
||||||
&.big
|
|
||||||
width 100%
|
|
||||||
height 38px
|
|
||||||
line-height 38px
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -14,7 +14,6 @@ import mediaVideo from './media-video.vue';
|
|||||||
import notifications from './notifications.vue';
|
import notifications from './notifications.vue';
|
||||||
import noteForm from './post-form.vue';
|
import noteForm from './post-form.vue';
|
||||||
import renoteForm from './renote-form.vue';
|
import renoteForm from './renote-form.vue';
|
||||||
import followButton from './follow-button.vue';
|
|
||||||
import notePreview from './note-preview.vue';
|
import notePreview from './note-preview.vue';
|
||||||
import noteDetail from './note-detail.vue';
|
import noteDetail from './note-detail.vue';
|
||||||
import settings from './settings.vue';
|
import settings from './settings.vue';
|
||||||
@@ -39,7 +38,6 @@ Vue.component('mk-media-video', mediaVideo);
|
|||||||
Vue.component('mk-notifications', notifications);
|
Vue.component('mk-notifications', notifications);
|
||||||
Vue.component('mk-post-form', noteForm);
|
Vue.component('mk-post-form', noteForm);
|
||||||
Vue.component('mk-renote-form', renoteForm);
|
Vue.component('mk-renote-form', renoteForm);
|
||||||
Vue.component('mk-follow-button', followButton);
|
|
||||||
Vue.component('mk-note-preview', notePreview);
|
Vue.component('mk-note-preview', notePreview);
|
||||||
Vue.component('mk-note-detail', noteDetail);
|
Vue.component('mk-note-detail', noteDetail);
|
||||||
Vue.component('mk-settings', settings);
|
Vue.component('mk-settings', settings);
|
||||||
|
|||||||
@@ -15,11 +15,15 @@
|
|||||||
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
|
<a v-for="tag in recentHashtags.slice(0, 5)" @click="addTag(tag)" :title="$t('click-to-tagging')">#{{ tag }}</a>
|
||||||
</div>
|
</div>
|
||||||
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
|
<input v-show="useCw" v-model="cw" :placeholder="$t('annotations')">
|
||||||
|
<div class="textarea">
|
||||||
<textarea :class="{ with: (files.length != 0 || poll) }"
|
<textarea :class="{ with: (files.length != 0 || poll) }"
|
||||||
ref="text" v-model="text" :disabled="posting"
|
ref="text" v-model="text" :disabled="posting"
|
||||||
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
|
@keydown="onKeydown" @paste="onPaste" :placeholder="placeholder"
|
||||||
v-autocomplete="'text'"
|
v-autocomplete="'text'"
|
||||||
></textarea>
|
></textarea>
|
||||||
|
<button class="emoji" @click="emoji" ref="emoji">
|
||||||
|
<fa :icon="['far', 'laugh']"/>
|
||||||
|
</button>
|
||||||
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
|
<div class="files" :class="{ with: poll }" v-show="files.length != 0">
|
||||||
<x-draggable :list="files" :options="{ animation: 150 }">
|
<x-draggable :list="files" :options="{ animation: 150 }">
|
||||||
<div v-for="file in files" :key="file.id">
|
<div v-for="file in files" :key="file.id">
|
||||||
@@ -31,6 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
|
<mk-poll-editor v-if="poll" ref="poll" @destroyed="poll = false" @updated="saveDraft()"/>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
|
<mk-uploader ref="uploader" @uploaded="attachMedia" @change="onChangeUploadings"/>
|
||||||
<button class="upload" :title="$t('attach-media-from-local')" @click="chooseFile"><fa icon="upload"/></button>
|
<button class="upload" :title="$t('attach-media-from-local')" @click="chooseFile"><fa icon="upload"/></button>
|
||||||
<button class="drive" :title="$t('attach-media-from-drive')" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
<button class="drive" :title="$t('attach-media-from-drive')" @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
||||||
@@ -377,6 +382,19 @@ export default Vue.extend({
|
|||||||
this.visibleUsers = erase(user, this.visibleUsers);
|
this.visibleUsers = erase(user, this.visibleUsers);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async emoji() {
|
||||||
|
const Picker = await import('./emoji-picker-dialog.vue').then(m => m.default);
|
||||||
|
const button = this.$refs.emoji;
|
||||||
|
const rect = button.getBoundingClientRect();
|
||||||
|
const vm = this.$root.new(Picker, {
|
||||||
|
x: button.offsetWidth + rect.left + window.pageXOffset,
|
||||||
|
y: rect.top + window.pageYOffset
|
||||||
|
});
|
||||||
|
vm.$once('chosen', emoji => {
|
||||||
|
insertTextAtCursor(this.$refs.text, emoji);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
post() {
|
post() {
|
||||||
this.posting = true;
|
this.posting = true;
|
||||||
|
|
||||||
@@ -469,7 +487,7 @@ export default Vue.extend({
|
|||||||
|
|
||||||
> .content
|
> .content
|
||||||
> input
|
> input
|
||||||
> textarea
|
> .textarea > textarea
|
||||||
display block
|
display block
|
||||||
width 100%
|
width 100%
|
||||||
padding 12px
|
padding 12px
|
||||||
@@ -498,6 +516,24 @@ export default Vue.extend({
|
|||||||
> input
|
> input
|
||||||
margin-bottom 8px
|
margin-bottom 8px
|
||||||
|
|
||||||
|
> .textarea
|
||||||
|
> .emoji
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
padding 10px
|
||||||
|
font-size 18px
|
||||||
|
color var(--text)
|
||||||
|
opacity 0.5
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color var(--textHighlighted)
|
||||||
|
opacity 1
|
||||||
|
|
||||||
|
&:active
|
||||||
|
color var(--primary)
|
||||||
|
opacity 1
|
||||||
|
|
||||||
> textarea
|
> textarea
|
||||||
margin 0
|
margin 0
|
||||||
max-width 100%
|
max-width 100%
|
||||||
@@ -505,42 +541,24 @@ export default Vue.extend({
|
|||||||
min-height 84px
|
min-height 84px
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
& + *
|
|
||||||
& + * + *
|
& + * + *
|
||||||
|
& + * + * + *
|
||||||
border-color var(--primaryAlpha02)
|
border-color var(--primaryAlpha02)
|
||||||
transition border-color .1s ease
|
transition border-color .1s ease
|
||||||
|
|
||||||
&:focus
|
&:focus
|
||||||
& + *
|
|
||||||
& + * + *
|
& + * + *
|
||||||
|
& + * + * + *
|
||||||
border-color var(--primaryAlpha05)
|
border-color var(--primaryAlpha05)
|
||||||
transition border-color 0s ease
|
transition border-color 0s ease
|
||||||
|
|
||||||
|
& + .emoji
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
&.with
|
&.with
|
||||||
border-bottom solid 1px var(--primaryAlpha01) !important
|
border-bottom solid 1px var(--primaryAlpha01) !important
|
||||||
border-radius 4px 4px 0 0
|
border-radius 4px 4px 0 0
|
||||||
|
|
||||||
> .visibleUsers
|
|
||||||
margin-bottom 8px
|
|
||||||
font-size 14px
|
|
||||||
|
|
||||||
> span
|
|
||||||
margin-right 16px
|
|
||||||
color var(--primary)
|
|
||||||
|
|
||||||
> .hashtags
|
|
||||||
margin 0 0 8px 0
|
|
||||||
overflow hidden
|
|
||||||
white-space nowrap
|
|
||||||
font-size 14px
|
|
||||||
|
|
||||||
> b
|
|
||||||
color var(--primary)
|
|
||||||
|
|
||||||
> *
|
|
||||||
margin-right 8px
|
|
||||||
white-space nowrap
|
|
||||||
|
|
||||||
> .files
|
> .files
|
||||||
margin 0
|
margin 0
|
||||||
padding 0
|
padding 0
|
||||||
@@ -601,6 +619,27 @@ export default Vue.extend({
|
|||||||
border-radius 0 0 4px 4px
|
border-radius 0 0 4px 4px
|
||||||
transition border-color .3s ease
|
transition border-color .3s ease
|
||||||
|
|
||||||
|
> .visibleUsers
|
||||||
|
margin-bottom 8px
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
|
> span
|
||||||
|
margin-right 16px
|
||||||
|
color var(--primary)
|
||||||
|
|
||||||
|
> .hashtags
|
||||||
|
margin 0 0 8px 0
|
||||||
|
overflow hidden
|
||||||
|
white-space nowrap
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
|
> b
|
||||||
|
color var(--primary)
|
||||||
|
|
||||||
|
> *
|
||||||
|
margin-right 8px
|
||||||
|
white-space nowrap
|
||||||
|
|
||||||
> .mk-uploader
|
> .mk-uploader
|
||||||
margin 8px 0 0 0
|
margin 8px 0 0 0
|
||||||
padding 8px
|
padding 8px
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
|
<div class="zvdbznxvfixtmujpsigoccczftvpiwqh">
|
||||||
<div class="banner" :style="bannerStyle"></div>
|
<div class="banner" :style="bannerStyle"></div>
|
||||||
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
||||||
<mk-follow-button :user="user" class="follow"/>
|
<mk-follow-button :user="user" class="follow" mini/>
|
||||||
<div class="body">
|
<div class="body">
|
||||||
<router-link :to="user | userPage" class="name">{{ user | userName }}</router-link>
|
<router-link :to="user | userPage" class="name">{{ user | userName }}</router-link>
|
||||||
<span class="username">@{{ user | acct }}</span>
|
<span class="username">@{{ user | acct }}</span>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
<p>{{ $t('followers') }}</p><span>{{ u.followersCount }}</span>
|
<p>{{ $t('followers') }}</p><span>{{ u.followersCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<mk-follow-button v-if="$store.getters.isSignedIn && u.id != $store.state.i.id" :user="u"/>
|
<mk-follow-button class="follow-button" v-if="$store.getters.isSignedIn && u.id != $store.state.i.id" :user="u" mini/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -154,7 +154,7 @@ export default Vue.extend({
|
|||||||
font-size 1em
|
font-size 1em
|
||||||
color var(--primary)
|
color var(--primary)
|
||||||
|
|
||||||
> .mk-follow-button
|
> .follow-button
|
||||||
position absolute
|
position absolute
|
||||||
top 92px
|
top 92px
|
||||||
right 8px
|
right 8px
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<header :style="bannerStyle">
|
<header :style="bannerStyle">
|
||||||
<div>
|
<div>
|
||||||
<button class="menu" @click="menu" ref="menu"><fa icon="ellipsis-h"/></button>
|
<button class="menu" @click="menu" ref="menu"><fa icon="ellipsis-h"/></button>
|
||||||
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow"/>
|
<mk-follow-button v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" class="follow" mini/>
|
||||||
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
<mk-avatar class="avatar" :user="user" :disable-preview="true"/>
|
||||||
<span class="name">{{ user | userName }}</span>
|
<span class="name">{{ user | userName }}</span>
|
||||||
<span class="acct">@{{ user | acct }}</span>
|
<span class="acct">@{{ user | acct }}</span>
|
||||||
@@ -155,7 +155,8 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
fileType: image,
|
fileType: image,
|
||||||
limit: 9
|
limit: 9,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
notes.forEach(note => {
|
notes.forEach(note => {
|
||||||
note.files.forEach(file => {
|
note.files.forEach(file => {
|
||||||
@@ -254,6 +255,7 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365,
|
||||||
withFiles: this.withFiles,
|
withFiles: this.withFiles,
|
||||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||||
@@ -274,7 +276,7 @@ export default Vue.extend({
|
|||||||
const promise = this.$root.api('users/notes', {
|
const promise = this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
untilId: (this.$refs.timeline as any).tail().id,
|
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime(),
|
||||||
withFiles: this.withFiles,
|
withFiles: this.withFiles,
|
||||||
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
includeMyRenotes: this.$store.state.settings.showMyRenotes,
|
||||||
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
includeRenotedMyNotes: this.$store.state.settings.showRenotedMyNotes,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
<router-link class="name" :to="friend | userPage" v-user-preview="friend.id">{{ friend.name }}</router-link>
|
<router-link class="name" :to="friend | userPage" v-user-preview="friend.id">{{ friend.name }}</router-link>
|
||||||
<p class="username">@{{ friend | acct }}</p>
|
<p class="username">@{{ friend | acct }}</p>
|
||||||
</div>
|
</div>
|
||||||
<mk-follow-button :user="friend"/>
|
<mk-follow-button class="follow-button" :user="friend"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('no-users') }}</p>
|
<p class="empty" v-if="!fetching && users.length == 0">{{ $t('no-users') }}</p>
|
||||||
@@ -110,7 +110,7 @@ export default Vue.extend({
|
|||||||
color var(--text)
|
color var(--text)
|
||||||
opacity 0.7
|
opacity 0.7
|
||||||
|
|
||||||
> .mk-follow-button
|
> .follow-button
|
||||||
position absolute
|
position absolute
|
||||||
top 16px
|
top 16px
|
||||||
right 16px
|
right 16px
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withFiles: true,
|
withFiles: true,
|
||||||
limit: 9
|
limit: 9,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
notes.forEach(note => {
|
notes.forEach(note => {
|
||||||
note.files.forEach(file => {
|
note.files.forEach(file => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="profile" v-if="$store.getters.isSignedIn">
|
<div class="profile" v-if="$store.getters.isSignedIn">
|
||||||
<div class="friend-form" v-if="$store.state.i.id != user.id">
|
<div class="friend-form" v-if="$store.state.i.id != user.id">
|
||||||
<mk-follow-button :user="user" size="big"/>
|
<mk-follow-button :user="user" block/>
|
||||||
<p class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</p>
|
<p class="followed" v-if="user.isFollowed">{{ $t('follows-you') }}</p>
|
||||||
<p class="stalk" v-if="user.isFollowing">
|
<p class="stalk" v-if="user.isFollowing">
|
||||||
<span v-if="user.isStalking">{{ $t('stalking') }} <a @click="unstalk"><fa icon="meh"/> {{ $t('unstalk') }}</a></span>
|
<span v-if="user.isStalking">{{ $t('stalking') }} <a @click="unstalk"><fa icon="meh"/> {{ $t('unstalk') }}</a></span>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
untilDate: this.date ? this.date.getTime() : undefined,
|
untilDate: this.date ? this.date.getTime() : new Date().getTime() + 1000 * 86400 * 365,
|
||||||
includeReplies: this.mode == 'with-replies',
|
includeReplies: this.mode == 'with-replies',
|
||||||
withFiles: this.mode == 'with-media'
|
withFiles: this.mode == 'with-media'
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
@@ -86,7 +86,7 @@ export default Vue.extend({
|
|||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
includeReplies: this.mode == 'with-replies',
|
includeReplies: this.mode == 'with-replies',
|
||||||
withFiles: this.mode == 'with-media',
|
withFiles: this.mode == 'with-media',
|
||||||
untilId: (this.$refs.timeline as any).tail().id
|
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime()
|
||||||
});
|
});
|
||||||
|
|
||||||
promise.then(notes => {
|
promise.then(notes => {
|
||||||
|
|||||||
@@ -1,16 +1,51 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="mkw-post-form">
|
<div>
|
||||||
<template v-if="props.design == 0">
|
<mk-widget-container :show-header="props.design == 0">
|
||||||
<p class="title"><fa icon="pencil-alt"/>{{ $t('title') }}</p>
|
<template slot="header"><fa icon="pencil-alt"/>{{ $t('title') }}</template>
|
||||||
</template>
|
|
||||||
<textarea :disabled="posting" v-model="text" @keydown="onKeydown" :placeholder="placeholder"></textarea>
|
<div class="lhcuptdmcdkfwmipgazeawoiuxpzaclc-body"
|
||||||
<button @click="post" :disabled="posting">{{ $t('note') }}</button>
|
@dragover.stop="onDragover"
|
||||||
|
@drop.stop="onDrop"
|
||||||
|
>
|
||||||
|
<div class="textarea">
|
||||||
|
<textarea
|
||||||
|
:disabled="posting"
|
||||||
|
v-model="text"
|
||||||
|
@keydown="onKeydown"
|
||||||
|
@paste="onPaste"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
ref="text"
|
||||||
|
v-autocomplete="'text'"
|
||||||
|
></textarea>
|
||||||
|
<button class="emoji" @click="emoji" ref="emoji">
|
||||||
|
<fa :icon="['far', 'laugh']"/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="files" v-show="files.length != 0">
|
||||||
|
<x-draggable :list="files" :options="{ animation: 150 }">
|
||||||
|
<div v-for="file in files" :key="file.id">
|
||||||
|
<div class="img" :style="{ backgroundImage: `url(${file.thumbnailUrl})` }" :title="file.name"></div>
|
||||||
|
<img class="remove" @click="detachMedia(file.id)" src="/assets/desktop/remove.png" :title="$t('attach-cancel')" alt=""/>
|
||||||
|
</div>
|
||||||
|
</x-draggable>
|
||||||
|
</div>
|
||||||
|
<input ref="file" type="file" multiple="multiple" tabindex="-1" @change="onChangeFile"/>
|
||||||
|
<mk-uploader ref="uploader" @uploaded="attachMedia"/>
|
||||||
|
<footer>
|
||||||
|
<button @click="chooseFile"><fa icon="upload"/></button>
|
||||||
|
<button @click="chooseFileFromDrive"><fa icon="cloud"/></button>
|
||||||
|
<button @click="post" :disabled="posting" class="post">{{ $t('note') }}</button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</mk-widget-container>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import define from '../../../common/define-widget';
|
import define from '../../../common/define-widget';
|
||||||
import i18n from '../../../i18n';
|
import i18n from '../../../i18n';
|
||||||
|
import insertTextAtCursor from 'insert-text-at-cursor';
|
||||||
|
import * as XDraggable from 'vuedraggable';
|
||||||
|
|
||||||
export default define({
|
export default define({
|
||||||
name: 'post-form',
|
name: 'post-form',
|
||||||
@@ -19,12 +54,19 @@ export default define({
|
|||||||
})
|
})
|
||||||
}).extend({
|
}).extend({
|
||||||
i18n: i18n('desktop/views/widgets/post-form.vue'),
|
i18n: i18n('desktop/views/widgets/post-form.vue'),
|
||||||
|
|
||||||
|
components: {
|
||||||
|
XDraggable
|
||||||
|
},
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
posting: false,
|
posting: false,
|
||||||
text: ''
|
text: '',
|
||||||
|
files: [],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
placeholder(): string {
|
placeholder(): string {
|
||||||
const xs = [
|
const xs = [
|
||||||
@@ -38,6 +80,7 @@ export default define({
|
|||||||
return xs[Math.floor(Math.random() * xs.length)];
|
return xs[Math.floor(Math.random() * xs.length)];
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
func() {
|
func() {
|
||||||
if (this.props.design == 1) {
|
if (this.props.design == 1) {
|
||||||
@@ -47,14 +90,95 @@ export default define({
|
|||||||
}
|
}
|
||||||
this.save();
|
this.save();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
chooseFile() {
|
||||||
|
(this.$refs.file as any).click();
|
||||||
|
},
|
||||||
|
|
||||||
|
chooseFileFromDrive() {
|
||||||
|
this.$chooseDriveFile({
|
||||||
|
multiple: true
|
||||||
|
}).then(files => {
|
||||||
|
files.forEach(this.attachMedia);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
attachMedia(driveFile) {
|
||||||
|
this.files.push(driveFile);
|
||||||
|
this.$emit('change-attached-files', this.files);
|
||||||
|
},
|
||||||
|
|
||||||
|
detachMedia(id) {
|
||||||
|
this.files = this.files.filter(x => x.id != id);
|
||||||
|
this.$emit('change-attached-files', this.files);
|
||||||
|
},
|
||||||
|
|
||||||
onKeydown(e) {
|
onKeydown(e) {
|
||||||
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && !this.posting && this.text) this.post();
|
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && !this.posting && this.text) this.post();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onPaste(e) {
|
||||||
|
Array.from(e.clipboardData.items).forEach((item: any) => {
|
||||||
|
if (item.kind == 'file') {
|
||||||
|
this.upload(item.getAsFile());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
onChangeFile() {
|
||||||
|
Array.from((this.$refs.file as any).files).forEach(this.upload);
|
||||||
|
},
|
||||||
|
|
||||||
|
upload(file) {
|
||||||
|
(this.$refs.uploader as any).upload(file);
|
||||||
|
},
|
||||||
|
|
||||||
|
onDragover(e) {
|
||||||
|
const isFile = e.dataTransfer.items[0].kind == 'file';
|
||||||
|
const isDriveFile = e.dataTransfer.types[0] == 'mk_drive_file';
|
||||||
|
if (isFile || isDriveFile) {
|
||||||
|
e.preventDefault();
|
||||||
|
e.dataTransfer.dropEffect = e.dataTransfer.effectAllowed == 'all' ? 'copy' : 'move';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onDrop(e): void {
|
||||||
|
// ファイルだったら
|
||||||
|
if (e.dataTransfer.files.length > 0) {
|
||||||
|
e.preventDefault();
|
||||||
|
Array.from(e.dataTransfer.files).forEach(this.upload);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#region ドライブのファイル
|
||||||
|
const driveFile = e.dataTransfer.getData('mk_drive_file');
|
||||||
|
if (driveFile != null && driveFile != '') {
|
||||||
|
const file = JSON.parse(driveFile);
|
||||||
|
this.files.push(file);
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
//#endregion
|
||||||
|
},
|
||||||
|
|
||||||
|
async emoji() {
|
||||||
|
const Picker = await import('../components/emoji-picker-dialog.vue').then(m => m.default);
|
||||||
|
const button = this.$refs.emoji;
|
||||||
|
const rect = button.getBoundingClientRect();
|
||||||
|
const vm = this.$root.new(Picker, {
|
||||||
|
x: button.offsetWidth + rect.left + window.pageXOffset,
|
||||||
|
y: rect.top + window.pageYOffset
|
||||||
|
});
|
||||||
|
vm.$once('chosen', emoji => {
|
||||||
|
insertTextAtCursor(this.$refs.text, emoji);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
post() {
|
post() {
|
||||||
this.posting = true;
|
this.posting = true;
|
||||||
|
|
||||||
this.$root.api('notes/create', {
|
this.$root.api('notes/create', {
|
||||||
text: this.text
|
text: this.text == '' ? undefined : this.text,
|
||||||
|
fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
||||||
}).then(data => {
|
}).then(data => {
|
||||||
this.clear();
|
this.clear();
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
@@ -63,34 +187,34 @@ export default define({
|
|||||||
this.posting = false;
|
this.posting = false;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
clear() {
|
clear() {
|
||||||
this.text = '';
|
this.text = '';
|
||||||
|
this.files = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
<style lang="stylus" scoped>
|
||||||
|
.lhcuptdmcdkfwmipgazeawoiuxpzaclc-body
|
||||||
|
> .textarea
|
||||||
|
> .emoji
|
||||||
|
position absolute
|
||||||
|
top 0
|
||||||
|
right 0
|
||||||
|
padding 10px
|
||||||
|
font-size 18px
|
||||||
|
color var(--text)
|
||||||
|
opacity 0.5
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color var(--textHighlighted)
|
||||||
|
opacity 1
|
||||||
|
|
||||||
.mkw-post-form
|
&:active
|
||||||
background #fff
|
color var(--primary)
|
||||||
overflow hidden
|
opacity 1
|
||||||
border solid 1px rgba(#000, 0.075)
|
|
||||||
border-radius 6px
|
|
||||||
|
|
||||||
> .title
|
|
||||||
z-index 1
|
|
||||||
margin 0
|
|
||||||
padding 0 16px
|
|
||||||
line-height 42px
|
|
||||||
font-size 0.9em
|
|
||||||
font-weight bold
|
|
||||||
color #888
|
|
||||||
box-shadow 0 1px rgba(#000, 0.07)
|
|
||||||
|
|
||||||
> [data-icon]
|
|
||||||
margin-right 4px
|
|
||||||
|
|
||||||
> textarea
|
> textarea
|
||||||
display block
|
display block
|
||||||
@@ -98,16 +222,64 @@ export default define({
|
|||||||
max-width 100%
|
max-width 100%
|
||||||
min-width 100%
|
min-width 100%
|
||||||
padding 16px
|
padding 16px
|
||||||
margin-bottom 28px + 16px
|
color var(--desktopPostFormTextareaFg)
|
||||||
|
outline none
|
||||||
|
background var(--desktopPostFormTextareaBg)
|
||||||
border none
|
border none
|
||||||
border-bottom solid 1px #eee
|
border-bottom solid 1px var(--faceDivider)
|
||||||
|
|
||||||
> button
|
&:focus
|
||||||
|
& + .emoji
|
||||||
|
opacity 0.7
|
||||||
|
|
||||||
|
> .files
|
||||||
|
> div
|
||||||
|
padding 4px
|
||||||
|
|
||||||
|
&:after
|
||||||
|
content ""
|
||||||
display block
|
display block
|
||||||
|
clear both
|
||||||
|
|
||||||
|
> div
|
||||||
|
float left
|
||||||
|
border solid 4px transparent
|
||||||
|
cursor move
|
||||||
|
|
||||||
|
&:hover > .remove
|
||||||
|
display block
|
||||||
|
|
||||||
|
> .img
|
||||||
|
width 64px
|
||||||
|
height 64px
|
||||||
|
background-size cover
|
||||||
|
background-position center center
|
||||||
|
|
||||||
|
> .remove
|
||||||
|
display none
|
||||||
position absolute
|
position absolute
|
||||||
bottom 8px
|
top -6px
|
||||||
right 8px
|
right -6px
|
||||||
margin 0
|
width 16px
|
||||||
|
height 16px
|
||||||
|
cursor pointer
|
||||||
|
|
||||||
|
> input[type=file]
|
||||||
|
display none
|
||||||
|
|
||||||
|
> footer
|
||||||
|
display flex
|
||||||
|
padding 8px
|
||||||
|
|
||||||
|
> button:not(.post)
|
||||||
|
color var(--text)
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color var(--textHighlighted)
|
||||||
|
|
||||||
|
> .post
|
||||||
|
display block
|
||||||
|
margin 0 0 0 auto
|
||||||
padding 0 10px
|
padding 0 10px
|
||||||
height 28px
|
height 28px
|
||||||
color var(--primaryForeground)
|
color var(--primaryForeground)
|
||||||
|
|||||||
@@ -114,11 +114,6 @@ export default define({
|
|||||||
color var(--text)
|
color var(--text)
|
||||||
opacity 0.7
|
opacity 0.7
|
||||||
|
|
||||||
> .mk-follow-button
|
|
||||||
position absolute
|
|
||||||
top 16px
|
|
||||||
right 16px
|
|
||||||
|
|
||||||
> .empty
|
> .empty
|
||||||
margin 0
|
margin 0
|
||||||
padding 16px
|
padding 16px
|
||||||
|
|||||||
@@ -1,134 +0,0 @@
|
|||||||
<template>
|
|
||||||
<button class="mk-follow-button"
|
|
||||||
:class="{ wait: wait, active: u.isFollowing || u.hasPendingFollowRequestFromYou }"
|
|
||||||
@click="onClick"
|
|
||||||
:disabled="wait"
|
|
||||||
>
|
|
||||||
<template v-if="!wait">
|
|
||||||
<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/> {{ $t('request-pending') }}</template>
|
|
||||||
<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/> {{ $t('follow-processing') }}</template>
|
|
||||||
<template v-else-if="u.isFollowing"><fa icon="minus"/> {{ $t('following') }}</template>
|
|
||||||
<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/> {{ $t('follow-request') }}</template>
|
|
||||||
<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/> {{ $t('follow') }}</template>
|
|
||||||
</template>
|
|
||||||
<template v-else><fa icon="spinner .pulse" fixed-width/></template>
|
|
||||||
</button>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
import i18n from '../../../i18n';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
i18n: i18n('mobile/views/components/follow-button.vue'),
|
|
||||||
props: {
|
|
||||||
user: {
|
|
||||||
type: Object,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
u: this.user,
|
|
||||||
wait: false,
|
|
||||||
connection: null
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
mounted() {
|
|
||||||
this.connection = this.$root.stream.useSharedConnection('main');
|
|
||||||
|
|
||||||
this.connection.on('follow', this.onFollowChange);
|
|
||||||
this.connection.on('unfollow', this.onFollowChange);
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeDestroy() {
|
|
||||||
this.connection.dispose();
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
onFollowChange(user) {
|
|
||||||
if (user.id == this.u.id) {
|
|
||||||
this.u.isFollowing = user.isFollowing;
|
|
||||||
this.u.hasPendingFollowRequestFromYou = user.hasPendingFollowRequestFromYou;
|
|
||||||
this.$forceUpdate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
async onClick() {
|
|
||||||
this.wait = true;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (this.u.isFollowing) {
|
|
||||||
this.u = await this.$root.api('following/delete', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
if (this.u.hasPendingFollowRequestFromYou) {
|
|
||||||
this.u = await this.$root.api('following/requests/cancel', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else if (this.u.isLocked) {
|
|
||||||
this.u = await this.$root.api('following/create', {
|
|
||||||
userId: this.u.id
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this.u = await this.$root.api('following/create', {
|
|
||||||
userId: this.user.id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e);
|
|
||||||
} finally {
|
|
||||||
this.wait = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.mk-follow-button
|
|
||||||
display block
|
|
||||||
user-select none
|
|
||||||
cursor pointer
|
|
||||||
padding 0 16px
|
|
||||||
margin 0
|
|
||||||
min-width 100px
|
|
||||||
line-height 36px
|
|
||||||
font-size 14px
|
|
||||||
font-weight bold
|
|
||||||
color var(--primary)
|
|
||||||
background transparent
|
|
||||||
outline none
|
|
||||||
border solid 1px var(--primary)
|
|
||||||
border-radius 36px
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background var(--primaryAlpha01)
|
|
||||||
|
|
||||||
&:active
|
|
||||||
background var(--primaryAlpha02)
|
|
||||||
|
|
||||||
&.active
|
|
||||||
color var(--primaryForeground)
|
|
||||||
background var(--primary)
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background var(--primaryLighten10)
|
|
||||||
border-color var(--primaryLighten10)
|
|
||||||
|
|
||||||
&:active
|
|
||||||
background var(--primaryDarken10)
|
|
||||||
border-color var(--primaryDarken10)
|
|
||||||
|
|
||||||
&.wait
|
|
||||||
cursor wait !important
|
|
||||||
opacity 0.7
|
|
||||||
|
|
||||||
*
|
|
||||||
pointer-events none
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -10,7 +10,6 @@ import subNoteContent from './sub-note-content.vue';
|
|||||||
import noteCard from './note-card.vue';
|
import noteCard from './note-card.vue';
|
||||||
import userCard from './user-card.vue';
|
import userCard from './user-card.vue';
|
||||||
import noteDetail from './note-detail.vue';
|
import noteDetail from './note-detail.vue';
|
||||||
import followButton from './follow-button.vue';
|
|
||||||
import friendsMaker from './friends-maker.vue';
|
import friendsMaker from './friends-maker.vue';
|
||||||
import notification from './notification.vue';
|
import notification from './notification.vue';
|
||||||
import notifications from './notifications.vue';
|
import notifications from './notifications.vue';
|
||||||
@@ -33,7 +32,6 @@ Vue.component('mk-sub-note-content', subNoteContent);
|
|||||||
Vue.component('mk-note-card', noteCard);
|
Vue.component('mk-note-card', noteCard);
|
||||||
Vue.component('mk-user-card', userCard);
|
Vue.component('mk-user-card', userCard);
|
||||||
Vue.component('mk-note-detail', noteDetail);
|
Vue.component('mk-note-detail', noteDetail);
|
||||||
Vue.component('mk-follow-button', followButton);
|
|
||||||
Vue.component('mk-friends-maker', friendsMaker);
|
Vue.component('mk-friends-maker', friendsMaker);
|
||||||
Vue.component('mk-notification', notification);
|
Vue.component('mk-notification', notification);
|
||||||
Vue.component('mk-notifications', notifications);
|
Vue.component('mk-notifications', notifications);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
</header>
|
</header>
|
||||||
<a class="name" :href="user | userPage" target="_blank">{{ user | userName }}</a>
|
<a class="name" :href="user | userPage" target="_blank">{{ user | userName }}</a>
|
||||||
<p class="username"><mk-acct :user="user"/></p>
|
<p class="username"><mk-acct :user="user"/></p>
|
||||||
<mk-follow-button :user="user"/>
|
<mk-follow-button class="follow-button" :user="user"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -53,7 +53,7 @@ export default Vue.extend({
|
|||||||
font-size 15px
|
font-size 15px
|
||||||
color #ccc
|
color #ccc
|
||||||
|
|
||||||
> .mk-follow-button
|
> .follow-button
|
||||||
display inline-block
|
display inline-block
|
||||||
margin 8px 0 16px 0
|
margin 8px 0 16px 0
|
||||||
|
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withFiles: this.withMedia,
|
withFiles: this.withMedia,
|
||||||
limit: fetchLimit + 1
|
limit: fetchLimit + 1,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
if (notes.length == fetchLimit + 1) {
|
if (notes.length == fetchLimit + 1) {
|
||||||
notes.pop();
|
notes.pop();
|
||||||
@@ -66,7 +67,7 @@ export default Vue.extend({
|
|||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withFiles: this.withMedia,
|
withFiles: this.withMedia,
|
||||||
limit: fetchLimit + 1,
|
limit: fetchLimit + 1,
|
||||||
untilId: (this.$refs.timeline as any).tail().id
|
untilDate: new Date((this.$refs.timeline as any).tail().createdAt).getTime()
|
||||||
});
|
});
|
||||||
|
|
||||||
promise.then(notes => {
|
promise.then(notes => {
|
||||||
|
|||||||
@@ -243,9 +243,6 @@ main
|
|||||||
font-size 18px
|
font-size 18px
|
||||||
color var(--text)
|
color var(--text)
|
||||||
|
|
||||||
> .mk-follow-button
|
|
||||||
margin 0
|
|
||||||
|
|
||||||
> .title
|
> .title
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id
|
userId: this.user.id,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
this.fetching = false;
|
this.fetching = false;
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ export default Vue.extend({
|
|||||||
this.$root.api('users/notes', {
|
this.$root.api('users/notes', {
|
||||||
userId: this.user.id,
|
userId: this.user.id,
|
||||||
withFiles: true,
|
withFiles: true,
|
||||||
limit: 6
|
limit: 6,
|
||||||
|
untilDate: new Date().getTime() + 1000 * 86400 * 365
|
||||||
}).then(notes => {
|
}).then(notes => {
|
||||||
notes.forEach(note => {
|
notes.forEach(note => {
|
||||||
note.media.forEach(media => {
|
note.media.forEach(media => {
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
secondary: '$secondary',
|
secondary: '$secondary',
|
||||||
bg: ':darken<8<$secondary',
|
bg: ':darken<8<$secondary',
|
||||||
text: '$text',
|
text: '$text',
|
||||||
|
textHighlighted: ':lighten<7<$text',
|
||||||
|
|
||||||
scrollbarTrack: ':darken<5<$secondary',
|
scrollbarTrack: ':darken<5<$secondary',
|
||||||
scrollbarHandle: ':lighten<5<$secondary',
|
scrollbarHandle: ':lighten<5<$secondary',
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
secondary: '$secondary',
|
secondary: '$secondary',
|
||||||
bg: ':darken<8<$secondary',
|
bg: ':darken<8<$secondary',
|
||||||
text: '$text',
|
text: '$text',
|
||||||
|
textHighlighted: ':darken<7<$text',
|
||||||
|
|
||||||
scrollbarTrack: '#fff',
|
scrollbarTrack: '#fff',
|
||||||
scrollbarHandle: '#00000033',
|
scrollbarHandle: '#00000033',
|
||||||
|
|||||||
@@ -153,9 +153,7 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//#region Construct query
|
//#region Construct query
|
||||||
const sort = {
|
const sort = { } as any;
|
||||||
_id: -1
|
|
||||||
};
|
|
||||||
|
|
||||||
const query = {
|
const query = {
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
@@ -168,15 +166,17 @@ export default define(meta, (ps, me) => new Promise(async (res, rej) => {
|
|||||||
$gt: ps.sinceId
|
$gt: ps.sinceId
|
||||||
};
|
};
|
||||||
} else if (ps.untilId) {
|
} else if (ps.untilId) {
|
||||||
|
sort._id = -1;
|
||||||
query._id = {
|
query._id = {
|
||||||
$lt: ps.untilId
|
$lt: ps.untilId
|
||||||
};
|
};
|
||||||
} else if (ps.sinceDate) {
|
} else if (ps.sinceDate) {
|
||||||
sort._id = 1;
|
sort.createdAt = 1;
|
||||||
query.createdAt = {
|
query.createdAt = {
|
||||||
$gt: new Date(ps.sinceDate)
|
$gt: new Date(ps.sinceDate)
|
||||||
};
|
};
|
||||||
} else if (ps.untilDate) {
|
} else if (ps.untilDate) {
|
||||||
|
sort.createdAt = -1;
|
||||||
query.createdAt = {
|
query.createdAt = {
|
||||||
$lt: new Date(ps.untilDate)
|
$lt: new Date(ps.untilDate)
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user