further refactor ComposeBox
This commit is contained in:
		
							parent
							
								
									c1e64711c0
								
							
						
					
					
						commit
						333ac62b61
					
				
					 12 changed files with 141 additions and 89 deletions
				
			
		| 
						 | 
				
			
			@ -27,5 +27,5 @@ module.exports = [
 | 
			
		|||
  {id: 'fa-ban', src: 'node_modules/font-awesome-svg-png/white/svg/ban.svg', title: 'Ban'},
 | 
			
		||||
  {id: 'fa-camera', src: 'node_modules/font-awesome-svg-png/white/svg/camera.svg', title: 'Camera'},
 | 
			
		||||
  {id: 'fa-smile', src: 'node_modules/font-awesome-svg-png/white/svg/smile-o.svg', title: 'Smile'},
 | 
			
		||||
  {id: 'fa-exclamation-triangle', src: 'node_modules/font-awesome-svg-png/white/svg/exclamation-triangle.svg', title: 'Warning'},
 | 
			
		||||
  {id: 'fa-exclamation-triangle', src: 'node_modules/font-awesome-svg-png/white/svg/exclamation-triangle.svg', title: 'Warning'}
 | 
			
		||||
]
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,7 +19,12 @@ export function changeTheme (instanceName, newTheme) {
 | 
			
		|||
 | 
			
		||||
export function switchToInstance (instanceName) {
 | 
			
		||||
  let instanceThemes = store.get('instanceThemes')
 | 
			
		||||
  store.set({currentInstance: instanceName})
 | 
			
		||||
  store.set({
 | 
			
		||||
    currentInstance: instanceName,
 | 
			
		||||
    searchResults: null,
 | 
			
		||||
    queryInSearch: '',
 | 
			
		||||
    rawInputTextInCompose: ''
 | 
			
		||||
  })
 | 
			
		||||
  store.save()
 | 
			
		||||
  switchToTheme(instanceThemes[instanceName])
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +46,8 @@ export async function logOutOfInstance (instanceName) {
 | 
			
		|||
    loggedInInstancesInOrder: loggedInInstancesInOrder,
 | 
			
		||||
    currentInstance: newInstance,
 | 
			
		||||
    searchResults: null,
 | 
			
		||||
    queryInSearch: ''
 | 
			
		||||
    queryInSearch: '',
 | 
			
		||||
    rawInputTextInCompose: ''
 | 
			
		||||
  })
 | 
			
		||||
  store.save()
 | 
			
		||||
  toast.say(`Logged out of ${instanceName}`)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,17 +1,10 @@
 | 
			
		|||
<div class="compose-box {{overLimit ? 'over-char-limit' : ''}}">
 | 
			
		||||
  <ComposeAuthor :verifyCredentials />
 | 
			
		||||
  <textarea
 | 
			
		||||
    class="compose-box-input"
 | 
			
		||||
    placeholder="What's on your mind?"
 | 
			
		||||
    ref:textarea
 | 
			
		||||
    bind:value=inputText
 | 
			
		||||
  ></textarea>
 | 
			
		||||
  <ComposeLengthGauge :inputLength />
 | 
			
		||||
  <ComposeInput />
 | 
			
		||||
  <ComposeLengthGauge />
 | 
			
		||||
  <ComposeToolbar />
 | 
			
		||||
  <ComposeLengthIndicator :inputLength />
 | 
			
		||||
  <button class="primary compose-box-button">
 | 
			
		||||
    Toot!
 | 
			
		||||
  </button>
 | 
			
		||||
  <ComposeLengthIndicator />
 | 
			
		||||
  <ComposeButton />
 | 
			
		||||
</div>
 | 
			
		||||
<style>
 | 
			
		||||
  .compose-box {
 | 
			
		||||
| 
						 | 
				
			
			@ -31,30 +24,6 @@
 | 
			
		|||
    max-width: calc(100vw - 40px);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  :global(.compose-box-input) {
 | 
			
		||||
    grid-area: input;
 | 
			
		||||
    margin: 10px 0 0 5px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    border: 1px solid var(--input-border);
 | 
			
		||||
    min-height: 75px;
 | 
			
		||||
    resize: none;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
    /* Text must be at least 16px or else iOS Safari zooms in */
 | 
			
		||||
    font-size: 1.2em;
 | 
			
		||||
    /* Hack to make Edge stretch the element all the way to the right.
 | 
			
		||||
     * Also desktop Safari makes the gauge stretch too far to the right without it.
 | 
			
		||||
     */
 | 
			
		||||
    width: calc(100% - 5px);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  .compose-box-button {
 | 
			
		||||
    grid-area: button;
 | 
			
		||||
    justify-self: right;
 | 
			
		||||
    text-transform: uppercase;
 | 
			
		||||
    margin-top: 10px;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @media (max-width: 767px) {
 | 
			
		||||
    .compose-box {
 | 
			
		||||
      padding: 10px 10px;
 | 
			
		||||
| 
						 | 
				
			
			@ -62,56 +31,23 @@
 | 
			
		|||
      width: 580px;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
  import { store } from '../../_store/store'
 | 
			
		||||
  import { autosize } from '../../_utils/autosize'
 | 
			
		||||
  import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
 | 
			
		||||
  import debounce from 'lodash/debounce'
 | 
			
		||||
  import { mark, stop } from '../../_utils/marks'
 | 
			
		||||
  import ComposeToolbar from './ComposeToolbar.html'
 | 
			
		||||
  import ComposeLengthGauge from './ComposeLengthGauge.html'
 | 
			
		||||
  import ComposeLengthIndicator from './ComposeLengthIndicator.html'
 | 
			
		||||
  import ComposeAuthor from './ComposeAuthor.html'
 | 
			
		||||
  import ComposeInput from './ComposeInput.html'
 | 
			
		||||
  import ComposeButton from './ComposeButton.html'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    oncreate() {
 | 
			
		||||
      this.set({inputText: store.get('currentInputTextInCompose')})
 | 
			
		||||
 | 
			
		||||
      requestAnimationFrame(() => {
 | 
			
		||||
        mark('autosize()')
 | 
			
		||||
        autosize(this.refs.textarea)
 | 
			
		||||
        stop('autosize()')
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const saveText = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
 | 
			
		||||
      this.observe('inputText', inputText => {
 | 
			
		||||
        let inputTextInCompose = this.store.get('inputTextInCompose')
 | 
			
		||||
        let currentInstance = this.store.get('currentInstance')
 | 
			
		||||
        inputTextInCompose[currentInstance] = inputText || ''
 | 
			
		||||
        this.store.set({inputTextInCompose: inputTextInCompose})
 | 
			
		||||
        saveText()
 | 
			
		||||
      }, {init: false})
 | 
			
		||||
    },
 | 
			
		||||
    ondestroy() {
 | 
			
		||||
      mark('autosize.destroy()')
 | 
			
		||||
      autosize.destroy(this.refs.textarea)
 | 
			
		||||
      stop('autosize.destroy()')
 | 
			
		||||
    },
 | 
			
		||||
    data: () => ({
 | 
			
		||||
      inputText: ''
 | 
			
		||||
    }),
 | 
			
		||||
    components: {
 | 
			
		||||
      ComposeAuthor,
 | 
			
		||||
      ComposeToolbar,
 | 
			
		||||
      ComposeLengthGauge,
 | 
			
		||||
      ComposeLengthIndicator
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    computed: {
 | 
			
		||||
      currentInputTextInCompose: ($currentInputTextInCompose) => $currentInputTextInCompose,
 | 
			
		||||
      inputLength: (inputText) => inputText ? inputText.length : 0,
 | 
			
		||||
      ComposeLengthIndicator,
 | 
			
		||||
      ComposeInput,
 | 
			
		||||
      ComposeButton
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										24
									
								
								routes/_components/compose/ComposeButton.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								routes/_components/compose/ComposeButton.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,24 @@
 | 
			
		|||
<button class="primary compose-box-button"
 | 
			
		||||
        :disabled >
 | 
			
		||||
  Toot!
 | 
			
		||||
</button>
 | 
			
		||||
<style>
 | 
			
		||||
  .compose-box-button {
 | 
			
		||||
    grid-area: button;
 | 
			
		||||
    justify-self: right;
 | 
			
		||||
    text-transform: uppercase;
 | 
			
		||||
    margin-top: 10px;
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
  import { store } from '../../_store/store'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    computed: {
 | 
			
		||||
      disabled: ($rawInputTextInComposeOverLimit, $rawInputTextInComposeLength) => {
 | 
			
		||||
        return $rawInputTextInComposeOverLimit || $rawInputTextInComposeLength === 0
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
							
								
								
									
										62
									
								
								routes/_components/compose/ComposeInput.html
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								routes/_components/compose/ComposeInput.html
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,62 @@
 | 
			
		|||
<textarea
 | 
			
		||||
  class="compose-box-input"
 | 
			
		||||
  placeholder="What's on your mind?"
 | 
			
		||||
  ref:textarea
 | 
			
		||||
  bind:value=$rawInputTextInCompose
 | 
			
		||||
></textarea>
 | 
			
		||||
<style>
 | 
			
		||||
  .compose-box-input {
 | 
			
		||||
    grid-area: input;
 | 
			
		||||
    margin: 10px 0 0 5px;
 | 
			
		||||
    padding: 10px;
 | 
			
		||||
    border: 1px solid var(--input-border);
 | 
			
		||||
    min-height: 75px;
 | 
			
		||||
    resize: none;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
    /* Text must be at least 16px or else iOS Safari zooms in */
 | 
			
		||||
    font-size: 1.2em;
 | 
			
		||||
    /* Hack to make Edge stretch the element all the way to the right.
 | 
			
		||||
     * Also desktop Safari makes the gauge stretch too far to the right without it.
 | 
			
		||||
     */
 | 
			
		||||
    width: calc(100% - 5px);
 | 
			
		||||
  }
 | 
			
		||||
</style>
 | 
			
		||||
<script>
 | 
			
		||||
  import { store } from '../../_store/store'
 | 
			
		||||
  import { autosize } from '../../_utils/autosize'
 | 
			
		||||
  import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
 | 
			
		||||
  import debounce from 'lodash/debounce'
 | 
			
		||||
  import { mark, stop } from '../../_utils/marks'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    oncreate() {
 | 
			
		||||
      this.store.set({rawInputTextInCompose: store.get('currentInputTextInCompose')})
 | 
			
		||||
 | 
			
		||||
      requestAnimationFrame(() => {
 | 
			
		||||
        mark('autosize()')
 | 
			
		||||
        autosize(this.refs.textarea)
 | 
			
		||||
        stop('autosize()')
 | 
			
		||||
      })
 | 
			
		||||
 | 
			
		||||
      const saveText = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
 | 
			
		||||
      this.observe('rawInputTextInCompose', rawInputTextInCompose => {
 | 
			
		||||
        let inputTextInCompose = this.store.get('inputTextInCompose')
 | 
			
		||||
        let currentInstance = this.store.get('currentInstance')
 | 
			
		||||
        inputTextInCompose[currentInstance] = rawInputTextInCompose || ''
 | 
			
		||||
        this.store.set({inputTextInCompose: inputTextInCompose})
 | 
			
		||||
        saveText()
 | 
			
		||||
      }, {init: false})
 | 
			
		||||
    },
 | 
			
		||||
    ondestroy() {
 | 
			
		||||
      mark('autosize.destroy()')
 | 
			
		||||
      autosize.destroy(this.refs.textarea)
 | 
			
		||||
      stop('autosize.destroy()')
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    computed: {
 | 
			
		||||
      rawInputTextInCompose: ($rawInputTextInCompose) => $rawInputTextInCompose,
 | 
			
		||||
      currentInputTextInCompose: ($currentInputTextInCompose) => $currentInputTextInCompose,
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
</script>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
<div class="compose-box-length-gauge {{shouldAnimate ? 'should-animate' : ''}}  {{overLimit ? 'over-char-limit' : ''}}"
 | 
			
		||||
<div class="compose-box-length-gauge {{shouldAnimate ? 'should-animate' : ''}}  {{$rawInputTextInComposeOverLimit ? 'over-char-limit' : ''}}"
 | 
			
		||||
     style="transform: scaleX({{inputLengthAsFractionRoundedAfterRaf || 0}});"
 | 
			
		||||
     aria-hidden="true"
 | 
			
		||||
></div>
 | 
			
		||||
| 
						 | 
				
			
			@ -20,9 +20,11 @@
 | 
			
		|||
<script>
 | 
			
		||||
  import { CHAR_LIMIT } from '../../_static/statuses'
 | 
			
		||||
  import { mark, stop } from '../../_utils/marks'
 | 
			
		||||
  import { store } from '../../_store/store'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    oncreate() {
 | 
			
		||||
      // perf improvement for keyboard input latency
 | 
			
		||||
      this.observe('inputLengthAsFractionRounded', inputLengthAsFractionRounded => {
 | 
			
		||||
        requestAnimationFrame(() => {
 | 
			
		||||
          mark('set inputLengthAsFractionRoundedAfterRaf')
 | 
			
		||||
| 
						 | 
				
			
			@ -32,9 +34,11 @@
 | 
			
		|||
        })
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    computed: {
 | 
			
		||||
      overLimit: (inputLength) => inputLength > CHAR_LIMIT,
 | 
			
		||||
      inputLengthAsFraction: (inputLength) => (Math.min(CHAR_LIMIT, inputLength) / CHAR_LIMIT),
 | 
			
		||||
      inputLengthAsFraction: ($rawInputTextInComposeLength) => {
 | 
			
		||||
        return Math.min(CHAR_LIMIT, $rawInputTextInComposeLength) / CHAR_LIMIT
 | 
			
		||||
      },
 | 
			
		||||
      inputLengthAsFractionRounded: (inputLengthAsFraction) => {
 | 
			
		||||
        // 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)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
<span class="compose-box-length {{overLimit ? 'over-char-limit' : ''}}"
 | 
			
		||||
<span class="compose-box-length {{$rawInputTextInComposeOverLimit ? 'over-char-limit' : ''}}"
 | 
			
		||||
      aria-label="{{inputLengthLabel}}">
 | 
			
		||||
      {{inputLengthToDisplayAfterRaf || '0'}}
 | 
			
		||||
    </span>
 | 
			
		||||
| 
						 | 
				
			
			@ -18,10 +18,11 @@
 | 
			
		|||
<script>
 | 
			
		||||
  import { CHAR_LIMIT } from '../../_static/statuses'
 | 
			
		||||
  import { mark, stop } from '../../_utils/marks'
 | 
			
		||||
  import { store } from '../../_store/store'
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    oncreate() {
 | 
			
		||||
      // Avoid input delays by updating these values after a rAF
 | 
			
		||||
      // perf improvement for keyboard input latency
 | 
			
		||||
      this.observe('inputLengthToDisplay', inputLengthToDisplay => {
 | 
			
		||||
        requestAnimationFrame(() => {
 | 
			
		||||
          mark('set inputLengthToDisplayAfterRaf')
 | 
			
		||||
| 
						 | 
				
			
			@ -30,12 +31,15 @@
 | 
			
		|||
        })
 | 
			
		||||
      })
 | 
			
		||||
    },
 | 
			
		||||
    store: () => store,
 | 
			
		||||
    computed: {
 | 
			
		||||
      overLimit: (inputLength) => inputLength > CHAR_LIMIT,
 | 
			
		||||
      inputLength: (inputText) => inputText ? inputText.length : 0,
 | 
			
		||||
      inputLengthToDisplay: (inputLength) => (inputLength <= CHAR_LIMIT ? inputLength : CHAR_LIMIT - inputLength),
 | 
			
		||||
      inputLengthLabel: (overLimit, inputLengthToDisplay) => {
 | 
			
		||||
        if (overLimit) {
 | 
			
		||||
      inputLengthToDisplay: ($rawInputTextInComposeLength) => {
 | 
			
		||||
        return ($rawInputTextInComposeLength <= CHAR_LIMIT
 | 
			
		||||
          ? $rawInputTextInComposeLength
 | 
			
		||||
          : CHAR_LIMIT - $rawInputTextInComposeLength)
 | 
			
		||||
      },
 | 
			
		||||
      inputLengthLabel: ($rawInputTextInComposeOverLimit, inputLengthToDisplay) => {
 | 
			
		||||
        if ($rawInputTextInComposeOverLimit) {
 | 
			
		||||
          return `${inputLengthToDisplay} characters over limit`
 | 
			
		||||
        } else {
 | 
			
		||||
          return `${inputLengthToDisplay} characters`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,9 @@
 | 
			
		|||
import { instanceComputations } from './instanceComputations'
 | 
			
		||||
import { timelineComputations } from './timelineComputations'
 | 
			
		||||
import { statusComputations } from './statusComputations'
 | 
			
		||||
 | 
			
		||||
export function computations (store) {
 | 
			
		||||
  instanceComputations(store)
 | 
			
		||||
  timelineComputations(store)
 | 
			
		||||
  statusComputations(store)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										13
									
								
								routes/_store/statusComputations.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								routes/_store/statusComputations.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,13 @@
 | 
			
		|||
import { CHAR_LIMIT } from '../_static/statuses'
 | 
			
		||||
 | 
			
		||||
export function statusComputations (store) {
 | 
			
		||||
  store.compute('rawInputTextInComposeLength',
 | 
			
		||||
    ['rawInputTextInCompose'],
 | 
			
		||||
    (rawInputTextInCompose) => rawInputTextInCompose.length
 | 
			
		||||
  )
 | 
			
		||||
 | 
			
		||||
  store.compute('rawInputTextInComposeOverLimit',
 | 
			
		||||
    ['rawInputTextInComposeLength'],
 | 
			
		||||
    (rawInputTextInComposeLength) => rawInputTextInComposeLength > CHAR_LIMIT
 | 
			
		||||
  )
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -39,7 +39,8 @@ export const store = new PinaforeStore({
 | 
			
		|||
  pinnedStatuses: {},
 | 
			
		||||
  instanceInfos: {},
 | 
			
		||||
  statusModifications: {},
 | 
			
		||||
  inputTextInCompose: {}
 | 
			
		||||
  inputTextInCompose: {},
 | 
			
		||||
  rawInputTextInCompose: ''
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
mixins(PinaforeStore)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue