more options button to follow/unfollow
This commit is contained in:
parent
92176df3ab
commit
c13771c3a0
|
@ -3,7 +3,7 @@ import { followAccount, unfollowAccount } from '../_api/follow'
|
||||||
import { database } from '../_database/database'
|
import { database } from '../_database/database'
|
||||||
import { toast } from '../_utils/toast'
|
import { toast } from '../_utils/toast'
|
||||||
|
|
||||||
export async function setAccountFollowed (accountId, follow) {
|
export async function setAccountFollowed (accountId, follow, toastOnSuccess) {
|
||||||
let instanceName = store.get('currentInstance')
|
let instanceName = store.get('currentInstance')
|
||||||
let accessToken = store.get('accessToken')
|
let accessToken = store.get('accessToken')
|
||||||
try {
|
try {
|
||||||
|
@ -15,6 +15,9 @@ export async function setAccountFollowed (accountId, follow) {
|
||||||
let relationship = await database.getRelationship(instanceName, accountId)
|
let relationship = await database.getRelationship(instanceName, accountId)
|
||||||
relationship.following = follow
|
relationship.following = follow
|
||||||
await database.setRelationship(instanceName, relationship)
|
await database.setRelationship(instanceName, relationship)
|
||||||
|
if (toastOnSuccess) {
|
||||||
|
toast.say(`${follow ? 'Followed' : 'Unfollowed'}`)
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
toast.say(`Unable to ${follow ? 'follow' : 'unfollow'} account: ` + (e.message || ''))
|
toast.say(`Unable to ${follow ? 'follow' : 'unfollow'} account: ` + (e.message || ''))
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<ul class="generic-dialog-list">
|
||||||
|
{{#each items as item @key}}
|
||||||
|
<li class="generic-dialog-list-item">
|
||||||
|
<button class="generic-dialog-list-button" on:click="fire('click', item)">
|
||||||
|
<svg>
|
||||||
|
<use xlink:href="{{item.icon}}" />
|
||||||
|
</svg>
|
||||||
|
<span>
|
||||||
|
{{item.label}}
|
||||||
|
</span>
|
||||||
|
<svg class="{{item.selected ? '' : 'hidden'}}" aria-hidden="{{!item.selected}}">
|
||||||
|
<use xlink:href="#fa-check" />
|
||||||
|
</svg>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
<style>
|
||||||
|
.generic-dialog-list {
|
||||||
|
list-style: none;
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid var(--settings-list-item-border);
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: calc(100vw - 20px);
|
||||||
|
}
|
||||||
|
.generic-dialog-list-item {
|
||||||
|
border: 1px solid var(--settings-list-item-border);
|
||||||
|
font-size: 1.2em;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.generic-dialog-list-item svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
fill: var(--svg-fill);
|
||||||
|
}
|
||||||
|
.generic-dialog-list-button {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20px;
|
||||||
|
background: var(--settings-list-item-bg);
|
||||||
|
border: none;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
.generic-dialog-list-button span {
|
||||||
|
flex: 1;
|
||||||
|
text-align: left;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
.generic-dialog-list-button:hover {
|
||||||
|
background: var(--settings-list-item-bg-hover);
|
||||||
|
}
|
||||||
|
.generic-dialog-list-button:active {
|
||||||
|
background: var(--settings-list-item-bg-active);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,86 +1,28 @@
|
||||||
<ModalDialog :label :shown :closed :title background="var(--main-bg)">
|
<ModalDialog :label :shown :closed :title background="var(--main-bg)">
|
||||||
<ul class="post-privacy">
|
<GenericDialogList :items on:click="onClick(event)" />
|
||||||
{{#each postPrivacyOptions as option}}
|
|
||||||
<li class="post-privacy-item">
|
|
||||||
<button class="post-privacy-button" on:click="onClick(option)">
|
|
||||||
<svg>
|
|
||||||
<use xlink:href="{{option.icon}}" />
|
|
||||||
</svg>
|
|
||||||
<span>
|
|
||||||
{{option.label}}
|
|
||||||
</span>
|
|
||||||
<svg class="{{isSelected(option, postPrivacy) ? '' : 'hidden'}}"
|
|
||||||
aria-hidden="{{!isSelected(option, postPrivacy)}}">
|
|
||||||
<use xlink:href="#fa-check" />
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
{{/each}}
|
|
||||||
</ul>
|
|
||||||
</ModalDialog>
|
</ModalDialog>
|
||||||
<style>
|
|
||||||
.post-privacy {
|
|
||||||
list-style: none;
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid var(--settings-list-item-border);
|
|
||||||
box-sizing: border-box;
|
|
||||||
min-width: 300px;
|
|
||||||
max-width: calc(100vw - 20px);
|
|
||||||
}
|
|
||||||
.post-privacy-item {
|
|
||||||
border: 1px solid var(--settings-list-item-border);
|
|
||||||
font-size: 1.2em;
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
.post-privacy-item svg {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
fill: var(--svg-fill);
|
|
||||||
}
|
|
||||||
.post-privacy-button {
|
|
||||||
flex: 1;
|
|
||||||
padding: 20px;
|
|
||||||
background: var(--settings-list-item-bg);
|
|
||||||
border: none;
|
|
||||||
margin: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
.post-privacy-button span {
|
|
||||||
flex: 1;
|
|
||||||
text-align: left;
|
|
||||||
margin-left: 20px;
|
|
||||||
}
|
|
||||||
.post-privacy-button:hover {
|
|
||||||
background: var(--settings-list-item-bg-hover);
|
|
||||||
}
|
|
||||||
.post-privacy-button:active {
|
|
||||||
background: var(--settings-list-item-bg-active);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script>
|
<script>
|
||||||
import ModalDialog from './ModalDialog.html'
|
import ModalDialog from './ModalDialog.html'
|
||||||
import { store } from '../../_store/store'
|
import { store } from '../../_store/store'
|
||||||
import { POST_PRIVACY_OPTIONS } from '../../_static/statuses'
|
import { POST_PRIVACY_OPTIONS } from '../../_static/statuses'
|
||||||
import { setPostPrivacy } from '../../_actions/postPrivacy'
|
import { setPostPrivacy } from '../../_actions/postPrivacy'
|
||||||
|
import GenericDialogList from './GenericDialogList.html'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
ModalDialog
|
ModalDialog,
|
||||||
|
GenericDialogList
|
||||||
},
|
},
|
||||||
store: () => store,
|
store: () => store,
|
||||||
data: () => ({
|
data: () => ({
|
||||||
postPrivacyOptions: POST_PRIVACY_OPTIONS
|
postPrivacyOptions: POST_PRIVACY_OPTIONS
|
||||||
}),
|
}),
|
||||||
helpers: {
|
|
||||||
isSelected: (option, postPrivacy) => postPrivacy.key === option.key
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
async show() {
|
async show() {
|
||||||
this.set({shown: true})
|
this.set({shown: true})
|
||||||
},
|
},
|
||||||
onClick(option) {
|
onClick(item) {
|
||||||
setPostPrivacy(this.get('realm'), option.key)
|
setPostPrivacy(this.get('realm'), item.key)
|
||||||
this.set({closed: true})
|
this.set({closed: true})
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -91,6 +33,14 @@
|
||||||
},
|
},
|
||||||
postPrivacyKey: (composeData, $currentVerifyCredentials) => {
|
postPrivacyKey: (composeData, $currentVerifyCredentials) => {
|
||||||
return composeData.postPrivacy || $currentVerifyCredentials.source.privacy
|
return composeData.postPrivacy || $currentVerifyCredentials.source.privacy
|
||||||
|
},
|
||||||
|
items: (postPrivacy, postPrivacyOptions) => {
|
||||||
|
return postPrivacyOptions.map(option => ({
|
||||||
|
key: option.key,
|
||||||
|
label: option.label,
|
||||||
|
icon: option.icon,
|
||||||
|
selected: postPrivacy.key === option.key
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<ModalDialog :label :shown :closed :title background="var(--main-bg)">
|
||||||
|
<GenericDialogList :items on:click="onClick(event)"/>
|
||||||
|
</ModalDialog>
|
||||||
|
<script>
|
||||||
|
import ModalDialog from './ModalDialog.html'
|
||||||
|
import { store } from '../../_store/store'
|
||||||
|
import GenericDialogList from './GenericDialogList.html'
|
||||||
|
import { setAccountFollowed } from '../../_actions/follow'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
computed: {
|
||||||
|
relationship: ($currentAccountRelationship) => $currentAccountRelationship,
|
||||||
|
account: ($currentAccountProfile) => $currentAccountProfile,
|
||||||
|
following: (relationship) => relationship && !!relationship.following,
|
||||||
|
accountName: (account) => account && (account.display_name || account.acct),
|
||||||
|
accountId: (account) => account && account.id,
|
||||||
|
followLabel: (following, accountName) => {
|
||||||
|
if (typeof following === 'undefined' || !accountName) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return following ? `Unfollow ${accountName}` : `Follow ${accountName}`
|
||||||
|
},
|
||||||
|
items: (followLabel, following) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
key: 'follow',
|
||||||
|
label: followLabel,
|
||||||
|
icon: following ? '#fa-user-times' : '#fa-user-plus'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
ModalDialog,
|
||||||
|
GenericDialogList
|
||||||
|
},
|
||||||
|
store: () => store,
|
||||||
|
methods: {
|
||||||
|
async show() {
|
||||||
|
this.set({shown: true})
|
||||||
|
},
|
||||||
|
async onClick(item) {
|
||||||
|
if (item.key === 'follow') {
|
||||||
|
let accountId = this.get('accountId')
|
||||||
|
let following = this.get('following')
|
||||||
|
await setAccountFollowed(accountId, !following, true)
|
||||||
|
this.set({closed: true})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -3,3 +3,4 @@ export * from './showImageDialog'
|
||||||
export * from './showVideoDialog'
|
export * from './showVideoDialog'
|
||||||
export * from './showEmojiDialog'
|
export * from './showEmojiDialog'
|
||||||
export * from './showPostPrivacyDialog'
|
export * from './showPostPrivacyDialog'
|
||||||
|
export * from './showStatusOptionsDialog'
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import StatusOptionsDialog from './StatusOptionsDialog.html'
|
||||||
|
|
||||||
|
export function showStatusOptionsDialog (statusId) {
|
||||||
|
let dialog = new StatusOptionsDialog({
|
||||||
|
target: document.getElementById('modal-dialog'),
|
||||||
|
data: {
|
||||||
|
label: 'Status options dialog',
|
||||||
|
title: 'Status options',
|
||||||
|
statusId: statusId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
dialog.show()
|
||||||
|
}
|
|
@ -4,7 +4,6 @@
|
||||||
href="#fa-reply"
|
href="#fa-reply"
|
||||||
disabled="{{disableReply}}"
|
disabled="{{disableReply}}"
|
||||||
delegateKey="{{replyKey}}"
|
delegateKey="{{replyKey}}"
|
||||||
ref:replyNode
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
label="{{reblogLabel}}"
|
label="{{reblogLabel}}"
|
||||||
|
@ -13,7 +12,6 @@
|
||||||
disabled="{{reblogDisabled}}"
|
disabled="{{reblogDisabled}}"
|
||||||
href="{{reblogIcon}}"
|
href="{{reblogIcon}}"
|
||||||
delegateKey="{{reblogKey}}"
|
delegateKey="{{reblogKey}}"
|
||||||
ref:reblogNode
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
label="Favorite"
|
label="Favorite"
|
||||||
|
@ -21,11 +19,11 @@
|
||||||
pressed="{{favorited}}"
|
pressed="{{favorited}}"
|
||||||
href="#fa-star"
|
href="#fa-star"
|
||||||
delegateKey="{{favoriteKey}}"
|
delegateKey="{{favoriteKey}}"
|
||||||
ref:favoriteNode
|
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
label="Show more actions"
|
label="Show more options"
|
||||||
href="#fa-ellipsis-h"
|
href="#fa-ellipsis-h"
|
||||||
|
delegateKey="{{optionsKey}}"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<style>
|
<style>
|
||||||
|
@ -45,17 +43,21 @@
|
||||||
import { setFavorited } from '../../_actions/favorite'
|
import { setFavorited } from '../../_actions/favorite'
|
||||||
import { setReblogged } from '../../_actions/reblog'
|
import { setReblogged } from '../../_actions/reblog'
|
||||||
import { goto } from 'sapper/runtime.js'
|
import { goto } from 'sapper/runtime.js'
|
||||||
|
import { importDialogs } from '../../_utils/asyncModules'
|
||||||
|
import { updateProfileAndRelationship } from '../../_actions/accounts'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
oncreate() {
|
oncreate() {
|
||||||
registerClickDelegate(this.get('favoriteKey'), () => this.onFavoriteClick())
|
registerClickDelegate(this.get('favoriteKey'), () => this.onFavoriteClick())
|
||||||
registerClickDelegate(this.get('reblogKey'), () => this.onReblogClick())
|
registerClickDelegate(this.get('reblogKey'), () => this.onReblogClick())
|
||||||
registerClickDelegate(this.get('replyKey'), () => this.onReplyClick())
|
registerClickDelegate(this.get('replyKey'), () => this.onReplyClick())
|
||||||
|
registerClickDelegate(this.get('optionsKey'), () => this.onOptionsClick())
|
||||||
},
|
},
|
||||||
ondestroy() {
|
ondestroy() {
|
||||||
unregisterClickDelegate(this.get('favoriteKey'))
|
unregisterClickDelegate(this.get('favoriteKey'))
|
||||||
unregisterClickDelegate(this.get('reblogKey'))
|
unregisterClickDelegate(this.get('reblogKey'))
|
||||||
unregisterClickDelegate(this.get('replyKey'))
|
unregisterClickDelegate(this.get('replyKey'))
|
||||||
|
unregisterClickDelegate(this.get('optionsKey'))
|
||||||
},
|
},
|
||||||
components: {
|
components: {
|
||||||
IconButton
|
IconButton
|
||||||
|
@ -75,6 +77,14 @@
|
||||||
onReplyClick() {
|
onReplyClick() {
|
||||||
let statusId = this.get('statusId')
|
let statusId = this.get('statusId')
|
||||||
goto(`/statuses/${statusId}/reply`)
|
goto(`/statuses/${statusId}/reply`)
|
||||||
|
},
|
||||||
|
async onOptionsClick() {
|
||||||
|
let statusId = this.get('statusId')
|
||||||
|
let accountId = this.get('accountId')
|
||||||
|
let updateRelationshipPromise = updateProfileAndRelationship(accountId)
|
||||||
|
let dialogs = await importDialogs()
|
||||||
|
await updateRelationshipPromise
|
||||||
|
dialogs.showStatusOptionsDialog(statusId)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -115,10 +125,13 @@
|
||||||
return status.favourited
|
return status.favourited
|
||||||
},
|
},
|
||||||
statusId: (status) => status.id,
|
statusId: (status) => status.id,
|
||||||
|
accountId: (status) => status.account.id,
|
||||||
disableReply: (statusId, timelineType, timelineValue) => timelineType === 'reply' && statusId === timelineValue,
|
disableReply: (statusId, timelineType, timelineValue) => timelineType === 'reply' && statusId === timelineValue,
|
||||||
favoriteKey: (statusId, timelineType, timelineValue) => `fav-${timelineType}-${timelineValue}-${statusId}`,
|
keyPrefix: (timelineType, timelineValue, statusId) => `${timelineType}-${timelineValue}-${statusId}`,
|
||||||
reblogKey: (statusId, timelineType, timelineValue) => `reblog-${timelineType}-${timelineValue}-${statusId}`,
|
favoriteKey: (keyPrefix) => `${keyPrefix}-fav`,
|
||||||
replyKey: (statusId, timelineType, timelineValue) => `reply-${timelineType}-${timelineValue}-${statusId}`,
|
reblogKey: (keyPrefix) => `${keyPrefix}-reblog`,
|
||||||
|
replyKey: (keyPrefix) => `${keyPrefix}-reply`,
|
||||||
|
optionsKey: (keyPrefix) => `${keyPrefix}-options`,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
|
@ -8,9 +8,9 @@ test('Changes post privacy', async t => {
|
||||||
await t.useRole(foobarRole)
|
await t.useRole(foobarRole)
|
||||||
.expect(postPrivacyButton.getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
|
.expect(postPrivacyButton.getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
|
||||||
.click(postPrivacyButton)
|
.click(postPrivacyButton)
|
||||||
.click('.post-privacy li:nth-child(2) button')
|
.click('.generic-dialog-list li:nth-child(2) button')
|
||||||
.expect(postPrivacyButton.getAttribute('aria-label')).eql('Adjust privacy (currently Unlisted)')
|
.expect(postPrivacyButton.getAttribute('aria-label')).eql('Adjust privacy (currently Unlisted)')
|
||||||
.click(postPrivacyButton)
|
.click(postPrivacyButton)
|
||||||
.click('.post-privacy li:nth-child(1) button')
|
.click('.generic-dialog-list li:nth-child(1) button')
|
||||||
.expect(postPrivacyButton.getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
|
.expect(postPrivacyButton.getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in New Issue