more options button to follow/unfollow

This commit is contained in:
Nolan Lawson 2018-03-11 19:40:32 -07:00
parent 92176df3ab
commit c13771c3a0
8 changed files with 164 additions and 75 deletions

View file

@ -3,7 +3,7 @@ import { followAccount, unfollowAccount } from '../_api/follow'
import { database } from '../_database/database'
import { toast } from '../_utils/toast'
export async function setAccountFollowed (accountId, follow) {
export async function setAccountFollowed (accountId, follow, toastOnSuccess) {
let instanceName = store.get('currentInstance')
let accessToken = store.get('accessToken')
try {
@ -15,6 +15,9 @@ export async function setAccountFollowed (accountId, follow) {
let relationship = await database.getRelationship(instanceName, accountId)
relationship.following = follow
await database.setRelationship(instanceName, relationship)
if (toastOnSuccess) {
toast.say(`${follow ? 'Followed' : 'Unfollowed'}`)
}
} catch (e) {
console.error(e)
toast.say(`Unable to ${follow ? 'follow' : 'unfollow'} account: ` + (e.message || ''))

View file

@ -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>

View file

@ -1,86 +1,28 @@
<ModalDialog :label :shown :closed :title background="var(--main-bg)">
<ul class="post-privacy">
{{#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>
<GenericDialogList :items on:click="onClick(event)" />
</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>
import ModalDialog from './ModalDialog.html'
import { store } from '../../_store/store'
import { POST_PRIVACY_OPTIONS } from '../../_static/statuses'
import { setPostPrivacy } from '../../_actions/postPrivacy'
import GenericDialogList from './GenericDialogList.html'
export default {
components: {
ModalDialog
ModalDialog,
GenericDialogList
},
store: () => store,
data: () => ({
postPrivacyOptions: POST_PRIVACY_OPTIONS
}),
helpers: {
isSelected: (option, postPrivacy) => postPrivacy.key === option.key
},
methods: {
async show() {
this.set({shown: true})
},
onClick(option) {
setPostPrivacy(this.get('realm'), option.key)
onClick(item) {
setPostPrivacy(this.get('realm'), item.key)
this.set({closed: true})
}
},
@ -91,6 +33,14 @@
},
postPrivacyKey: (composeData, $currentVerifyCredentials) => {
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
}))
}
}
}

View file

@ -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>

View file

@ -3,3 +3,4 @@ export * from './showImageDialog'
export * from './showVideoDialog'
export * from './showEmojiDialog'
export * from './showPostPrivacyDialog'
export * from './showStatusOptionsDialog'

View file

@ -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()
}

View file

@ -4,7 +4,6 @@
href="#fa-reply"
disabled="{{disableReply}}"
delegateKey="{{replyKey}}"
ref:replyNode
/>
<IconButton
label="{{reblogLabel}}"
@ -13,7 +12,6 @@
disabled="{{reblogDisabled}}"
href="{{reblogIcon}}"
delegateKey="{{reblogKey}}"
ref:reblogNode
/>
<IconButton
label="Favorite"
@ -21,11 +19,11 @@
pressed="{{favorited}}"
href="#fa-star"
delegateKey="{{favoriteKey}}"
ref:favoriteNode
/>
/>
<IconButton
label="Show more actions"
label="Show more options"
href="#fa-ellipsis-h"
delegateKey="{{optionsKey}}"
/>
</div>
<style>
@ -45,17 +43,21 @@
import { setFavorited } from '../../_actions/favorite'
import { setReblogged } from '../../_actions/reblog'
import { goto } from 'sapper/runtime.js'
import { importDialogs } from '../../_utils/asyncModules'
import { updateProfileAndRelationship } from '../../_actions/accounts'
export default {
oncreate() {
registerClickDelegate(this.get('favoriteKey'), () => this.onFavoriteClick())
registerClickDelegate(this.get('reblogKey'), () => this.onReblogClick())
registerClickDelegate(this.get('replyKey'), () => this.onReplyClick())
registerClickDelegate(this.get('optionsKey'), () => this.onOptionsClick())
},
ondestroy() {
unregisterClickDelegate(this.get('favoriteKey'))
unregisterClickDelegate(this.get('reblogKey'))
unregisterClickDelegate(this.get('replyKey'))
unregisterClickDelegate(this.get('optionsKey'))
},
components: {
IconButton
@ -75,6 +77,14 @@
onReplyClick() {
let statusId = this.get('statusId')
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: {
@ -115,10 +125,13 @@
return status.favourited
},
statusId: (status) => status.id,
accountId: (status) => status.account.id,
disableReply: (statusId, timelineType, timelineValue) => timelineType === 'reply' && statusId === timelineValue,
favoriteKey: (statusId, timelineType, timelineValue) => `fav-${timelineType}-${timelineValue}-${statusId}`,
reblogKey: (statusId, timelineType, timelineValue) => `reblog-${timelineType}-${timelineValue}-${statusId}`,
replyKey: (statusId, timelineType, timelineValue) => `reply-${timelineType}-${timelineValue}-${statusId}`,
keyPrefix: (timelineType, timelineValue, statusId) => `${timelineType}-${timelineValue}-${statusId}`,
favoriteKey: (keyPrefix) => `${keyPrefix}-fav`,
reblogKey: (keyPrefix) => `${keyPrefix}-reblog`,
replyKey: (keyPrefix) => `${keyPrefix}-reply`,
optionsKey: (keyPrefix) => `${keyPrefix}-options`,
}
}
</script>

View file

@ -8,9 +8,9 @@ test('Changes post privacy', async t => {
await t.useRole(foobarRole)
.expect(postPrivacyButton.getAttribute('aria-label')).eql('Adjust privacy (currently Public)')
.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)')
.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)')
})