Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
f1047f1ce5 | |||
9beddc941a | |||
3a6a01d2d6 | |||
e0e4bdbdbc | |||
b3daf79b6a | |||
81aa21915b | |||
5aadd80243 | |||
23350b1cbc | |||
daff0977cb | |||
c1e7d56ff8 | |||
61aef56f83 |
@ -1,6 +1,13 @@
|
|||||||
ChangeLog
|
ChangeLog
|
||||||
=========
|
=========
|
||||||
|
|
||||||
|
10.87.5
|
||||||
|
----------
|
||||||
|
* モバイル版でも連携サービスを表示するように
|
||||||
|
* webfingerのacceptが反映されない問題を修正
|
||||||
|
* 返信が遷移後も残り続ける問題を修正
|
||||||
|
* デザインの調整
|
||||||
|
|
||||||
10.87.4
|
10.87.4
|
||||||
----------
|
----------
|
||||||
* フォローリクエストを許可するときにエラーになる問題を修正
|
* フォローリクエストを許可するときにエラーになる問題を修正
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "Following"
|
following: "Following"
|
||||||
followers: "Followers"
|
followers: "Followers"
|
||||||
is-bot: "This account is a Bot"
|
is-bot: "This account is a Bot"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age} years old"
|
years-old: "{age} years old"
|
||||||
year: "/"
|
year: "/"
|
||||||
month: "/"
|
month: "/"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "Suit"
|
following: "Suit"
|
||||||
followers: "Abonné·e·s"
|
followers: "Abonné·e·s"
|
||||||
is-bot: "Ce compte est un Bot"
|
is-bot: "Ce compte est un Bot"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age} ans"
|
years-old: "{age} ans"
|
||||||
year: "/"
|
year: "/"
|
||||||
month: "/"
|
month: "/"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotや"
|
is-bot: "このアカウントはBotや"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -214,7 +214,7 @@ common/views/pages/explore.vue:
|
|||||||
recently-registered-users: "신규 사용자"
|
recently-registered-users: "신규 사용자"
|
||||||
popular-tags: "인기 태그"
|
popular-tags: "인기 태그"
|
||||||
federated: "연합"
|
federated: "연합"
|
||||||
explore: "{host}를 탐색"
|
explore: "{host}을(를) 탐색"
|
||||||
users-info: "현재 {users} 사용자가 등록되어 있습니다"
|
users-info: "현재 {users} 사용자가 등록되어 있습니다"
|
||||||
common/views/components/user-list.vue:
|
common/views/components/user-list.vue:
|
||||||
no-users: "사용자가 없습니다"
|
no-users: "사용자가 없습니다"
|
||||||
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "팔로잉"
|
following: "팔로잉"
|
||||||
followers: "팔로워"
|
followers: "팔로워"
|
||||||
is-bot: "이 계정은 Bot입니다"
|
is-bot: "이 계정은 Bot입니다"
|
||||||
|
no-description: "자기소개가 없습니다"
|
||||||
years-old: "{age}세"
|
years-old: "{age}세"
|
||||||
year: "년"
|
year: "년"
|
||||||
month: "월"
|
month: "월"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "Følger"
|
following: "Følger"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "Śledzeni"
|
following: "Śledzeni"
|
||||||
followers: "Śledzący"
|
followers: "Śledzący"
|
||||||
is-bot: "To konto jest botem"
|
is-bot: "To konto jest botem"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age} lat"
|
years-old: "{age} lat"
|
||||||
year: "/"
|
year: "/"
|
||||||
month: "/"
|
month: "/"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "フォロー"
|
following: "フォロー"
|
||||||
followers: "フォロワー"
|
followers: "フォロワー"
|
||||||
is-bot: "このアカウントはBotです"
|
is-bot: "このアカウントはBotです"
|
||||||
|
no-description: "自己紹介はありません"
|
||||||
years-old: "{age}歳"
|
years-old: "{age}歳"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1353,6 +1353,7 @@ desktop/views/pages/user/user.header.vue:
|
|||||||
following: "关注中"
|
following: "关注中"
|
||||||
followers: "关注者"
|
followers: "关注者"
|
||||||
is-bot: "这个账户是Bot"
|
is-bot: "这个账户是Bot"
|
||||||
|
no-description: "没有自我介绍"
|
||||||
years-old: "{age}岁"
|
years-old: "{age}岁"
|
||||||
year: "年"
|
year: "年"
|
||||||
month: "月"
|
month: "月"
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "misskey",
|
"name": "misskey",
|
||||||
"author": "syuilo <i@syuilo.com>",
|
"author": "syuilo <i@syuilo.com>",
|
||||||
"version": "10.87.4",
|
"version": "10.87.5",
|
||||||
"codename": "nighthike",
|
"codename": "nighthike",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<a class="zxrjzpcj" :href="url" :class="service" target="_blank">
|
||||||
|
<fa :icon="icon" size="lg" fixed-width /><span>{{ text }}</span>
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
props: ['url', 'text', 'icon', 'service']
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.zxrjzpcj
|
||||||
|
padding 6px 8px 6px 6px
|
||||||
|
border-radius 32px
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
text-decoration none
|
||||||
|
|
||||||
|
&.twitter
|
||||||
|
color #fff
|
||||||
|
background #1da1f3
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background #0c87cf
|
||||||
|
|
||||||
|
&.github
|
||||||
|
color #fff
|
||||||
|
background #171515
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background #000
|
||||||
|
|
||||||
|
&.discord
|
||||||
|
color #fff
|
||||||
|
background #7289da
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background #4968ce
|
||||||
|
|
||||||
|
</style>
|
26
src/client/app/common/views/components/integrations.vue
Normal file
26
src/client/app/common/views/components/integrations.vue
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<div class="nbogcrmo" :v-if="user.twitter || user.github || user.discord">
|
||||||
|
<x-integration v-if="user.twitter" service="twitter" :url="`https://twitter.com/${user.twitter.screenName}`" :text="user.twitter.screenName" :icon="['fab', 'twitter']"/>
|
||||||
|
<x-integration v-if="user.github" service="github" :url="`https://github.com/${user.github.login}`" :text="user.github.login" :icon="['fab', 'github']"/>
|
||||||
|
<x-integration v-if="user.discord" service="discord" :url="`https://discordapp.com/users/${user.discord.id}`" :text="`${user.discord.username}#${user.discord.discriminator}`" :icon="['fab', 'discord']"/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import Vue from 'vue';
|
||||||
|
import XIntegration from './integrations.integration.vue';
|
||||||
|
|
||||||
|
export default Vue.extend({
|
||||||
|
components: {
|
||||||
|
XIntegration
|
||||||
|
},
|
||||||
|
props: ['user']
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="stylus" scoped>
|
||||||
|
.nbogcrmo
|
||||||
|
> *
|
||||||
|
margin-right 10px
|
||||||
|
|
||||||
|
</style>
|
@ -9,7 +9,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</ui-container>
|
</ui-container>
|
||||||
|
|
||||||
<ui-container :body-togglable="true" ref="tags">
|
<ui-container :body-togglable="true" :expanded="tag == null" ref="tags">
|
||||||
<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
|
<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
|
||||||
|
|
||||||
<div class="vxjfqztj">
|
<div class="vxjfqztj">
|
||||||
|
@ -30,6 +30,10 @@ export default Vue.extend({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
expanded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
},
|
},
|
||||||
inject: {
|
inject: {
|
||||||
inDeck: {
|
inDeck: {
|
||||||
@ -38,7 +42,7 @@ export default Vue.extend({
|
|||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showBody: true
|
showBody: this.expanded
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
<div class="description">
|
<div class="description">
|
||||||
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
|
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
|
||||||
<p v-else class="empty">{{ $t('no-description') }}</p>
|
<p v-else class="empty">{{ $t('no-description') }}</p>
|
||||||
|
<x-integrations :user="user" style="margin-top:16px;"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="fields" v-if="user.fields">
|
<div class="fields" v-if="user.fields">
|
||||||
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
||||||
@ -52,9 +53,13 @@ import Vue from 'vue';
|
|||||||
import i18n from '../../../../i18n';
|
import i18n from '../../../../i18n';
|
||||||
import * as age from 's-age';
|
import * as age from 's-age';
|
||||||
import XUserMenu from '../../../../common/views/components/user-menu.vue';
|
import XUserMenu from '../../../../common/views/components/user-menu.vue';
|
||||||
|
import XIntegrations from '../../../../common/views/components/integrations.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('desktop/views/pages/user/user.header.vue'),
|
i18n: i18n('desktop/views/pages/user/user.header.vue'),
|
||||||
|
components: {
|
||||||
|
XIntegrations
|
||||||
|
},
|
||||||
props: ['user'],
|
props: ['user'],
|
||||||
computed: {
|
computed: {
|
||||||
style(): any {
|
style(): any {
|
||||||
@ -215,6 +220,8 @@ export default Vue.extend({
|
|||||||
color var(--text)
|
color var(--text)
|
||||||
|
|
||||||
> .description
|
> .description
|
||||||
|
font-size 15px
|
||||||
|
|
||||||
> .empty
|
> .empty
|
||||||
margin 0
|
margin 0
|
||||||
opacity 0.5
|
opacity 0.5
|
||||||
@ -251,6 +258,7 @@ export default Vue.extend({
|
|||||||
margin-top 16px
|
margin-top 16px
|
||||||
padding-top 16px
|
padding-top 16px
|
||||||
border-top solid 1px var(--faceDivider)
|
border-top solid 1px var(--faceDivider)
|
||||||
|
font-size 15px
|
||||||
|
|
||||||
&:empty
|
&:empty
|
||||||
display none
|
display none
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="lnctpgve">
|
<div class="lnctpgve">
|
||||||
<x-integrations :user="user" v-if="user.twitter || user.github || user.discord"/>
|
|
||||||
<mk-note-detail v-for="n in user.pinnedNotes" :key="n.id" :note="n" :compact="true"/>
|
<mk-note-detail v-for="n in user.pinnedNotes" :key="n.id" :note="n" :compact="true"/>
|
||||||
<!--<mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>-->
|
<!--<mk-calendar @chosen="warp" :start="new Date(user.createdAt)"/>-->
|
||||||
<div class="activity">
|
<div class="activity">
|
||||||
@ -17,11 +16,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Vue from 'vue';
|
import Vue from 'vue';
|
||||||
import i18n from '../../../../i18n';
|
import i18n from '../../../../i18n';
|
||||||
import parseAcct from '../../../../../../misc/acct/parse';
|
|
||||||
import Progress from '../../../../common/scripts/loading';
|
|
||||||
import XTimeline from './user.timeline.vue';
|
import XTimeline from './user.timeline.vue';
|
||||||
import XPhotos from './user.photos.vue';
|
import XPhotos from './user.photos.vue';
|
||||||
import XIntegrations from './user.integrations.vue';
|
|
||||||
import XActivity from '../../../../common/views/components/activity.vue';
|
import XActivity from '../../../../common/views/components/activity.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
@ -29,7 +25,6 @@ export default Vue.extend({
|
|||||||
components: {
|
components: {
|
||||||
XTimeline,
|
XTimeline,
|
||||||
XPhotos,
|
XPhotos,
|
||||||
XIntegrations,
|
|
||||||
XActivity
|
XActivity
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
<template>
|
|
||||||
<a :href="url" :class="service" target="_blank">
|
|
||||||
<fa :icon="icon" size="lg" fixed-width />
|
|
||||||
<div>{{ text }}</div>
|
|
||||||
</a>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
props: ['url', 'text', 'icon', 'service']
|
|
||||||
});
|
|
||||||
</script>
|
|
@ -1,66 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="usertwitxxxgithxxdiscxxxintegrat" :v-if="user.twitter || user.github || user.discord">
|
|
||||||
<x-integration v-if="user.twitter" service="twitter" :url="`https://twitter.com/${user.twitter.screenName}`" :text="user.twitter.screenName" :icon="['fab', 'twitter']"/>
|
|
||||||
<x-integration v-if="user.github" service="github" :url="`https://github.com/${user.github.login}`" :text="user.github.login" :icon="['fab', 'github']"/>
|
|
||||||
<x-integration v-if="user.discord" service="discord" :url="`https://discordapp.com/users/${user.discord.id}`" :text="`${user.discord.username}#${user.discord.discriminator}`" :icon="['fab', 'discord']"/>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import Vue from 'vue';
|
|
||||||
import XIntegration from './user.integrations.integration.vue';
|
|
||||||
|
|
||||||
export default Vue.extend({
|
|
||||||
components: {
|
|
||||||
XIntegration
|
|
||||||
},
|
|
||||||
props: ['user']
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="stylus" scoped>
|
|
||||||
.usertwitxxxgithxxdiscxxxintegrat
|
|
||||||
display flex
|
|
||||||
|
|
||||||
> a
|
|
||||||
display flex
|
|
||||||
flex 1
|
|
||||||
align-items center
|
|
||||||
padding 16px
|
|
||||||
box-shadow var(--shadow)
|
|
||||||
border-radius var(--round)
|
|
||||||
|
|
||||||
&:not(:last-child)
|
|
||||||
margin-right 16px
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
text-decoration none
|
|
||||||
|
|
||||||
> div
|
|
||||||
padding-left .2em
|
|
||||||
line-height 1.3em
|
|
||||||
flex 1 0
|
|
||||||
word-wrap anywhere
|
|
||||||
|
|
||||||
&.twitter
|
|
||||||
color #fff
|
|
||||||
background #1da1f3
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background #0c87cf
|
|
||||||
|
|
||||||
&.github
|
|
||||||
color #fff
|
|
||||||
background #171515
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background #000
|
|
||||||
|
|
||||||
&.discord
|
|
||||||
color #fff
|
|
||||||
background #7289da
|
|
||||||
|
|
||||||
&:hover
|
|
||||||
background #4968ce
|
|
||||||
|
|
||||||
</style>
|
|
@ -122,19 +122,23 @@ export default Vue.extend({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
mounted() {
|
watch: {
|
||||||
// Get replies
|
note() {
|
||||||
if (!this.compact) {
|
this.fetchReplies();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
fetchReplies() {
|
||||||
|
if (this.compact) return;
|
||||||
this.$root.api('notes/replies', {
|
this.$root.api('notes/replies', {
|
||||||
noteId: this.appearNote.id,
|
noteId: this.appearNote.id,
|
||||||
limit: 8
|
limit: 8
|
||||||
}).then(replies => {
|
}).then(replies => {
|
||||||
this.replies = replies;
|
this.replies = replies;
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
fetchConversation() {
|
fetchConversation() {
|
||||||
this.conversationFetching = true;
|
this.conversationFetching = true;
|
||||||
|
|
||||||
|
@ -30,10 +30,14 @@ export default Vue.extend({
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
|
expanded: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
showBody: true
|
showBody: this.expanded
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="description">
|
<div class="description">
|
||||||
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
|
<mfm v-if="user.description" :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis"/>
|
||||||
|
<x-integrations :user="user" style="margin:24px 0;"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="fields" v-if="user.fields">
|
<div class="fields" v-if="user.fields">
|
||||||
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
<dl class="field" v-for="(field, i) in user.fields" :key="i">
|
||||||
@ -86,11 +87,13 @@ import Progress from '../../../../common/scripts/loading';
|
|||||||
import XUserMenu from '../../../../common/views/components/user-menu.vue';
|
import XUserMenu from '../../../../common/views/components/user-menu.vue';
|
||||||
import XHome from './home.vue';
|
import XHome from './home.vue';
|
||||||
import { getStaticImageUrl } from '../../../../common/scripts/get-static-image-url';
|
import { getStaticImageUrl } from '../../../../common/scripts/get-static-image-url';
|
||||||
|
import XIntegrations from '../../../../common/views/components/integrations.vue';
|
||||||
|
|
||||||
export default Vue.extend({
|
export default Vue.extend({
|
||||||
i18n: i18n('mobile/views/pages/user.vue'),
|
i18n: i18n('mobile/views/pages/user.vue'),
|
||||||
components: {
|
components: {
|
||||||
XHome
|
XHome,
|
||||||
|
XIntegrations
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
@ -245,6 +248,12 @@ export default Vue.extend({
|
|||||||
margin 8px 0
|
margin 8px 0
|
||||||
color var(--mobileUserPageDescription)
|
color var(--mobileUserPageDescription)
|
||||||
|
|
||||||
|
@media (max-width 450px)
|
||||||
|
font-size 15px
|
||||||
|
|
||||||
|
@media (max-width 400px)
|
||||||
|
font-size 14px
|
||||||
|
|
||||||
> .fields
|
> .fields
|
||||||
margin 8px 0
|
margin 8px 0
|
||||||
|
|
||||||
|
41
src/prelude/xml.ts
Normal file
41
src/prelude/xml.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
const map: Record<string, string> = {
|
||||||
|
'&': '&',
|
||||||
|
'<': '<',
|
||||||
|
'>': '>',
|
||||||
|
'"': '"',
|
||||||
|
'\'': '''
|
||||||
|
};
|
||||||
|
|
||||||
|
const beginingOfCDATA = '<![CDATA[';
|
||||||
|
const endOfCDATA = ']]>';
|
||||||
|
|
||||||
|
export function escapeValue(x: string): string {
|
||||||
|
let insideOfCDATA = false;
|
||||||
|
let builder = '';
|
||||||
|
for (
|
||||||
|
let i = 0;
|
||||||
|
i < x.length;
|
||||||
|
) {
|
||||||
|
if (insideOfCDATA) {
|
||||||
|
if (x.slice(i, i + beginingOfCDATA.length) === beginingOfCDATA) {
|
||||||
|
insideOfCDATA = true;
|
||||||
|
i += beginingOfCDATA.length;
|
||||||
|
} else {
|
||||||
|
builder += x[i++];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (x.slice(i, i + endOfCDATA.length) === endOfCDATA) {
|
||||||
|
insideOfCDATA = false;
|
||||||
|
i += endOfCDATA.length;
|
||||||
|
} else {
|
||||||
|
const b = x[i++];
|
||||||
|
builder += map[b] || b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function escapeAttribute(x: string): string {
|
||||||
|
return Object.entries(map).reduce((a, [k, v]) => a.replace(k, v), x);
|
||||||
|
}
|
@ -6,27 +6,37 @@ import parseAcct from '../misc/acct/parse';
|
|||||||
import User from '../models/user';
|
import User from '../models/user';
|
||||||
import Acct from '../misc/acct/type';
|
import Acct from '../misc/acct/type';
|
||||||
import { links } from './nodeinfo';
|
import { links } from './nodeinfo';
|
||||||
|
import { escapeAttribute, escapeValue } from '../prelude/xml';
|
||||||
|
|
||||||
// Init router
|
// Init router
|
||||||
const router = new Router();
|
const router = new Router();
|
||||||
|
|
||||||
|
const XRD = (...x: { element: string, value?: string, attributes?: Record<string, string> }[]) =>
|
||||||
|
`<?xml version="1.0" encoding="UTF-8"?><XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">${x.map(({ element, value, attributes }) =>
|
||||||
|
`<${
|
||||||
|
Object.entries(typeof attributes === 'object' && attributes || {}).reduce((a, [k, v]) => `${a} ${k}="${escapeAttribute(v)}"`, element)
|
||||||
|
}${
|
||||||
|
typeof value === 'string' ? `>${escapeValue(value)}</${element}` : '/'
|
||||||
|
}>`).reduce((a, c) => a + c, '')}</XRD>`;
|
||||||
|
|
||||||
const webFingerPath = '/.well-known/webfinger';
|
const webFingerPath = '/.well-known/webfinger';
|
||||||
|
const jrd = 'application/jrd+json';
|
||||||
|
const xrd = 'application/xrd+xml';
|
||||||
|
|
||||||
router.get('/.well-known/host-meta', async ctx => {
|
router.get('/.well-known/host-meta', async ctx => {
|
||||||
ctx.set('Content-Type', 'application/xrd+xml');
|
ctx.set('Content-Type', xrd);
|
||||||
ctx.body = `<?xml version="1.0" encoding="UTF-8"?>
|
ctx.body = XRD({ element: 'Link', attributes: {
|
||||||
<XRD xmlns="http://docs.oasis-open.org/ns/xri/xrd-1.0">
|
type: xrd,
|
||||||
<Link rel="lrdd" type="application/xrd+xml" template="${config.url}${webFingerPath}?resource={uri}"/>
|
template: `${config.url}${webFingerPath}?resource={uri}`
|
||||||
</XRD>
|
}});
|
||||||
`;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
router.get('/.well-known/host-meta.json', async ctx => {
|
router.get('/.well-known/host-meta.json', async ctx => {
|
||||||
ctx.set('Content-Type', 'application/jrd+json');
|
ctx.set('Content-Type', jrd);
|
||||||
ctx.body = {
|
ctx.body = {
|
||||||
links: [{
|
links: [{
|
||||||
rel: 'lrdd',
|
rel: 'lrdd',
|
||||||
type: 'application/xrd+xml',
|
type: jrd,
|
||||||
template: `${config.url}${webFingerPath}?resource={uri}`
|
template: `${config.url}${webFingerPath}?resource={uri}`
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
@ -75,22 +85,38 @@ router.get(webFingerPath, async ctx => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.body = {
|
const subject = `acct:${user.username}@${config.host}`;
|
||||||
subject: `acct:${user.username}@${config.host}`,
|
const self = {
|
||||||
links: [{
|
rel: 'self',
|
||||||
rel: 'self',
|
type: 'application/activity+json',
|
||||||
type: 'application/activity+json',
|
href: `${config.url}/users/${user._id}`
|
||||||
href: `${config.url}/users/${user._id}`
|
};
|
||||||
}, {
|
const profilePage = {
|
||||||
rel: 'http://webfinger.net/rel/profile-page',
|
rel: 'http://webfinger.net/rel/profile-page',
|
||||||
type: 'text/html',
|
type: 'text/html',
|
||||||
href: `${config.url}/@${user.username}`
|
href: `${config.url}/@${user.username}`
|
||||||
}, {
|
};
|
||||||
rel: 'http://ostatus.org/schema/1.0/subscribe',
|
const subscribe = {
|
||||||
template: `${config.url}/authorize-follow?acct={uri}`
|
rel: 'http://ostatus.org/schema/1.0/subscribe',
|
||||||
}]
|
template: `${config.url}/authorize-follow?acct={uri}`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (ctx.accepts(jrd, xrd) === xrd) {
|
||||||
|
ctx.body = XRD(
|
||||||
|
{ element: 'Subject', value: subject },
|
||||||
|
{ element: 'Link', attributes: self },
|
||||||
|
{ element: 'Link', attributes: profilePage },
|
||||||
|
{ element: 'Link', attributes: subscribe });
|
||||||
|
ctx.type = xrd;
|
||||||
|
} else {
|
||||||
|
ctx.body = {
|
||||||
|
subject,
|
||||||
|
links: [self, profilePage, subscribe]
|
||||||
|
};
|
||||||
|
ctx.type = jrd;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.vary('Accept');
|
||||||
ctx.set('Cache-Control', 'public, max-age=180');
|
ctx.set('Cache-Control', 'public, max-age=180');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user