simplify and refactor dialogs using event bus

This commit is contained in:
Nolan Lawson 2018-04-08 16:56:20 -07:00
parent bcc7fb47ef
commit eb8cd5f83d
26 changed files with 237 additions and 153 deletions

View file

@ -1,20 +1,27 @@
<ModalDialog
:id
:label
:shown
:closed
:title
background="var(--main-bg)"
on:destroyDialog="destroy()"
>
<GenericDialogList :items on:click="onClick(event)"/>
</ModalDialog>
<script>
import ModalDialog from './ModalDialog.html'
import { store } from '../../_store/store'
import { store } from '../../../_store/store'
import GenericDialogList from './GenericDialogList.html'
import { importDialogs } from '../../_utils/asyncModules'
import { importDialogs } from '../../../_utils/asyncModules'
import { createDialogId } from '../helpers/createDialogId'
import { show } from '../helpers/showDialog'
import { close } from '../helpers/closeDialog'
import { oncreate } from '../helpers/onCreateDialog'
export default {
oncreate,
store: () => store,
data: () => ({
id: createDialogId()
}),
computed: {
items: (account) => (
[
@ -26,15 +33,9 @@ export default {
]
)
},
components: {
ModalDialog,
GenericDialogList
},
store: () => store,
methods: {
async show() {
this.set({shown: true})
},
show,
close,
async onClick() {
let account = this.get('account')
this.store.setComposeData('dialog', {
@ -42,8 +43,12 @@ export default {
})
let dialogs = await importDialogs()
dialogs.showComposeDialog()
this.set({closed: true})
this.close()
}
}
},
components: {
ModalDialog,
GenericDialogList
},
}
</script>

View file

@ -1,41 +1,37 @@
<ModalDialog
:id
:label
:shown
:closed
:title
background="var(--main-bg)"
on:destroyDialog="destroy()"
>
<ComposeBox realm="dialog" size="slim" autoFocus="true" />
</ModalDialog>
<script>
import ModalDialog from './ModalDialog.html'
import ComposeBox from '../compose/ComposeBox.html'
import { on } from '../../_utils/eventBus'
import ComposeBox from '../../compose/ComposeBox.html'
import { on } from '../../../_utils/eventBus'
import { show } from '../helpers/showDialog'
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
import { close } from '../helpers/closeDialog'
export default {
oncreate() {
on('postedStatus', this, this.onPostedStatus)
},
components: {
ModalDialog,
ComposeBox
onCreateDialog.call(this)
},
methods: {
async show() {
this.set({shown: true})
},
show,
close,
onPostedStatus(realm) {
if (realm !== 'dialog') {
return
}
try {
this.set({closed: true})
} catch (e) {
// TODO: this seems to error sometimes, not sure why
console.error(e)
}
this.close()
}
},
components: {
ModalDialog,
ComposeBox
}
}
</script>

View file

@ -1,9 +1,7 @@
<ModalDialog
:id
:label
:shown
:closed background="var(--main-bg)"
on:close="onClose()"
on:destroyDialog="destroy()"
background="var(--main-bg)"
>
<form class="confirmation-dialog-form">
<p>
@ -36,16 +34,23 @@
</style>
<script>
import ModalDialog from './ModalDialog.html'
import { show } from '../helpers/showDialog'
import { close } from '../helpers/closeDialog'
import { on } from '../../../_utils/eventBus'
import { oncreate as onCreateDialog } from '../helpers/onCreateDialog'
export default {
components: {
ModalDialog
oncreate() {
on('destroyDialog', this, this.onDestroyDialog)
onCreateDialog.call(this)
},
methods: {
show() {
this.set({shown: true})
},
onClose() {
show,
close,
onDestroyDialog(id) {
if (id !== this.get('id')) {
return
}
if (this.get('positiveResult')) {
if (this.get('onPositive')) {
this.get('onPositive')()
@ -58,11 +63,14 @@
},
onPositive() {
this.set({positiveResult: true})
this.set({closed: true})
this.close()
},
onNegative() {
this.set({closed: true})
this.close()
}
},
components: {
ModalDialog
}
}
</script>

View file

@ -1,10 +1,8 @@
<ModalDialog
:id
:label
:shown
:closed
:title
background="var(--main-bg)"
on:destroyDialog="destroy()"
>
<div class="custom-emoji-container">
{{#if emojis.length}}
@ -60,10 +58,14 @@
</style>
<script>
import ModalDialog from './ModalDialog.html'
import { store } from '../../_store/store'
import { insertEmoji } from '../../_actions/emoji'
import { store } from '../../../_store/store'
import { insertEmoji } from '../../../_actions/emoji'
import { show } from '../helpers/showDialog'
import { close } from '../helpers/closeDialog'
import { oncreate } from '../helpers/onCreateDialog'
export default {
oncreate,
components: {
ModalDialog
},
@ -77,12 +79,11 @@
}
},
methods: {
async show() {
this.set({shown: true})
},
show,
close,
onClickEmoji(emoji) {
insertEmoji(this.get('realm'), emoji)
this.set({closed: true})
this.close()
}
}
}

View file

@ -1,9 +1,9 @@
<ModalDialog :label
:shown
background="var(--muted-modal-bg)"
muted="true"
className="image-modal-dialog"
on:destroyDialog="destroy()"
<ModalDialog
:id
:label
background="var(--muted-modal-bg)"
muted="true"
className="image-modal-dialog"
>
{{#if type === 'gifv'}}
<AutoplayVideo
@ -32,17 +32,18 @@
</style>
<script>
import ModalDialog from './ModalDialog.html'
import AutoplayVideo from '../AutoplayVideo.html'
import AutoplayVideo from '../../AutoplayVideo.html'
import { show } from '../helpers/showDialog'
import { oncreate } from '../helpers/onCreateDialog'
export default {
oncreate,
components: {
ModalDialog,
AutoplayVideo
},
methods: {
async show() {
this.set({shown: true})
}
show
}
}
</script>

View file

@ -116,10 +116,21 @@
</style>
<script>
import A11yDialog from 'a11y-dialog'
import { classname } from '../../_utils/classname'
import { classname } from '../../../_utils/classname'
import { on, emit } from '../../../_utils/eventBus'
export default {
oncreate() {
let dialogElement = this.refs.node.parentElement
this._a11yDialog = new A11yDialog(dialogElement)
this._a11yDialog.on('hide', () => {
this._a11yDialog.destroy()
emit('destroyDialog', this.get('id'))
requestAnimationFrame(() => document.body.removeChild(dialogElement))
})
on('showDialog', this, this.showDialog)
on('closeDialog', this, this.closeDialog)
},
data: () => ({
// don't animate if we're showing a modal dialog on top of another modal dialog. it looks ugly
shouldAnimate: !process.browser || document.getElementsByClassName('modal-dialog').length < 2
@ -142,31 +153,24 @@
)
}
},
oncreate() {
let dialogElement = this.refs.node.parentElement
let a11yDialog = new A11yDialog(dialogElement)
a11yDialog.on('hide', () => {
a11yDialog.destroy()
this.fire('close')
console.log('destroyDialog()')
this.fire('destroyDialog')
requestAnimationFrame(() => document.body.removeChild(dialogElement))
})
this.observe('shown', shown => {
if (shown) {
a11yDialog.show()
requestAnimationFrame(() => {
this.set({ fadedIn: true })
})
methods: {
showDialog(id) {
if (this.get('id') !== id) {
return
}
})
this.observe('closed', closed => {
if (closed) {
setTimeout(() => { // use setTimeout to workaround svelte timing issue
a11yDialog.hide()
}, 0)
this._a11yDialog.show()
requestAnimationFrame(() => {
this.set({ fadedIn: true })
})
},
closeDialog(id) {
if (this.get('id') !== id) {
return
}
})
setTimeout(() => { // use setTimeout to workaround svelte timing issue
this._a11yDialog.hide()
}, 0)
}
}
}
</script>

View file

@ -1,21 +1,23 @@
<ModalDialog
:id
:label
:shown
:closed
:title
background="var(--main-bg)"
on:destroyDialog="destroy()"
>
<GenericDialogList :items on:click="onClick(event)" />
</ModalDialog>
<script>
import ModalDialog from './ModalDialog.html'
import { store } from '../../_store/store'
import { POST_PRIVACY_OPTIONS } from '../../_static/statuses'
import { setPostPrivacy } from '../../_actions/postPrivacy'
import { store } from '../../../_store/store'
import { POST_PRIVACY_OPTIONS } from '../../../_static/statuses'
import { setPostPrivacy } from '../../../_actions/postPrivacy'
import GenericDialogList from './GenericDialogList.html'
import { show } from '../helpers/showDialog'
import { close } from '../helpers/closeDialog'
import { oncreate } from '../helpers/onCreateDialog'
export default {
oncreate,
components: {
ModalDialog,
GenericDialogList
@ -25,12 +27,11 @@
postPrivacyOptions: POST_PRIVACY_OPTIONS
}),
methods: {
async show() {
this.set({shown: true})
},
show,
close,
onClick(item) {
setPostPrivacy(this.get('realm'), item.key)
this.set({closed: true})
this.close()
}
},
computed: {

View file

@ -1,21 +1,23 @@
<ModalDialog
:id
:label
:shown
:closed
:title
background="var(--main-bg)"
on:destroyDialog="destroy()"
>
<GenericDialogList :items on:click="onClick(event)"/>
</ModalDialog>
<script>
import ModalDialog from './ModalDialog.html'
import { store } from '../../_store/store'
import { store } from '../../../_store/store'
import GenericDialogList from './GenericDialogList.html'
import { setAccountFollowed } from '../../_actions/follow'
import { doDeleteStatus } from '../../_actions/delete'
import { setAccountFollowed } from '../../../_actions/follow'
import { doDeleteStatus } from '../../../_actions/delete'
import { show } from '../helpers/showDialog'
import { close } from '../helpers/closeDialog'
import { oncreate } from '../helpers/onCreateDialog'
export default {
oncreate,
computed: {
relationship: ($currentAccountRelationship) => $currentAccountRelationship,
account: ($currentAccountProfile) => $currentAccountProfile,
@ -55,19 +57,18 @@ export default {
},
store: () => store,
methods: {
async show() {
this.set({shown: true})
},
show,
close,
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})
this.close()
} else if (item.key === 'delete') {
let statusId = this.get('statusId')
await doDeleteStatus(statusId)
this.set({closed: true})
this.close()
}
}
}

View file

@ -1,15 +1,14 @@
<ModalDialog
:id
:label
:shown
background="var(--muted-modal-bg)"
muted="true"
className="video-modal-dialog"
on:destroyDialog="destroy()"
>
<video poster="{{poster}}"
src="{{src}}"
width="{{width}}"
height="{{height}}"
<video :poster
:src
:width
:height
aria-label="Video: {{description || ''}}"
controls
/>
@ -24,15 +23,16 @@
</style>
<script>
import ModalDialog from './ModalDialog.html'
import { show } from '../helpers/showDialog'
import { oncreate } from '../helpers/onCreateDialog'
export default {
oncreate,
components: {
ModalDialog
},
methods: {
async show() {
this.set({shown: true})
}
show
}
}
</script>

View file

@ -1,10 +1,12 @@
import AccountProfileOptionsDialog from './AccountProfileOptionsDialog.html'
import { createDialogElement } from './createDialogElement'
import AccountProfileOptionsDialog from '../components/AccountProfileOptionsDialog.html'
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'
export function showAccountProfileOptionsDialog (account) {
let dialog = new AccountProfileOptionsDialog({
target: createDialogElement(),
data: {
id: createDialogId(),
label: 'Profile options dialog',
title: '',
account: account

View file

@ -0,0 +1,14 @@
import ComposeDialog from '../components/ComposeDialog.html'
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'
export function showComposeDialog () {
let dialog = new ComposeDialog({
target: createDialogElement(),
data: {
id: createDialogId(),
label: 'Compose dialog'
}
})
dialog.show()
}

View file

@ -1,10 +1,12 @@
import ConfirmationDialog from './ConfirmationDialog.html'
import { createDialogElement } from './createDialogElement'
import ConfirmationDialog from '../components/ConfirmationDialog.html'
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'
export function showConfirmationDialog (options) {
let dialog = new ConfirmationDialog({
target: createDialogElement(),
data: Object.assign({
id: createDialogId(),
label: 'Confirmation dialog'
}, options)
})

View file

@ -1,10 +1,12 @@
import EmojiDialog from './EmojiDialog.html'
import { createDialogElement } from './createDialogElement'
import EmojiDialog from '../components/EmojiDialog.html'
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'
export function showEmojiDialog (realm) {
let emojiDialog = new EmojiDialog({
target: createDialogElement(),
data: {
id: createDialogId(),
label: 'Emoji dialog',
title: 'Custom emoji',
realm

View file

@ -1,10 +1,12 @@
import ImageDialog from './ImageDialog.html'
import { createDialogElement } from './createDialogElement'
import ImageDialog from '../components/ImageDialog.html'
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'
export function showImageDialog (poster, src, type, width, height, description) {
let imageDialog = new ImageDialog({
target: createDialogElement(),
data: {
id: createDialogId(),
label: 'Image dialog',
poster,
src,

View file

@ -1,10 +1,12 @@
import PostPrivacyDialog from './PostPrivacyDialog.html'
import { createDialogElement } from './createDialogElement'
import PostPrivacyDialog from '../components/PostPrivacyDialog.html'
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'
export function showPostPrivacyDialog (realm) {
let dialog = new PostPrivacyDialog({
target: createDialogElement(),
data: {
id: createDialogId(),
label: 'Post privacy dialog',
title: 'Post privacy',
realm: realm

View file

@ -1,10 +1,12 @@
import StatusOptionsDialog from './StatusOptionsDialog.html'
import { createDialogElement } from './createDialogElement'
import StatusOptionsDialog from '../components/StatusOptionsDialog.html'
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'
export function showStatusOptionsDialog (statusId) {
let dialog = new StatusOptionsDialog({
target: createDialogElement(),
data: {
id: createDialogId(),
label: 'Status options dialog',
title: '',
statusId: statusId

View file

@ -1,10 +1,12 @@
import VideoDialog from './VideoDialog.html'
import { createDialogElement } from './createDialogElement'
import VideoDialog from '../components/VideoDialog.html'
import { createDialogElement } from '../helpers/createDialogElement'
import { createDialogId } from '../helpers/createDialogId'
export function showVideoDialog (poster, src, width, height, description) {
let videoDialog = new VideoDialog({
target: createDialogElement(),
data: {
id: createDialogId(),
label: 'Video dialog',
poster,
src,

View file

@ -1,8 +1,8 @@
export * from './showConfirmationDialog'
export * from './showImageDialog'
export * from './showVideoDialog'
export * from './showEmojiDialog'
export * from './showPostPrivacyDialog'
export * from './showStatusOptionsDialog'
export * from './showComposeDialog'
export * from './showAccountProfileOptionsDialog'
export * from './creators/showConfirmationDialog'
export * from './creators/showImageDialog'
export * from './creators/showVideoDialog'
export * from './creators/showEmojiDialog'
export * from './creators/showPostPrivacyDialog'
export * from './creators/showStatusOptionsDialog'
export * from './creators/showComposeDialog'
export * from './creators/showAccountProfileOptionsDialog'

View file

@ -0,0 +1,6 @@
import { emit } from '../../../_utils/eventBus'
export function close () {
let id = this.get('id')
emit('closeDialog', id)
}

View file

@ -0,0 +1,5 @@
let count = -1
export function createDialogId () {
return ++count
}

View file

@ -0,0 +1,12 @@
import { on } from '../../../_utils/eventBus'
function onDestroy (id) {
if (this.get('id') !== id) {
return
}
this.destroy()
}
export function oncreate () {
on('destroyDialog', this, onDestroy)
}

View file

@ -0,0 +1,6 @@
import { emit } from '../../../_utils/eventBus'
export function show () {
let id = this.get('id')
emit('showDialog', id)
}

View file

@ -1,12 +0,0 @@
import ComposeDialog from './ComposeDialog.html'
import { createDialogElement } from './createDialogElement'
export function showComposeDialog () {
let dialog = new ComposeDialog({
target: createDialogElement(),
data: {
label: 'Compose dialog'
}
})
dialog.show()
}

View file

@ -3,6 +3,7 @@ import {
scrollContainerToTop
} from '../utils'
import { foobarRole } from '../roles'
import { Selector as $ } from 'testcafe'
fixture`108-compose-dialog.js`
.page`http://localhost:4002`
@ -27,3 +28,24 @@ test('can compose using a dialog', async t => {
.click(showMoreButton)
await t.expect(getNthStatus(0).innerText).contains('hello from the modal', {timeout: 20000})
})
test('can use emoji dialog within compose dialog', async t => {
await t.useRole(foobarRole)
await scrollToStatus(t, 15)
await t.expect(composeButton.getAttribute('aria-label')).eql('Compose')
await sleep(2000)
await t.click(composeButton)
.click(modalDialog.find('.compose-box-toolbar button:nth-child(1)'))
.click($('button img[title=":blobpats:"]'))
.expect(modalDialog.find('.compose-box-input').value).eql(':blobpats: ')
.click(modalDialog.find('.compose-box-button-compose'))
.expect(modalDialog.exists).notOk()
await sleep(5000)
await scrollToTopOfTimeline(t)
await t.hover(getNthStatus(0))
await scrollContainerToTop()
await t
.expect(showMoreButton.innerText).contains('Show 1 more')
.click(showMoreButton)
await t.expect(getNthStatus(0).find('img[alt=":blobpats:"]').exists).ok({timeout: 20000})
})