feat: mark media as sensitive without a CW (#1486)

fixes #1297
This commit is contained in:
Nolan Lawson 2019-09-15 10:45:46 -07:00 committed by GitHub
parent 8035cb2580
commit 35058ed965
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 130 additions and 8 deletions

View File

@ -6,6 +6,7 @@ export function toggleContentWarningShown (realm) {
const newShown = !shown const newShown = !shown
store.setComposeData(realm, { store.setComposeData(realm, {
contentWarning: newShown ? contentWarning : '', contentWarning: newShown ? contentWarning : '',
contentWarningShown: newShown contentWarningShown: newShown,
sensitive: contentWarning && newShown // toggling content warning automatically toggles sensitive media
}) })
} }

View File

@ -22,6 +22,7 @@
<ComposeToolbar {realm} {postPrivacy} {media} {contentWarningShown} {text} {poll} /> <ComposeToolbar {realm} {postPrivacy} {media} {contentWarningShown} {text} {poll} />
<ComposeLengthIndicator {length} {overLimit} /> <ComposeLengthIndicator {length} {overLimit} />
<ComposeMedia {realm} {media} /> <ComposeMedia {realm} {media} />
<ComposeMediaSensitive {realm} {media} {sensitive} {contentWarning} {contentWarningShown} />
</div> </div>
</ComposeFileDrop> </ComposeFileDrop>
<ComposeStickyButton {showSticky} <ComposeStickyButton {showSticky}
@ -46,7 +47,8 @@
"avatar autosuggest autosuggest autosuggest" "avatar autosuggest autosuggest autosuggest"
"avatar poll poll poll" "avatar poll poll poll"
"avatar toolbar toolbar length" "avatar toolbar toolbar length"
"avatar media media media"; "avatar media media media"
"avatar sensitive sensitive sensitive";
grid-template-columns: min-content minmax(0, max-content) 1fr 1fr; grid-template-columns: min-content minmax(0, max-content) 1fr 1fr;
position: relative; position: relative;
} }
@ -95,6 +97,7 @@
import ComposeFileDrop from './ComposeFileDrop.html' import ComposeFileDrop from './ComposeFileDrop.html'
import ComposeAutosuggest from './ComposeAutosuggest.html' import ComposeAutosuggest from './ComposeAutosuggest.html'
import ComposePoll from './ComposePoll.html' import ComposePoll from './ComposePoll.html'
import ComposeMediaSensitive from './ComposeMediaSensitive.html'
import { measureText } from '../../_utils/measureText' import { measureText } from '../../_utils/measureText'
import { POST_PRIVACY_OPTIONS } from '../../_static/statuses' import { POST_PRIVACY_OPTIONS } from '../../_static/statuses'
import { store } from '../../_store/store' import { store } from '../../_store/store'
@ -133,7 +136,8 @@
ComposeContentWarning, ComposeContentWarning,
ComposeFileDrop, ComposeFileDrop,
ComposeAutosuggest, ComposeAutosuggest,
ComposePoll ComposePoll,
ComposeMediaSensitive
}, },
data: () => ({ data: () => ({
size: undefined, size: undefined,
@ -173,7 +177,8 @@
), ),
overLimit: ({ length, $maxStatusChars }) => length > $maxStatusChars, overLimit: ({ length, $maxStatusChars }) => length > $maxStatusChars,
contentWarningShown: ({ composeData }) => composeData.contentWarningShown, contentWarningShown: ({ composeData }) => composeData.contentWarningShown,
contentWarning: ({ composeData }) => composeData.contentWarning || '' contentWarning: ({ composeData }) => composeData.contentWarning || '',
sensitive: ({ composeData }) => !!composeData.sensitive
}, },
transitions: { transitions: {
slide slide
@ -196,9 +201,9 @@
overLimit, overLimit,
inReplyToUuid, // typical replies, using Pinafore-specific uuid inReplyToUuid, // typical replies, using Pinafore-specific uuid
inReplyToId, // delete-and-redraft replies, using standard id inReplyToId, // delete-and-redraft replies, using standard id
poll poll,
sensitive
} = this.get() } = this.get()
const sensitive = media.length && !!contentWarning
const mediaIds = media.map(_ => _.data.id) const mediaIds = media.map(_ => _.data.id)
const mediaDescriptions = media.map(_ => _.description) const mediaDescriptions = media.map(_ => _.description)
const mediaFocalPoints = media.map(_ => [_.focusX, _.focusY]) const mediaFocalPoints = media.map(_ => [_.focusX, _.focusY])

View File

@ -41,7 +41,10 @@
const { realm } = this.get() const { realm } = this.get()
this.observe('rawText', rawText => { this.observe('rawText', rawText => {
updateContentWarningInStore(() => { updateContentWarningInStore(() => {
this.store.setComposeData(realm, { contentWarning: rawText }) this.store.setComposeData(realm, {
sensitive: !!rawText, // toggling the content warning automatically toggles sensitive media
contentWarning: rawText
})
this.store.save() this.store.save()
}) })
}, { init: false }) }, { init: false })

View File

@ -113,6 +113,12 @@
max-height: 7vh; max-height: 7vh;
} }
} }
@media (max-width: 320px) {
.compose-media-realm-dialog .compose-media-alt-input {
display: none; /* too small to show this - use the edit button instead */
}
}
</style> </style>
<script> <script>
import { store } from '../../_store/store' import { store } from '../../_store/store'

View File

@ -0,0 +1,79 @@
{#if media.length}
<div class="compose-media-sensitive">
<label>
<input type="checkbox" bind:checked="rawChecked" {disabled} />
<span class="{disabled ? 'compose-sensitive-span-disabled' : ''}">
Mark media as sensitive
</span>
</label>
</div>
{/if}
<style>
.compose-media-sensitive {
grid-area: sensitive;
margin-top: 10px;
}
label {
padding: 5px;
display: flex;
align-items: center;
justify-content: flex-start;
}
span {
margin-left: 5px;
}
.compose-sensitive-span-disabled {
color: var(--deemphasized-text-color);
}
@media (max-width: 767px) {
.compose-media-sensitive {
margin-top: 0;
}
}
@media (max-width: 320px) {
span {
font-size: 0.9em;
}
}
</style>
<script>
import { observe } from 'svelte-extras'
import { scheduleIdleTask } from '../../_utils/scheduleIdleTask'
export default {
oncreate () {
this.setupSyncToStore()
this.setupSyncFromStore()
},
data: () => ({
rawChecked: false
}),
computed: {
disabled: ({ contentWarning, contentWarningShown }) => contentWarning && contentWarningShown
},
methods: {
observe,
setupSyncToStore () {
this.observe('rawChecked', () => {
scheduleIdleTask(() => {
const { realm } = this.get()
const { rawChecked } = this.get()
const sensitive = this.store.getComposeData(realm, 'sensitive')
if (sensitive !== rawChecked) {
this.store.setComposeData(realm, { sensitive: rawChecked })
this.store.save()
}
})
}, { init: false })
},
setupSyncFromStore () {
this.observe('sensitive', sensitive => {
this.set({ rawChecked: sensitive })
})
}
}
}
</script>

View File

@ -10,7 +10,8 @@ import {
homeNavButton, homeNavButton,
mediaButton, mediaButton,
notificationsNavButton, notificationsNavButton,
uploadKittenImage uploadKittenImage,
composeMediaSensitiveCheckbox, getNthStatusAndSensitiveImage, getNthStatusAndSensitiveButton, getNthStatusContent
} from '../utils' } from '../utils'
import { loginAsFoobar } from '../roles' import { loginAsFoobar } from '../roles'
@ -97,3 +98,20 @@ test('can post a status with empty content if there is media', async t => {
await t.click(composeButton) await t.click(composeButton)
.expect(getNthStatusAndImage(1, 1).getAttribute('alt')).eql('just an image!') .expect(getNthStatusAndImage(1, 1).getAttribute('alt')).eql('just an image!')
}) })
test('can make an image sensitive without adding a CW', async t => {
await loginAsFoobar(t)
await t
.typeText(composeInput, 'this is just a kitteh')
await (uploadKittenImage(2)())
await t
.typeText(getNthMediaAltInput(1), 'sensitive kitteh')
.expect(composeMediaSensitiveCheckbox.checked).notOk()
.click(composeMediaSensitiveCheckbox)
.expect(composeMediaSensitiveCheckbox.checked).ok()
.click(composeButton)
.expect(getNthStatusContent(1).innerText).contains('this is just a kitteh')
.expect(getNthStatusAndSensitiveImage(1, 1).getAttribute('src')).match(/^blob:http:\/\/localhost/)
.click(getNthStatusAndSensitiveButton(1, 1))
.expect(getNthStatusAndImage(1, 1).getAttribute('alt')).eql('sensitive kitteh')
})

View File

@ -71,6 +71,8 @@ export const composePollExpiryOption = $('.compose-poll select option')
export const composePollExpiryInDialog = $('.modal-dialog .compose-poll select') export const composePollExpiryInDialog = $('.modal-dialog .compose-poll select')
export const composePollAddButton = $('.compose-poll button:last-of-type') export const composePollAddButton = $('.compose-poll button:last-of-type')
export const composeMediaSensitiveCheckbox = $('.compose-media-sensitive input')
export const postPrivacyDialogButtonUnlisted = $('[aria-label="Post privacy dialog"] li:nth-child(2) button') export const postPrivacyDialogButtonUnlisted = $('[aria-label="Post privacy dialog"] li:nth-child(2) button')
export const accountProfileFilterStatuses = $('.account-profile-filters li:nth-child(1)') export const accountProfileFilterStatuses = $('.account-profile-filters li:nth-child(1)')
@ -352,6 +354,14 @@ export function getNthStatusAndImage (nStatus, nImage) {
return $(`${getNthStatusSelector(nStatus)} .status-media .show-image-button:nth-child(${nImage}) img`) return $(`${getNthStatusSelector(nStatus)} .status-media .show-image-button:nth-child(${nImage}) img`)
} }
export function getNthStatusAndSensitiveButton (nStatus, nImage) {
return $(`${getNthStatusSelector(nStatus)} .status-sensitive-media-button:nth-child(${nImage})`)
}
export function getNthStatusAndSensitiveImage (nStatus, nImage) {
return $(`${getNthStatusSelector(nStatus)} .status-media button:nth-child(${nImage}) img`)
}
export function getFirstVisibleStatus () { export function getFirstVisibleStatus () {
return $('.list-item > article[aria-posinset]').nth(0) return $('.list-item > article[aria-posinset]').nth(0)
} }