Commit 0c064105 authored by HJ's avatar HJ 🐼

Merge branch '482-subscribe-user' into 'develop'

Implement ability to subscribe to a user

Closes #482

See merge request pleroma/pleroma-fe!771
parents 0a7c60c3 ff90d864
......@@ -283,6 +283,31 @@ i[class*=icon-] {
color: var(--icon, $fallback--icon)
}
.btn-block {
display: block;
width: 100%;
}
.btn-group {
position: relative;
display: inline-flex;
vertical-align: middle;
button {
position: relative;
flex: 1 1 auto;
&:not(:last-child) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
&:not(:first-child) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
}
}
.container {
display: flex;
......
......@@ -100,7 +100,7 @@
<!-- eslint-disable vue/no-v-html -->
<h1><a :href="attachment.url">{{ attachment.oembed.title }}</a></h1>
<div v-html="attachment.oembed.oembedHTML" />
<!-- eslint-enabled vue/no-v-html -->
<!-- eslint-enable vue/no-v-html -->
</div>
</div>
</div>
......
<template>
<div
class="block"
style="position: relative"
>
<div>
<Popper
trigger="click"
append-to-body
......@@ -131,6 +128,7 @@
</div>
<button
slot="reference"
class="btn btn-default btn-block"
:class="{ pressed: showDropDown }"
@click="toggleMenu"
>
......
......@@ -3,7 +3,7 @@
:disabled="progress || disabled"
@click="onClick"
>
<template v-if="progress">
<template v-if="progress && $slots.progress">
<slot name="progress" />
</template>
<template v-else>
......
import UserAvatar from '../user_avatar/user_avatar.vue'
import RemoteFollow from '../remote_follow/remote_follow.vue'
import ProgressButton from '../progress_button/progress_button.vue'
import ModerationTools from '../moderation_tools/moderation_tools.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js'
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
......@@ -104,7 +105,8 @@ export default {
components: {
UserAvatar,
RemoteFollow,
ModerationTools
ModerationTools,
ProgressButton
},
methods: {
followUser () {
......@@ -135,6 +137,12 @@ export default {
unmuteUser () {
this.$store.dispatch('unmuteUser', this.user.id)
},
subscribeUser () {
return this.$store.dispatch('subscribeUser', this.user.id)
},
unsubscribeUser () {
return this.$store.dispatch('unsubscribeUser', this.user.id)
},
setProfileView (v) {
if (this.switcher) {
const store = this.$store
......
......@@ -112,101 +112,120 @@
</div>
</div>
<div
v-if="isOtherUser"
v-if="loggedIn && isOtherUser"
class="user-interactions"
>
<div
v-if="loggedIn"
class="follow"
>
<span v-if="user.following">
<!--Following them!-->
<button
class="pressed"
:disabled="followRequestInProgress"
:title="$t('user_card.follow_unfollow')"
@click="unfollowUser"
>
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else>
{{ $t('user_card.following') }}
</template>
</button>
</span>
<span v-if="!user.following">
<button
:disabled="followRequestInProgress"
:title="followRequestSent ? $t('user_card.follow_again') : ''"
@click="followUser"
>
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else-if="followRequestSent">
{{ $t('user_card.follow_sent') }}
</template>
<template v-else>
{{ $t('user_card.follow') }}
</template>
</button>
</span>
<div v-if="!user.following">
<button
class="btn btn-default btn-block"
:disabled="followRequestInProgress"
:title="followRequestSent ? $t('user_card.follow_again') : ''"
@click="followUser"
>
<template v-if="followRequestInProgress">
{{ $t('user_card.follow_progress') }}
</template>
<template v-else-if="followRequestSent">
{{ $t('user_card.follow_sent') }}
</template>
<template v-else>
{{ $t('user_card.follow') }}
</template>
</button>
</div>
<div v-else-if="followRequestInProgress">
<button
class="btn btn-default btn-block pressed"
disabled
:title="$t('user_card.follow_unfollow')"
@click="unfollowUser"
>
{{ $t('user_card.follow_progress') }}
</button>
</div>
<div
v-if="isOtherUser && loggedIn"
class="mute"
v-else
class="btn-group"
>
<span v-if="user.muted">
<button
class="pressed"
@click="unmuteUser"
>
{{ $t('user_card.muted') }}
</button>
</span>
<span v-if="!user.muted">
<button @click="muteUser">
{{ $t('user_card.mute') }}
</button>
</span>
<button
class="btn btn-default pressed"
:title="$t('user_card.follow_unfollow')"
@click="unfollowUser"
>
{{ $t('user_card.following') }}
</button>
<ProgressButton
v-if="!user.subscribed"
class="btn btn-default"
:click="subscribeUser"
:title="$t('user_card.subscribe')"
>
<i class="icon-bell-alt" />
</ProgressButton>
<ProgressButton
v-else
class="btn btn-default pressed"
:click="unsubscribeUser"
:title="$t('user_card.unsubscribe')"
>
<i class="icon-bell-ringing-o" />
</ProgressButton>
</div>
<div v-if="!loggedIn && user.is_local">
<RemoteFollow :user="user" />
<div>
<button
v-if="user.muted"
class="btn btn-default btn-block pressed"
@click="unmuteUser"
>
{{ $t('user_card.muted') }}
</button>
<button
v-else
class="btn btn-default btn-block"
@click="muteUser"
>
{{ $t('user_card.mute') }}
</button>
</div>
<div
v-if="isOtherUser && loggedIn"
class="block"
>
<span v-if="user.statusnet_blocking">
<button
class="pressed"
@click="unblockUser"
>
{{ $t('user_card.blocked') }}
</button>
</span>
<span v-if="!user.statusnet_blocking">
<button @click="blockUser">
{{ $t('user_card.block') }}
</button>
</span>
<div>
<button
v-if="user.statusnet_blocking"
class="btn btn-default btn-block pressed"
@click="unblockUser"
>
{{ $t('user_card.blocked') }}
</button>
<button
v-else
class="btn btn-default btn-block"
@click="blockUser"
>
{{ $t('user_card.block') }}
</button>
</div>
<div
v-if="isOtherUser && loggedIn"
class="block"
>
<span>
<button @click="reportUser">
{{ $t('user_card.report') }}
</button>
</span>
<div>
<button
class="btn btn-default btn-block"
@click="reportUser"
>
{{ $t('user_card.report') }}
</button>
</div>
<ModerationTools
v-if="loggedIn.role === &quot;admin&quot;"
:user="user"
/>
</div>
<div
v-if="!loggedIn && user.is_local"
class="user-interactions"
>
<RemoteFollow :user="user" />
</div>
</div>
</div>
<div
......@@ -487,40 +506,22 @@
display: flex;
flex-flow: row wrap;
justify-content: space-between;
margin-right: -.75em;
div {
> * {
flex: 1 0 0;
margin-right: .75em;
margin-bottom: .6em;
margin: 0 .75em .6em 0;
white-space: nowrap;
}
.mute {
max-width: 220px;
min-height: 28px;
}
.follow {
max-width: 220px;
min-height: 28px;
}
button {
width: 100%;
height: 100%;
margin: 0;
}
.remote-button {
height: 28px !important;
width: 92%;
}
.pressed {
border-bottom-color: rgba(255, 255, 255, 0.2);
border-top-color: rgba(0, 0, 0, 0.2);
&.pressed {
// TODO: This should be themed.
border-bottom-color: rgba(255, 255, 255, 0.2);
border-top-color: rgba(0, 0, 0, 0.2);
}
}
}
}
......
......@@ -3,7 +3,6 @@ import UserCard from '../user_card/user_card.vue'
import FollowCard from '../follow_card/follow_card.vue'
import Timeline from '../timeline/timeline.vue'
import Conversation from '../conversation/conversation.vue'
import ModerationTools from '../moderation_tools/moderation_tools.vue'
import List from '../list/list.vue'
import withLoadMore from '../../hocs/with_load_more/with_load_more'
......@@ -132,7 +131,6 @@ const UserProfile = {
Timeline,
FollowerList,
FriendList,
ModerationTools,
FollowCard,
Conversation
}
......
......@@ -529,6 +529,8 @@
"remote_follow": "Remote follow",
"report": "Report",
"statuses": "Statuses",
"subscribe": "Subscribe",
"unsubscribe": "Unsubscribe",
"unblock": "Unblock",
"unblock_progress": "Unblocking...",
"block_progress": "Blocking...",
......
......@@ -135,6 +135,7 @@ export const mutations = {
user.following = relationship.following
user.muted = relationship.muting
user.statusnet_blocking = relationship.blocking
user.subscribed = relationship.subscribing
}
})
},
......@@ -304,6 +305,14 @@ const users = {
clearFollowers ({ commit }, userId) {
commit('clearFollowers', userId)
},
subscribeUser ({ rootState, commit }, id) {
return rootState.api.backendInteractor.subscribeUser(id)
.then((relationship) => commit('updateUserRelationship', [relationship]))
},
unsubscribeUser ({ rootState, commit }, id) {
return rootState.api.backendInteractor.unsubscribeUser(id)
.then((relationship) => commit('updateUserRelationship', [relationship]))
},
registerPushNotifications (store) {
const token = store.state.currentUser.credentials
const vapidPublicKey = store.rootState.instance.vapidPublicKey
......
......@@ -55,6 +55,8 @@ const MASTODON_BLOCK_USER_URL = id => `/api/v1/accounts/${id}/block`
const MASTODON_UNBLOCK_USER_URL = id => `/api/v1/accounts/${id}/unblock`
const MASTODON_MUTE_USER_URL = id => `/api/v1/accounts/${id}/mute`
const MASTODON_UNMUTE_USER_URL = id => `/api/v1/accounts/${id}/unmute`
const MASTODON_SUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/subscribe`
const MASTODON_UNSUBSCRIBE_USER = id => `/api/v1/pleroma/accounts/${id}/unsubscribe`
const MASTODON_POST_STATUS_URL = '/api/v1/statuses'
const MASTODON_MEDIA_UPLOAD_URL = '/api/v1/media'
const MASTODON_VOTE_URL = id => `/api/v1/polls/${id}/votes`
......@@ -752,6 +754,14 @@ const unmuteUser = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_UNMUTE_USER_URL(id), credentials, method: 'POST' })
}
const subscribeUser = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_SUBSCRIBE_USER(id), credentials, method: 'POST' })
}
const unsubscribeUser = ({ id, credentials }) => {
return promisedRequest({ url: MASTODON_UNSUBSCRIBE_USER(id), credentials, method: 'POST' })
}
const fetchBlocks = ({ credentials }) => {
return promisedRequest({ url: MASTODON_USER_BLOCKS_URL, credentials })
.then((users) => users.map(parseUser))
......@@ -882,6 +892,8 @@ const apiService = {
fetchMutes,
muteUser,
unmuteUser,
subscribeUser,
unsubscribeUser,
fetchBlocks,
fetchOAuthTokens,
revokeOAuthToken,
......
......@@ -108,6 +108,8 @@ const backendInteractorService = credentials => {
const fetchMutes = () => apiService.fetchMutes({ credentials })
const muteUser = (id) => apiService.muteUser({ credentials, id })
const unmuteUser = (id) => apiService.unmuteUser({ credentials, id })
const subscribeUser = (id) => apiService.subscribeUser({ credentials, id })
const unsubscribeUser = (id) => apiService.unsubscribeUser({ credentials, id })
const fetchBlocks = () => apiService.fetchBlocks({ credentials })
const fetchFollowRequests = () => apiService.fetchFollowRequests({ credentials })
const fetchOAuthTokens = () => apiService.fetchOAuthTokens({ credentials })
......@@ -167,6 +169,8 @@ const backendInteractorService = credentials => {
fetchMutes,
muteUser,
unmuteUser,
subscribeUser,
unsubscribeUser,
fetchBlocks,
fetchOAuthTokens,
revokeOAuthToken,
......
......@@ -68,6 +68,7 @@ export const parseUser = (data) => {
output.following = relationship.following
output.statusnet_blocking = relationship.blocking
output.muted = relationship.muting
output.subscribed = relationship.subscribing
}
output.hide_follows = data.pleroma.hide_follows
......
File mode changed from 100644 to 100755
File mode changed from 100644 to 100755
......@@ -150,12 +150,6 @@
"code": 61669,
"src": "fontawesome"
},
{
"uid": "cd21cbfb28ad4d903cede582157f65dc",
"css": "bell",
"code": 59408,
"src": "fontawesome"
},
{
"uid": "ccc2329632396dc096bb638d4b46fb98",
"css": "mail-alt",
......@@ -277,6 +271,20 @@
"search": [
"ellipsis"
]
},
{
"uid": "0bef873af785ead27781fdf98b3ae740",
"css": "bell-ringing-o",
"code": 59408,
"src": "custom_icons",
"selected": true,
"svg": {
"path": "M497.8 0C468.3 0 444.4 23.9 444.4 53.3 444.4 61.1 446.1 68.3 448.9 75 301.7 96.7 213.3 213.3 213.3 320 213.3 588.3 117.8 712.8 35.6 782.2 35.6 821.1 67.8 853.3 106.7 853.3H355.6C355.6 931.7 419.4 995.6 497.8 995.6S640 931.7 640 853.3H888.9C927.8 853.3 960 821.1 960 782.2 877.8 712.8 782.2 588.3 782.2 320 782.2 213.3 693.9 96.7 546.7 75 549.4 68.3 551.1 61.1 551.1 53.3 551.1 23.9 527.2 0 497.8 0ZM189.4 44.8C108.4 118.6 70.5 215.1 71.1 320.2L142.2 319.8C141.7 231.2 170.4 158.3 237.3 97.4L189.4 44.8ZM806.2 44.8L758.3 97.4C825.2 158.3 853.9 231.2 853.3 319.8L924.4 320.2C925.1 215.1 887.2 118.6 806.2 44.8ZM408.9 844.4C413.9 844.4 417.8 848.3 417.8 853.3 417.8 897.2 453.9 933.3 497.8 933.3 502.8 933.3 506.7 937.2 506.7 942.2S502.8 951.1 497.8 951.1C443.9 951.1 400 907.2 400 853.3 400 848.3 403.9 844.4 408.9 844.4Z",
"width": 1000
},
"search": [
"bell-ringing-o"
]
}
]
}
\ No newline at end of file
......@@ -15,7 +15,7 @@
.icon-right-open:before { content: '\e80d'; } /* '' */
.icon-left-open:before { content: '\e80e'; } /* '' */
.icon-up-open:before { content: '\e80f'; } /* '' */
.icon-bell:before { content: '\e810'; } /* '' */
.icon-bell-ringing-o:before { content: '\e810'; } /* '' */
.icon-lock:before { content: '\e811'; } /* '' */
.icon-globe:before { content: '\e812'; } /* '' */
.icon-brush:before { content: '\e813'; } /* '' */
......
This diff is collapsed.
......@@ -15,7 +15,7 @@
.icon-right-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80d;&nbsp;'); }
.icon-left-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80e;&nbsp;'); }
.icon-up-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80f;&nbsp;'); }
.icon-bell { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe810;&nbsp;'); }
.icon-bell-ringing-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe810;&nbsp;'); }
.icon-lock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe811;&nbsp;'); }
.icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe812;&nbsp;'); }
.icon-brush { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe813;&nbsp;'); }
......
......@@ -26,7 +26,7 @@
.icon-right-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80d;&nbsp;'); }
.icon-left-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80e;&nbsp;'); }
.icon-up-open { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80f;&nbsp;'); }
.icon-bell { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe810;&nbsp;'); }
.icon-bell-ringing-o { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe810;&nbsp;'); }
.icon-lock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe811;&nbsp;'); }
.icon-globe { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe812;&nbsp;'); }
.icon-brush { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe813;&nbsp;'); }
......
@font-face {
font-family: 'fontello';
src: url('../font/fontello.eot?3304725');
src: url('../font/fontello.eot?3304725#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?3304725') format('woff2'),
url('../font/fontello.woff?3304725') format('woff'),
url('../font/fontello.ttf?3304725') format('truetype'),
url('../font/fontello.svg?3304725#fontello') format('svg');
src: url('../font/fontello.eot?91349539');
src: url('../font/fontello.eot?91349539#iefix') format('embedded-opentype'),
url('../font/fontello.woff2?91349539') format('woff2'),
url('../font/fontello.woff?91349539') format('woff'),
url('../font/fontello.ttf?91349539') format('truetype'),
url('../font/fontello.svg?91349539#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
......@@ -15,7 +15,7 @@
@media screen and (-webkit-min-device-pixel-ratio:0) {
@font-face {
font-family: 'fontello';
src: url('../font/fontello.svg?3304725#fontello') format('svg');
src: url('../font/fontello.svg?91349539#fontello') format('svg');
}
}
*/
......@@ -71,7 +71,7 @@
.icon-right-open:before { content: '\e80d'; } /* '' */
.icon-left-open:before { content: '\e80e'; } /* '' */
.icon-up-open:before { content: '\e80f'; } /* '' */
.icon-bell:before { content: '\e810'; } /* '' */
.icon-bell-ringing-o:before { content: '\e810'; } /* '' */
.icon-lock:before { content: '\e811'; } /* '' */
.icon-globe:before { content: '\e812'; } /* '' */
.icon-brush:before { content: '\e813'; } /* '' */
......
......@@ -229,11 +229,11 @@ body {
}
@font-face {
font-family: 'fontello';
src: url('./font/fontello.eot?14310629');
src: url('./font/fontello.eot?14310629#iefix') format('embedded-opentype'),
url('./font/fontello.woff?14310629') format('woff'),
url('./font/fontello.ttf?14310629') format('truetype'),
url('./font/fontello.svg?14310629#fontello') format('svg');
src: url('./font/fontello.eot?82370835');
src: url('./font/fontello.eot?82370835#iefix') format('embedded-opentype'),
url('./font/fontello.woff?82370835') format('woff'),
url('./font/fontello.ttf?82370835') format('truetype'),
url('./font/fontello.svg?82370835#fontello') format('svg');
font-weight: normal;
font-style: normal;
}
......@@ -322,7 +322,7 @@ body {
<div class="the-icons span3" title="Code: 0xe80f"><i class="demo-icon icon-up-open">&#xe80f;</i> <span class="i-name">icon-up-open</span><span class="i-code">0xe80f</span></div>
</div>
<div class="row">
<div class="the-icons span3" title="Code: 0xe810"><i class="demo-icon icon-bell">&#xe810;</i> <span class="i-name">icon-bell</span><span class="i-code">0xe810</span></div>
<div class="the-icons span3" title="Code: 0xe810"><i class="demo-icon icon-bell-ringing-o">&#xe810;</i> <span class="i-name">icon-bell-ringing-o</span><span class="i-code">0xe810</span></div>
<div class="the-icons span3" title="Code: 0xe811"><i class="demo-icon icon-lock">&#xe811;</i> <span class="i-name">icon-lock</span><span class="i-code">0xe811</span></div>
<div class="the-icons span3" title="Code: 0xe812"><i class="demo-icon icon-globe">&#xe812;</i> <span class="i-name">icon-globe</span><span class="i-code">0xe812</span></div>
<div class="the-icons span3" title="Code: 0xe813"><i class="demo-icon icon-brush">&#xe813;</i> <span class="i-name">icon-brush</span><span class="i-code">0xe813</span></div>
......
......@@ -38,7 +38,7 @@
<glyph glyph-name="up-open" unicode="&#xe80f;" d="M939 114l-92-92q-11-10-26-10t-25 10l-296 297-296-297q-11-10-25-10t-25 10l-93 92q-11 11-11 26t11 25l414 414q11 10 25 10t25-10l414-414q11-11 11-25t-11-26z" horiz-adv-x="1000" />
<glyph glyph-name="bell" unicode="&#xe810;" d="M509-89q0 8-9 8-33 0-57 24t-23 57q0 9-9 9t-9-9q0-41 29-70t69-28q9 0 9 9z m-372 160h726q-149 168-149 465 0 28-13 58t-39 58-67 45-95 17-95-17-67-45-39-58-13-58q0-297-149-465z m827 0q0-29-21-50t-50-21h-250q0-59-42-101t-101-42-101 42-42 101h-250q-29 0-50 21t-21 50q28 24 51 49t47 67 42 89 27 115 11 145q0 84 66 157t171 89q-5 10-5 21 0 23 16 38t38 16 38-16 16-38q0-11-5-21 106-16 171-89t66-157q0-78 11-145t28-115 41-89 48-67 50-49z" horiz-adv-x="1000" />
<glyph glyph-name="bell-ringing-o" unicode="&#xe810;" d="M498 857c-30 0-54-24-54-53 0-8 2-15 5-22-147-22-236-138-236-245 0-268-95-393-177-462 0-39 32-71 71-71h249c0-79 63-143 142-143s142 64 142 143h249c39 0 71 32 71 71-82 69-178 194-178 462 0 107-88 223-235 245 2 7 4 14 4 22 0 29-24 53-53 53z m-309-45c-81-74-118-170-118-275l71 0c0 89 28 162 95 223l-48 52z m617 0l-48-52c67-61 96-134 95-223l71 0c1 105-37 201-118 275z m-397-799c5 0 9-4 9-9 0-44 36-80 80-80 5 0 9-4 9-9s-4-9-9-9c-54 0-98 44-98 98 0 5 4 9 9 9z" horiz-adv-x="1000" />
<glyph glyph-name="lock" unicode="&#xe811;" d="M179 428h285v108q0 59-42 101t-101 41-101-41-41-101v-108z m464-53v-322q0-22-16-37t-38-16h-535q-23 0-38 16t-16 37v322q0 22 16 38t38 15h17v108q0 102 74 176t176 74 177-74 73-176v-108h18q23 0 38-15t16-38z" horiz-adv-x="642.9" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment