diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index de21364b91..7d716f6ca3 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -668,13 +668,6 @@ desktop/views/components/media-video.vue: sensitive: "閲覧注意" click-to-show: "クリックして表示" -desktop/views/components/follow-button.vue: - following: "フォロー中" - follow: "フォロー" - request-pending: "フォロー許可待ち" - follow-processing: "フォロー処理中" - follow-request: "フォロー申請" - desktop/views/components/followers-window.vue: followers: "{} のフォロワー" @@ -1336,7 +1329,7 @@ mobile/views/components/media-video.vue: sensitive: "閲覧注意" click-to-show: "クリックして表示" -mobile/views/components/follow-button.vue: +common/views/components/follow-button.vue: following: "フォロー中" follow: "フォロー" request-pending: "フォロー許可待ち" diff --git a/src/client/app/common/views/components/follow-button.vue b/src/client/app/common/views/components/follow-button.vue new file mode 100644 index 0000000000..d88a11aca8 --- /dev/null +++ b/src/client/app/common/views/components/follow-button.vue @@ -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> diff --git a/src/client/app/common/views/components/index.ts b/src/client/app/common/views/components/index.ts index f6ab63e63a..071c31a0d5 100644 --- a/src/client/app/common/views/components/index.ts +++ b/src/client/app/common/views/components/index.ts @@ -1,5 +1,6 @@ import Vue from 'vue'; +import followButton from './follow-button.vue'; import muteAndBlock from './mute-and-block.vue'; import error from './error.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 formRadio from './ui/form/radio.vue'; +Vue.component('mk-follow-button', followButton); Vue.component('mk-mute-and-block', muteAndBlock); Vue.component('mk-error', error); Vue.component('mk-api-settings', apiSettings); diff --git a/src/client/app/desktop/views/components/follow-button.vue b/src/client/app/desktop/views/components/follow-button.vue deleted file mode 100644 index 70711d1d8c..0000000000 --- a/src/client/app/desktop/views/components/follow-button.vue +++ /dev/null @@ -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> diff --git a/src/client/app/desktop/views/components/index.ts b/src/client/app/desktop/views/components/index.ts index 6d516c118a..36636c98e3 100644 --- a/src/client/app/desktop/views/components/index.ts +++ b/src/client/app/desktop/views/components/index.ts @@ -14,7 +14,6 @@ import mediaVideo from './media-video.vue'; import notifications from './notifications.vue'; import noteForm from './post-form.vue'; import renoteForm from './renote-form.vue'; -import followButton from './follow-button.vue'; import notePreview from './note-preview.vue'; import noteDetail from './note-detail.vue'; import settings from './settings.vue'; @@ -39,7 +38,6 @@ Vue.component('mk-media-video', mediaVideo); Vue.component('mk-notifications', notifications); Vue.component('mk-post-form', noteForm); Vue.component('mk-renote-form', renoteForm); -Vue.component('mk-follow-button', followButton); Vue.component('mk-note-preview', notePreview); Vue.component('mk-note-detail', noteDetail); Vue.component('mk-settings', settings); diff --git a/src/client/app/desktop/views/components/user-card.vue b/src/client/app/desktop/views/components/user-card.vue index bd5c6eed09..54fa15a190 100644 --- a/src/client/app/desktop/views/components/user-card.vue +++ b/src/client/app/desktop/views/components/user-card.vue @@ -2,7 +2,7 @@ <div class="zvdbznxvfixtmujpsigoccczftvpiwqh"> <div class="banner" :style="bannerStyle"></div> <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"> <router-link :to="user | userPage" class="name">{{ user | userName }}</router-link> <span class="username">@{{ user | acct }}</span> diff --git a/src/client/app/desktop/views/components/user-preview.vue b/src/client/app/desktop/views/components/user-preview.vue index ad4f60be63..64884f6f52 100644 --- a/src/client/app/desktop/views/components/user-preview.vue +++ b/src/client/app/desktop/views/components/user-preview.vue @@ -19,7 +19,7 @@ <p>{{ $t('followers') }}</p><span>{{ u.followersCount }}</span> </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> </div> </template> @@ -154,7 +154,7 @@ export default Vue.extend({ font-size 1em color var(--primary) - > .mk-follow-button + > .follow-button position absolute top 92px right 8px diff --git a/src/client/app/desktop/views/pages/deck/deck.user-column.vue b/src/client/app/desktop/views/pages/deck/deck.user-column.vue index 53bb54b5a6..27da5427b9 100644 --- a/src/client/app/desktop/views/pages/deck/deck.user-column.vue +++ b/src/client/app/desktop/views/pages/deck/deck.user-column.vue @@ -14,7 +14,7 @@ <header :style="bannerStyle"> <div> <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"/> <span class="name">{{ user | userName }}</span> <span class="acct">@{{ user | acct }}</span> diff --git a/src/client/app/desktop/views/pages/user/user.friends.vue b/src/client/app/desktop/views/pages/user/user.friends.vue index ed0fcd03c4..b1c1321e41 100644 --- a/src/client/app/desktop/views/pages/user/user.friends.vue +++ b/src/client/app/desktop/views/pages/user/user.friends.vue @@ -9,7 +9,7 @@ <router-link class="name" :to="friend | userPage" v-user-preview="friend.id">{{ friend.name }}</router-link> <p class="username">@{{ friend | acct }}</p> </div> - <mk-follow-button :user="friend"/> + <mk-follow-button class="follow-button" :user="friend"/> </div> </template> <p class="empty" v-if="!fetching && users.length == 0">{{ $t('no-users') }}</p> @@ -110,7 +110,7 @@ export default Vue.extend({ color var(--text) opacity 0.7 - > .mk-follow-button + > .follow-button position absolute top 16px right 16px diff --git a/src/client/app/desktop/views/pages/user/user.profile.vue b/src/client/app/desktop/views/pages/user/user.profile.vue index eba492d4f0..db797e3612 100644 --- a/src/client/app/desktop/views/pages/user/user.profile.vue +++ b/src/client/app/desktop/views/pages/user/user.profile.vue @@ -1,7 +1,7 @@ <template> <div class="profile" v-if="$store.getters.isSignedIn"> <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="stalk" v-if="user.isFollowing"> <span v-if="user.isStalking">{{ $t('stalking') }} <a @click="unstalk"><fa icon="meh"/> {{ $t('unstalk') }}</a></span> diff --git a/src/client/app/desktop/views/widgets/users.vue b/src/client/app/desktop/views/widgets/users.vue index 8ee73d0ded..d00561caa2 100644 --- a/src/client/app/desktop/views/widgets/users.vue +++ b/src/client/app/desktop/views/widgets/users.vue @@ -114,11 +114,6 @@ export default define({ color var(--text) opacity 0.7 - > .mk-follow-button - position absolute - top 16px - right 16px - > .empty margin 0 padding 16px diff --git a/src/client/app/mobile/views/components/follow-button.vue b/src/client/app/mobile/views/components/follow-button.vue deleted file mode 100644 index 00e96082ce..0000000000 --- a/src/client/app/mobile/views/components/follow-button.vue +++ /dev/null @@ -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> diff --git a/src/client/app/mobile/views/components/index.ts b/src/client/app/mobile/views/components/index.ts index 5e9640bf01..e6cc2c997c 100644 --- a/src/client/app/mobile/views/components/index.ts +++ b/src/client/app/mobile/views/components/index.ts @@ -10,7 +10,6 @@ import subNoteContent from './sub-note-content.vue'; import noteCard from './note-card.vue'; import userCard from './user-card.vue'; import noteDetail from './note-detail.vue'; -import followButton from './follow-button.vue'; import friendsMaker from './friends-maker.vue'; import notification from './notification.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-user-card', userCard); Vue.component('mk-note-detail', noteDetail); -Vue.component('mk-follow-button', followButton); Vue.component('mk-friends-maker', friendsMaker); Vue.component('mk-notification', notification); Vue.component('mk-notifications', notifications); diff --git a/src/client/app/mobile/views/components/user-card.vue b/src/client/app/mobile/views/components/user-card.vue index 7b8f2251b2..6d79e7a872 100644 --- a/src/client/app/mobile/views/components/user-card.vue +++ b/src/client/app/mobile/views/components/user-card.vue @@ -5,7 +5,7 @@ </header> <a class="name" :href="user | userPage" target="_blank">{{ user | userName }}</a> <p class="username"><mk-acct :user="user"/></p> - <mk-follow-button :user="user"/> + <mk-follow-button class="follow-button" :user="user"/> </div> </template> @@ -53,7 +53,7 @@ export default Vue.extend({ font-size 15px color #ccc - > .mk-follow-button + > .follow-button display inline-block margin 8px 0 16px 0 diff --git a/src/client/app/mobile/views/pages/user.vue b/src/client/app/mobile/views/pages/user.vue index c88cbe6813..63e322aac7 100644 --- a/src/client/app/mobile/views/pages/user.vue +++ b/src/client/app/mobile/views/pages/user.vue @@ -243,9 +243,6 @@ main font-size 18px color var(--text) - > .mk-follow-button - margin 0 - > .title margin 8px 0