This commit is contained in:
Nolan Lawson 2018-03-03 14:51:48 -08:00
parent 66801fbc96
commit 3dc46791e9
17 changed files with 81 additions and 103 deletions

View file

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

View file

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

View file

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

View file

@ -2,5 +2,5 @@
import { store } from '../_store/store'
export function setPostPrivacy (realm, postPrivacyKey) {
store.setComposeData(realm, 'postPrivacy', postPrivacyKey)
store.setComposeData(realm, {postPrivacy: postPrivacyKey})
}

View file

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

View file

@ -16,8 +16,8 @@
export default {
store: () => store,
computed: {
disabled: ($rawComposeTextOverLimit, $rawComposeTextLength) => {
return $rawComposeTextOverLimit || $rawComposeTextLength === 0
disabled: (textOverLimit, textLength) => {
return textOverLimit || textLength === 0
}
}
}

View file

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

View file

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

View file

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

View file

@ -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">&times;</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>

View file

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

View file

@ -74,7 +74,7 @@
this.set({shown: true})
},
onClickEmoji(emoji) {
insertEmoji(emoji)
insertEmoji(this.get('realm'), emoji)
this.set({closed: true})
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -39,7 +39,6 @@ export const store = new PinaforeStore({
pinnedStatuses: {},
instanceInfos: {},
statusModifications: {},
rawComposeText: '',
customEmoji: {},
composeData: {},
verifyCredentials: {}