forked from cybrespace/pinafore
refactor
This commit is contained in:
parent
66801fbc96
commit
3dc46791e9
|
@ -17,13 +17,11 @@ export async function updateCustomEmojiForInstance (instanceName) {
|
|||
)
|
||||
}
|
||||
|
||||
export function insertEmoji (emoji) {
|
||||
export function insertEmoji (realm, emoji) {
|
||||
let idx = store.get('composeSelectionStart') || 0
|
||||
let oldText = store.get('rawComposeText') || ''
|
||||
let oldText = store.getComposeData(realm, 'text')
|
||||
let pre = oldText ? substring(oldText, 0, idx) : ''
|
||||
let post = oldText ? substring(oldText, idx) : ''
|
||||
let newText = `${pre}:${emoji.shortcode}: ${post}`
|
||||
store.set({
|
||||
rawComposeText: newText
|
||||
})
|
||||
store.setComposeData(realm, {text: newText})
|
||||
}
|
||||
|
|
|
@ -22,8 +22,7 @@ export function switchToInstance (instanceName) {
|
|||
store.set({
|
||||
currentInstance: instanceName,
|
||||
searchResults: null,
|
||||
queryInSearch: '',
|
||||
rawComposeText: ''
|
||||
queryInSearch: ''
|
||||
})
|
||||
store.save()
|
||||
switchToTheme(instanceThemes[instanceName])
|
||||
|
@ -49,7 +48,6 @@ export async function logOutOfInstance (instanceName) {
|
|||
currentInstance: newInstance,
|
||||
searchResults: null,
|
||||
queryInSearch: '',
|
||||
rawComposeText: '',
|
||||
composeData: composeData
|
||||
})
|
||||
store.save()
|
||||
|
|
|
@ -14,10 +14,12 @@ export async function doMediaUpload (realm, file) {
|
|||
data: response,
|
||||
file: { name: file.name }
|
||||
})
|
||||
let rawComposeText = store.get('rawComposeText') || ''
|
||||
rawComposeText += ' ' + response.text_url
|
||||
store.setComposeData(realm, 'media', composeMedia)
|
||||
store.set({rawComposeText})
|
||||
let composeText = store.getComposeData(realm, 'text') || ''
|
||||
composeText += ' ' + response.text_url
|
||||
store.setComposeData(realm, {
|
||||
media: composeMedia,
|
||||
text: composeText
|
||||
})
|
||||
scheduleIdleTask(() => store.save())
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
|
@ -31,11 +33,12 @@ export function deleteMedia (realm, i) {
|
|||
let composeMedia = store.getComposeData(realm, 'media')
|
||||
let deletedMedia = composeMedia.splice(i, 1)[0]
|
||||
|
||||
let rawComposeText = store.get('rawComposeText') || ''
|
||||
let composeText = store.getComposeData(realm, 'text') || ''
|
||||
composeText = composeText.replace(' ' + deletedMedia.data.text_url, '')
|
||||
|
||||
rawComposeText = rawComposeText.replace(' ' + deletedMedia.data.text_url, '')
|
||||
|
||||
store.setComposeData(realm, 'media', composeMedia)
|
||||
store.set({rawComposeText})
|
||||
store.setComposeData(realm, {
|
||||
media: composeMedia,
|
||||
text: composeText
|
||||
})
|
||||
scheduleIdleTask(() => store.save())
|
||||
}
|
||||
|
|
|
@ -2,5 +2,5 @@
|
|||
import { store } from '../_store/store'
|
||||
|
||||
export function setPostPrivacy (realm, postPrivacyKey) {
|
||||
store.setComposeData(realm, 'postPrivacy', postPrivacyKey)
|
||||
store.setComposeData(realm, {postPrivacy: postPrivacyKey})
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<div class="compose-box {{overLimit ? 'over-char-limit' : ''}}">
|
||||
<ComposeAuthor />
|
||||
<ComposeInput :realm />
|
||||
<ComposeLengthGauge />
|
||||
<ComposeToolbar :realm />
|
||||
<ComposeLengthIndicator />
|
||||
<ComposeMedia :realm />
|
||||
<ComposeButton />
|
||||
<ComposeInput :realm :text />
|
||||
<ComposeLengthGauge :textLength :textOverLimit />
|
||||
<ComposeToolbar :realm :postPrivacy :media />
|
||||
<ComposeLengthIndicator :textLength :textOverLimit />
|
||||
<ComposeMedia :realm :media />
|
||||
<ComposeButton :textLength :textOverLimit />
|
||||
</div>
|
||||
<style>
|
||||
.compose-box {
|
||||
|
@ -42,6 +42,9 @@
|
|||
import ComposeInput from './ComposeInput.html'
|
||||
import ComposeButton from './ComposeButton.html'
|
||||
import ComposeMedia from './ComposeMedia.html'
|
||||
import { measureText } from '../../_utils/measureText'
|
||||
import { CHAR_LIMIT, POST_PRIVACY_OPTIONS } from '../../_static/statuses'
|
||||
import { store } from '../../_store/store'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -52,6 +55,17 @@
|
|||
ComposeInput,
|
||||
ComposeButton,
|
||||
ComposeMedia
|
||||
},
|
||||
store: () => store,
|
||||
computed: {
|
||||
composeData: ($currentComposeData, realm) => $currentComposeData[realm] || {},
|
||||
text: (composeData) => composeData.text || '',
|
||||
media: (composeData) => composeData.media || [],
|
||||
postPrivacy: (postPrivacyKey) => POST_PRIVACY_OPTIONS.find(_ => _.key === postPrivacyKey),
|
||||
defaultPostPrivacyKey: ($currentVerifyCredentials) => $currentVerifyCredentials.source.privacy,
|
||||
postPrivacyKey: (composeData, defaultPostPrivacyKey) => composeData.postPrivacy || defaultPostPrivacyKey,
|
||||
textLength: (text) => measureText(text),
|
||||
textOverLimit: (textLength) => textLength > CHAR_LIMIT
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
export default {
|
||||
store: () => store,
|
||||
computed: {
|
||||
disabled: ($rawComposeTextOverLimit, $rawComposeTextLength) => {
|
||||
return $rawComposeTextOverLimit || $rawComposeTextLength === 0
|
||||
disabled: (textOverLimit, textLength) => {
|
||||
return textOverLimit || textLength === 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
class="compose-box-input"
|
||||
placeholder="What's on your mind?"
|
||||
ref:textarea
|
||||
bind:value=$rawComposeText
|
||||
bind:value=rawText
|
||||
on:blur="onBlur()"
|
||||
></textarea>
|
||||
<style>
|
||||
|
@ -41,17 +41,16 @@
|
|||
},
|
||||
methods: {
|
||||
setupSyncFromStore() {
|
||||
let composeText = this.get('composeText')
|
||||
this.store.set({
|
||||
rawComposeText: composeText
|
||||
this.observe('text', text => {
|
||||
this.set({rawText: text})
|
||||
})
|
||||
},
|
||||
setupSyncToStore() {
|
||||
const saveText = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
|
||||
|
||||
this.observe('rawComposeText', rawComposeText => {
|
||||
this.observe('rawText', rawText => {
|
||||
let realm = this.get('realm')
|
||||
this.store.setComposeData(realm, 'text', rawComposeText)
|
||||
this.store.setComposeData(realm, {text: rawText})
|
||||
saveText()
|
||||
}, {init: false})
|
||||
},
|
||||
|
@ -72,10 +71,8 @@
|
|||
}
|
||||
},
|
||||
store: () => store,
|
||||
computed: {
|
||||
rawComposeText: ($rawComposeText) => $rawComposeText,
|
||||
composeData: ($currentComposeData, realm) => $currentComposeData[realm] || {},
|
||||
composeText: (composeData) => composeData.text || ''
|
||||
}
|
||||
data: () => ({
|
||||
rawText: ''
|
||||
})
|
||||
}
|
||||
</script>
|
|
@ -1,4 +1,4 @@
|
|||
<div class="compose-box-length-gauge {{shouldAnimate ? 'should-animate' : ''}} {{$rawComposeTextOverLimit ? 'over-char-limit' : ''}}"
|
||||
<div class="compose-box-length-gauge {{shouldAnimate ? 'should-animate' : ''}} {{textOverLimit ? 'over-char-limit' : ''}}"
|
||||
style="transform: scaleX({{inputLengthAsFractionRoundedAfterRaf || 0}});"
|
||||
aria-hidden="true"
|
||||
></div>
|
||||
|
@ -36,12 +36,12 @@
|
|||
},
|
||||
store: () => store,
|
||||
computed: {
|
||||
inputLengthAsFraction: ($rawComposeTextLength) => {
|
||||
return Math.min(CHAR_LIMIT, $rawComposeTextLength) / CHAR_LIMIT
|
||||
lengthAsFraction: (textLength) => {
|
||||
return Math.min(CHAR_LIMIT, textLength) / CHAR_LIMIT
|
||||
},
|
||||
inputLengthAsFractionRounded: (inputLengthAsFraction) => {
|
||||
lengthAsFractionRounded: (lengthAsFraction) => {
|
||||
// We don't need to update the gauge for every decimal point, so round it to the nearest 0.02
|
||||
let int = Math.round(inputLengthAsFraction * 100)
|
||||
let int = Math.round(lengthAsFraction * 100)
|
||||
int -= (int % 2)
|
||||
return int / 100
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<span class="compose-box-length {{$rawComposeTextOverLimit ? 'over-char-limit' : ''}}"
|
||||
aria-label="{{inputLengthLabel}}">
|
||||
{{inputLengthToDisplayAfterRaf || '0'}}
|
||||
<span class="compose-box-length {{textOverLimit ? 'over-char-limit' : ''}}"
|
||||
aria-label="{{lengthLabel}}">
|
||||
{{lengthToDisplayAfterRaf || '0'}}
|
||||
</span>
|
||||
<style>
|
||||
.compose-box-length {
|
||||
|
@ -23,24 +23,24 @@
|
|||
export default {
|
||||
oncreate() {
|
||||
// perf improvement for keyboard input latency
|
||||
this.observe('inputLengthToDisplay', inputLengthToDisplay => {
|
||||
this.observe('lengthToDisplay', lengthToDisplay => {
|
||||
requestAnimationFrame(() => {
|
||||
mark('set inputLengthToDisplayAfterRaf')
|
||||
this.set({inputLengthToDisplayAfterRaf: inputLengthToDisplay})
|
||||
stop('set inputLengthToDisplayAfterRaf')
|
||||
mark('set lengthToDisplayAfterRaf')
|
||||
this.set({lengthToDisplayAfterRaf: lengthToDisplay})
|
||||
stop('set lengthToDisplayAfterRaf')
|
||||
})
|
||||
})
|
||||
},
|
||||
store: () => store,
|
||||
computed: {
|
||||
inputLengthToDisplay: ($rawComposeTextLength) => {
|
||||
return CHAR_LIMIT - $rawComposeTextLength
|
||||
lengthToDisplay: (textLength) => {
|
||||
return CHAR_LIMIT - textLength
|
||||
},
|
||||
inputLengthLabel: ($rawComposeTextOverLimit, inputLengthToDisplay) => {
|
||||
if ($rawComposeTextOverLimit) {
|
||||
return `${inputLengthToDisplay} characters over limit`
|
||||
lengthLabel: (textOverLimit, lengthToDisplay) => {
|
||||
if (textOverLimit) {
|
||||
return `${lengthToDisplay} characters over limit`
|
||||
} else {
|
||||
return `${inputLengthToDisplay} characters remaining`
|
||||
return `${lengthToDisplay} characters remaining`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
{{#if composeMedia.length}}
|
||||
<div class="compose-media-container" style="grid-template-columns: repeat({{composeMedia.length}}, 1fr);">
|
||||
{{#each composeMedia as media, i}}
|
||||
{{#if media.length}}
|
||||
<div class="compose-media-container" style="grid-template-columns: repeat({{media.length}}, 1fr);">
|
||||
{{#each media as mediaItem, i}}
|
||||
<div class="compose-media">
|
||||
<img src="{{media.data.preview_url}}" alt="{{media.file.name}}"/>
|
||||
<img src="{{mediaItem.data.preview_url}}" alt="{{mediaItem.file.name}}"/>
|
||||
<div class="compose-media-delete">
|
||||
<button class="compose-media-delete-button"
|
||||
data-a11y-dialog-hide aria-label="Delete {{media.file.name}}"
|
||||
on:click="onDeleteMedia(i)"
|
||||
>
|
||||
data-a11y-dialog-hide aria-label="Delete {{mediaItem.file.name}}"
|
||||
on:click="onDeleteMedia(i)" >
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="compose-media-alt">
|
||||
<input type="text"
|
||||
placeholder="Description"
|
||||
aria-label="Describe {{media.file.name}} for the visually impaired">
|
||||
aria-label="Describe {{mediaItem.file.name}} for the visually impaired">
|
||||
</div>
|
||||
</div>
|
||||
{{/each}}
|
||||
|
@ -98,10 +97,6 @@
|
|||
onDeleteMedia(i) {
|
||||
deleteMedia(this.get('realm'), i)
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
composeData: ($currentComposeData, realm) => $currentComposeData[realm] || {},
|
||||
composeMedia: (composeData) => composeData.media || []
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -9,7 +9,7 @@
|
|||
label="Add media"
|
||||
href="{{$uploadingMedia ? '#fa-spinner' : '#fa-camera'}}"
|
||||
on:click="onMediaClick()"
|
||||
disabled="{{$uploadingMedia || (composeMedia.length === 4)}}"
|
||||
disabled="{{$uploadingMedia || (media.length === 4)}}"
|
||||
/>
|
||||
<IconButton
|
||||
label="Adjust privacy (currently {{postPrivacy.label}})"
|
||||
|
@ -62,7 +62,7 @@
|
|||
async onEmojiClick() {
|
||||
/* no await */ updateCustomEmojiForInstance(this.store.get('currentInstance'))
|
||||
let dialogs = await importDialogs()
|
||||
dialogs.showEmojiDialog()
|
||||
dialogs.showEmojiDialog(this.get('realm'))
|
||||
},
|
||||
onMediaClick() {
|
||||
this.refs.input.click()
|
||||
|
@ -76,16 +76,6 @@
|
|||
let dialogs = await importDialogs()
|
||||
dialogs.showPostPrivacyDialog(this.get('realm'))
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
composeData: ($currentComposeData, realm) => $currentComposeData[realm] || {},
|
||||
composeMedia: (composeData) => composeData.media || [],
|
||||
postPrivacy: (postPrivacyKey) => {
|
||||
return POST_PRIVACY_OPTIONS.find(_ => _.key === postPrivacyKey)
|
||||
},
|
||||
postPrivacyKey: (composeData, $currentVerifyCredentials) => {
|
||||
return composeData.postPrivacy || $currentVerifyCredentials.source.privacy
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -74,7 +74,7 @@
|
|||
this.set({shown: true})
|
||||
},
|
||||
onClickEmoji(emoji) {
|
||||
insertEmoji(emoji)
|
||||
insertEmoji(this.get('realm'), emoji)
|
||||
this.set({closed: true})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
import EmojiDialog from './EmojiDialog.html'
|
||||
|
||||
export function showEmojiDialog () {
|
||||
export function showEmojiDialog (realm) {
|
||||
let emojiDialog = new EmojiDialog({
|
||||
target: document.getElementById('modal-dialog'),
|
||||
data: {
|
||||
label: 'Emoji dialog',
|
||||
title: 'Custom emoji'
|
||||
title: 'Custom emoji',
|
||||
realm
|
||||
}
|
||||
})
|
||||
emojiDialog.show()
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
import { instanceComputations } from './instanceComputations'
|
||||
import { timelineComputations } from './timelineComputations'
|
||||
import { statusComputations } from './statusComputations'
|
||||
|
||||
export function computations (store) {
|
||||
instanceComputations(store)
|
||||
timelineComputations(store)
|
||||
statusComputations(store)
|
||||
}
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
import { CHAR_LIMIT } from '../../_static/statuses'
|
||||
import { measureText } from '../../_utils/measureText'
|
||||
|
||||
export function statusComputations (store) {
|
||||
store.compute('rawComposeTextLength',
|
||||
['rawComposeText'],
|
||||
(rawComposeText) => measureText(rawComposeText)
|
||||
)
|
||||
|
||||
store.compute('rawComposeTextOverLimit',
|
||||
['rawComposeTextLength'],
|
||||
(rawComposeTextLength) => rawComposeTextLength > CHAR_LIMIT
|
||||
)
|
||||
}
|
|
@ -1,10 +1,9 @@
|
|||
export function instanceMixins (Store) {
|
||||
Store.prototype.setComposeData = function (realm, key, value) {
|
||||
Store.prototype.setComposeData = function (realm, obj) {
|
||||
let composeData = this.get('composeData')
|
||||
let instanceName = this.get('currentInstance')
|
||||
composeData[instanceName] = composeData[instanceName] || {}
|
||||
composeData[instanceName][realm] = composeData[instanceName][realm] || {}
|
||||
composeData[instanceName][realm][key] = value
|
||||
let instanceNameData = composeData[instanceName] = composeData[instanceName] || {}
|
||||
instanceNameData[realm] = Object.assign(instanceNameData[realm] || {}, obj)
|
||||
this.set({composeData})
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,6 @@ export const store = new PinaforeStore({
|
|||
pinnedStatuses: {},
|
||||
instanceInfos: {},
|
||||
statusModifications: {},
|
||||
rawComposeText: '',
|
||||
customEmoji: {},
|
||||
composeData: {},
|
||||
verifyCredentials: {}
|
||||
|
|
Loading…
Reference in New Issue