start on content warnings

This commit is contained in:
Nolan Lawson 2018-03-03 15:44:43 -08:00
parent 1de9e49f78
commit 2f5e19bd44
6 changed files with 82 additions and 12 deletions

View file

@ -0,0 +1,6 @@
import { store } from '../_store/store'
export function toggleContentWarningShown (realm) {
let shown = store.getComposeData(realm, 'contentWarningShown')
store.setComposeData(realm, {contentWarningShown: !shown})
}

View file

@ -12,7 +12,7 @@
} }
.compose-box-display-name { .compose-box-display-name {
color: var(--deemphasized-text-color); color: var(--deemphasized-text-color);
grid-area: display-name; grid-area: name;
min-width: 0; min-width: 0;
white-space: nowrap; white-space: nowrap;
overflow: hidden; overflow: hidden;

View file

@ -1,8 +1,11 @@
<div class="compose-box {{overLimit ? 'over-char-limit' : ''}}"> <div class="compose-box {{overLimit ? 'over-char-limit' : ''}}">
<ComposeAuthor /> <ComposeAuthor />
{{#if contentWarningShown}}
<ComposeContentWarning :realm :contentWarning />
{{/if}}
<ComposeInput :realm :text /> <ComposeInput :realm :text />
<ComposeLengthGauge :textLength :textOverLimit /> <ComposeLengthGauge :textLength :textOverLimit />
<ComposeToolbar :realm :postPrivacy :media /> <ComposeToolbar :realm :postPrivacy :media :contentWarningShown />
<ComposeLengthIndicator :textLength :textOverLimit /> <ComposeLengthIndicator :textLength :textOverLimit />
<ComposeMedia :realm :media /> <ComposeMedia :realm :media />
<ComposeButton :textLength :textOverLimit /> <ComposeButton :textLength :textOverLimit />
@ -14,12 +17,13 @@
display: grid; display: grid;
align-items: flex-start; align-items: flex-start;
grid-template-areas: grid-template-areas:
"avatar display-name handle handle" "avatar name handle handle"
"avatar input input input" "avatar cw cw cw"
"avatar gauge gauge gauge" "avatar input input input"
"avatar toolbar toolbar length" "avatar gauge gauge gauge"
"avatar media media media" "avatar toolbar toolbar length"
"avatar button button button"; "avatar media media media"
"avatar button button button";
grid-template-columns: min-content minmax(0, max-content) 1fr 1fr; grid-template-columns: min-content minmax(0, max-content) 1fr 1fr;
border-bottom: 1px solid var(--main-border); border-bottom: 1px solid var(--main-border);
width: 560px; width: 560px;
@ -42,6 +46,7 @@
import ComposeInput from './ComposeInput.html' import ComposeInput from './ComposeInput.html'
import ComposeButton from './ComposeButton.html' import ComposeButton from './ComposeButton.html'
import ComposeMedia from './ComposeMedia.html' import ComposeMedia from './ComposeMedia.html'
import ComposeContentWarning from './ComposeContentWarning.html'
import { measureText } from '../../_utils/measureText' import { measureText } from '../../_utils/measureText'
import { CHAR_LIMIT, POST_PRIVACY_OPTIONS } from '../../_static/statuses' import { CHAR_LIMIT, POST_PRIVACY_OPTIONS } from '../../_static/statuses'
import { store } from '../../_store/store' import { store } from '../../_store/store'
@ -54,7 +59,8 @@
ComposeLengthIndicator, ComposeLengthIndicator,
ComposeInput, ComposeInput,
ComposeButton, ComposeButton,
ComposeMedia ComposeMedia,
ComposeContentWarning
}, },
store: () => store, store: () => store,
computed: { computed: {
@ -65,7 +71,9 @@
defaultPostPrivacyKey: ($currentVerifyCredentials) => $currentVerifyCredentials.source.privacy, defaultPostPrivacyKey: ($currentVerifyCredentials) => $currentVerifyCredentials.source.privacy,
postPrivacyKey: (composeData, defaultPostPrivacyKey) => composeData.postPrivacy || defaultPostPrivacyKey, postPrivacyKey: (composeData, defaultPostPrivacyKey) => composeData.postPrivacy || defaultPostPrivacyKey,
textLength: (text) => measureText(text), textLength: (text) => measureText(text),
textOverLimit: (textLength) => textLength > CHAR_LIMIT textOverLimit: (textLength) => textLength > CHAR_LIMIT,
contentWarningShown: (composeData) => composeData.contentWarningShown,
contentWarning: (composeData) => composeData.contentWarning || ''
} }
} }
</script> </script>

View file

@ -0,0 +1,49 @@
<input class="content-warning-input"
type="text"
placeholder="Content warning"
aria-label="Content warning"
bind:value=rawText
/>
<style>
.content-warning-input {
grid-area: cw;
font-size: 1.2em;
margin: 10px 0 0 5px;
padding: 10px;
border: 1px solid var(--input-border);
width: calc(100% - 5px);
}
</style>
<script>
import { store } from '../../_store/store'
import debounce from 'lodash/debounce'
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
export default {
oncreate() {
this.setupSyncFromStore()
this.setupSyncToStore()
},
store: () => store,
data: () => ({
rawText: ''
}),
methods: {
setupSyncFromStore() {
this.observe('contentWarning', contentWarning => {
this.set({rawText: contentWarning})
})
},
setupSyncToStore() {
const saveText = debounce(() => scheduleIdleTask(() => this.store.save()), 1000)
this.observe('rawText', rawText => {
this.store.setComposeData(this.get('realm'), {
contentWarning: rawText
})
saveText()
}, {init: false})
}
}
}
</script>

View file

@ -17,8 +17,11 @@
on:click="onPostPrivacyClick()" on:click="onPostPrivacyClick()"
/> />
<IconButton <IconButton
label="Add content warning" label="{{contentWarningShown ? 'Remove content warning' : 'Add content warning'}}"
href="#fa-exclamation-triangle" href="#fa-exclamation-triangle"
on:click="onContentWarningClick()"
pressable="true"
pressed="{{contentWarningShown}}"
/> />
<input ref:input <input ref:input
on:change="onFileChange(event)" on:change="onFileChange(event)"
@ -40,7 +43,7 @@
import { updateCustomEmojiForInstance } from '../../_actions/emoji' import { updateCustomEmojiForInstance } from '../../_actions/emoji'
import { importDialogs } from '../../_utils/asyncModules' import { importDialogs } from '../../_utils/asyncModules'
import { doMediaUpload } from '../../_actions/media' import { doMediaUpload } from '../../_actions/media'
import { POST_PRIVACY_OPTIONS } from '../../_static/statuses' import { toggleContentWarningShown } from '../../_actions/contentWarnings'
export default { export default {
oncreate() { oncreate() {
@ -75,6 +78,9 @@
async onPostPrivacyClick() { async onPostPrivacyClick() {
let dialogs = await importDialogs() let dialogs = await importDialogs()
dialogs.showPostPrivacyDialog(this.get('realm')) dialogs.showPostPrivacyDialog(this.get('realm'))
},
onContentWarningClick() {
toggleContentWarningShown(this.get('realm'))
} }
} }
} }

View file

@ -33,6 +33,7 @@ test('inserts media', async t => {
test('removes media', async t => { test('removes media', async t => {
await t.useRole(foobarRole) await t.useRole(foobarRole)
await (uploadKittenImage(1)()) await (uploadKittenImage(1)())
await t.expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg')
await (uploadKittenImage(2)()) await (uploadKittenImage(2)())
await t.expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg') await t.expect(getNthMedia(1).getAttribute('alt')).eql('kitten1.jpg')
.expect(getNthMedia(2).getAttribute('alt')).eql('kitten2.jpg') .expect(getNthMedia(2).getAttribute('alt')).eql('kitten2.jpg')