possibility to set language for post

This commit is contained in:
Arnas Udovic 2024-01-25 15:13:15 +02:00
parent a7eaec3391
commit 4d576b00fa
19 changed files with 327 additions and 14 deletions

View File

@ -60,5 +60,6 @@ export default [
{ id: 'fa-crosshairs', src: 'src/thirdparty/font-awesome-svg-png/white/svg/crosshairs.svg' },
{ id: 'fa-magic', src: 'src/thirdparty/font-awesome-svg-png/white/svg/magic.svg' },
{ id: 'fa-hashtag', src: 'src/thirdparty/font-awesome-svg-png/white/svg/hashtag.svg' },
{ id: 'fa-bookmark', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bookmark.svg' }
{ id: 'fa-bookmark', src: 'src/thirdparty/font-awesome-svg-png/white/svg/bookmark.svg' },
{ id: 'fa-language', src: 'src/thirdparty/font-awesome-svg-png/white/svg/language.svg' }
]

View File

@ -330,7 +330,7 @@ export default {
aboutAppDescription: `
<p>
Semaphore is
<a rel="noopener" target="_blank" href="https://github.com/NickColley/semaphore">free and open-source software</a>
<a rel="noopener" target="_blank" href="https://github.com/NickColley/semaphore">free and open-source software</a>
maintained by <a rel="noopener" target="_blank" href="https://nickcolley.co.uk">Nick Colley</a>
and distributed under the
<a rel="noopener" target="_blank"
@ -696,5 +696,15 @@ export default {
statusesList: 'Statuses: list',
notificationsOnInstance: 'Notifications on {instance}',
// Details
statusEdited: 'Edited'
statusEdited: 'Edited',
// Settings > Languages
languages: 'Languages',
addLanguage: 'Add language',
add: 'Add',
languageCode: 'Language code',
enterLanguageCode: 'Enter language code',
getLanguageCode: 'Language should be code from ISO-639 base. To check the right code for the language visti IANA language sub-tag registry.',
ianaLanguageRegistry: 'IANA registry',
setLanguageLabel: 'Set language (current {label})',
setLanguage: 'Set language'
}

View File

@ -0,0 +1,16 @@
import { store } from '../_store/store.js'
import { goto } from '../../../__sapper__/client.js'
export function addLanguage () {
const { languages, newLanguage } = store.get()
languages.push(newLanguage.toLowerCase().trim())
store.set({ languages })
store.save()
goto('/settings/languages')
}
export function resetNewLanguage() {
const newLanguage = ''
store.set({ newLanguage })
store.save()
}

View File

@ -27,7 +27,7 @@ export async function insertHandleForReply (statusId) {
export async function postStatus (realm, text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility,
mediaDescriptions, inReplyToUuid, poll, mediaFocalPoints) {
mediaDescriptions, inReplyToUuid, poll, mediaFocalPoints, language) {
const { currentInstance, accessToken, online } = store.get()
if (!online) {
@ -56,7 +56,7 @@ export async function postStatus (realm, text, inReplyToId, mediaIds,
}
}))
const status = await postStatusToServer(currentInstance, accessToken, text,
inReplyToId, mediaIds, sensitive, spoilerText, visibility, poll, mediaFocalPoints)
inReplyToId, mediaIds, sensitive, spoilerText, visibility, poll, language, mediaFocalPoints)
addStatusOrNotification(currentInstance, 'home', status)
store.clearComposeData(realm)
emit('postedStatus', realm, inReplyToUuid)

View File

@ -0,0 +1,5 @@
import { store } from '../_store/store.js'
export function setLanguage (realm, language) {
store.setComposeData(realm, { language })
}

View File

@ -3,13 +3,14 @@ import { DEFAULT_TIMEOUT, get, post, put, WRITE_TIMEOUT } from '../_utils/ajax.j
// post is create, put is edit
async function postOrPutStatus (url, accessToken, method, text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll) {
sensitive, spoilerText, visibility, poll, language) {
const body = {
status: text,
media_ids: mediaIds,
sensitive,
spoiler_text: spoilerText,
poll,
language,
...(method === 'post' && {
// you can't change these properties when editing
in_reply_to_id: inReplyToId,
@ -31,17 +32,17 @@ async function postOrPutStatus (url, accessToken, method, text, inReplyToId, med
}
export async function postStatus (instanceName, accessToken, text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll) {
sensitive, spoilerText, visibility, poll, language) {
const url = `${basename(instanceName)}/api/v1/statuses`
return postOrPutStatus(url, accessToken, 'post', text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll)
sensitive, spoilerText, visibility, poll, language)
}
export async function putStatus (instanceName, accessToken, id, text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll) {
sensitive, spoilerText, visibility, poll, language) {
const url = `${basename(instanceName)}/api/v1/statuses/${id}`
return postOrPutStatus(url, accessToken, 'put', text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll)
sensitive, spoilerText, visibility, poll, language)
}
export async function getStatusContext (instanceName, accessToken, statusId) {

View File

@ -19,7 +19,7 @@
<ComposePoll {realm} {poll} />
</div>
{/if}
<ComposeToolbar {realm} {postPrivacy} {media} {contentWarningShown} {text} {poll} />
<ComposeToolbar {realm} {postPrivacy} {media} {language} {contentWarningShown} {text} {poll} />
<ComposeLengthIndicator {length} {overLimit} />
<ComposeMedia {realm} {media} />
<ComposeMediaSensitive {realm} {media} {sensitive} {contentWarning} {contentWarningShown} />
@ -173,6 +173,9 @@
poll: ({ composeData }) => composeData.poll,
inReplyToId: ({ composeData }) => composeData.inReplyToId,
postPrivacy: ({ postPrivacyKey }) => POST_PRIVACY_OPTIONS.find(_ => _.key === postPrivacyKey),
language: ({ composeData, $languages }) => {
return composeData.language ? composeData.language : ($languages.length ? $languages[0] : 'en')
},
defaultPostPrivacyKey: ({ $currentVerifyCredentials }) => (
($currentVerifyCredentials && $currentVerifyCredentials.source.privacy) || 'public'
),
@ -219,7 +222,8 @@
inReplyToUuid, // typical replies, using Semaphore-specific uuid
inReplyToId, // delete-and-redraft replies, using standard id
poll,
sensitive
sensitive,
language
} = this.get()
const mediaIds = media.map(_ => _.data.id)
const mediaDescriptions = media.map(_ => _.description)
@ -248,7 +252,7 @@
/* no await */ postStatus(realm, text, inReplyTo, mediaIds,
sensitive, contentWarning, postPrivacyKey,
mediaDescriptions, inReplyToUuid, pollToPost,
mediaFocalPoints)
mediaFocalPoints, language)
}
}
}

View File

@ -38,6 +38,12 @@
pressable={true}
pressed={contentWarningShown}
/>
<IconButton
className="compose-toolbar-button"
label={setLanguageLabel}
href="#fa-language"
on:click="onSetLanguageClick()"
/>
</div>
<input ref:input
on:change="onFileChange(event)"
@ -74,6 +80,7 @@
import { store } from '../../_store/store.js'
import { importShowEmojiDialog } from '../dialog/asyncDialogs/importShowEmojiDialog.js'
import { importShowPostPrivacyDialog } from '../dialog/asyncDialogs/importShowPostPrivacyDialog.js'
import { importShowSetLanguageDialog } from '../dialog/asyncDialogs/importShowSetLanguageDialog.js'
import { doMediaUpload } from '../../_actions/media.js'
import { toggleContentWarningShown } from '../../_actions/contentWarnings.js'
import { mediaAccept } from '../../_static/media.js'
@ -91,6 +98,9 @@
computed: {
postPrivacyLabel: ({ postPrivacy }) => (
formatIntl('intl.postPrivacyLabel', { label: postPrivacy.label })
),
setLanguageLabel: ({ language }) => (
formatIntl('intl.setLanguageLabel', { label: language })
)
},
store: () => store,
@ -119,6 +129,11 @@
const showPostPrivacyDialog = await importShowPostPrivacyDialog()
showPostPrivacyDialog(realm)
},
async onSetLanguageClick () {
const { realm } = this.get()
const showSetLanguageDialog = await importShowSetLanguageDialog()
showSetLanguageDialog(realm)
},
onContentWarningClick () {
const { realm } = this.get()
toggleContentWarningShown(realm)

View File

@ -0,0 +1,3 @@
export const importShowSetLanguageDialog = () => import(
'../creators/showSetLanguageDialog.js'
).then(mod => mod.default)

View File

@ -0,0 +1,50 @@
<ModalDialog
{id}
{label}
{title}
shrinkWidthToFit={true}
background="var(--main-bg)"
>
<GenericDialogList selectable={true} {items} on:click="onClick(event)" />
</ModalDialog>
<script>
import ModalDialog from './ModalDialog.html'
import { store } from '../../../_store/store.js'
import { setLanguage } from '../../../_actions/language.js'
import GenericDialogList from './GenericDialogList.html'
import { show } from '../helpers/showDialog.js'
import { close } from '../helpers/closeDialog.js'
import { oncreate } from '../helpers/onCreateDialog.js'
export default {
oncreate,
components: {
ModalDialog,
GenericDialogList
},
store: () => store,
methods: {
show,
close,
onClick (item) {
const { realm } = this.get()
setLanguage(realm, item.key)
this.close()
}
},
computed: {
composeData: ({ $currentComposeData, realm }) => $currentComposeData[realm] || {},
language: ({ composeData, $languages }) => {
return composeData.language ? composeData.language : ($languages.length ? $languages[0] : 'en')
},
items: ({ language, $languages }) => {
return $languages.map(option => ({
key: option,
label: option.toUpperCase(),
icon: 'fa-language',
selected: language === option
}))
}
}
}
</script>

View File

@ -0,0 +1,10 @@
import SetLanguageDialog from '../components/SetLanguageDialog.html'
import { showDialog } from './showDialog.js'
export default function showSetLanguageDialog (realm) {
return showDialog(SetLanguageDialog, {
label: 'intl.setLanguage',
title: 'intl.setLanguage',
realm
})
}

View File

@ -51,6 +51,8 @@
settings: 'intl.settings',
'settings/about': 'intl.aboutApp',
'settings/general': 'intl.general',
'settings/languages': 'intl.languages',
'settings/languages/add': 'intl.addLanguage',
'settings/instances': 'intl.instances',
'settings/instances/add': $isUserLoggedIn ? 'intl.addInstance' : 'intl.logIn'
}),

View File

@ -27,6 +27,11 @@
</span>
{/if}
{/if}
{#if language}
<span class="status-application status-application-span language-tag">
{language}
</span>
{/if}
<a class="status-favs-reblogs status-reblogs"
rel="prefetch"
href="/statuses/{originalStatusId}/reblogs"
@ -131,6 +136,10 @@
}
.language-tag {
text-transform: uppercase;
}
</style>
<script>
import ExternalLink from '../ExternalLink.html'
@ -181,6 +190,7 @@
}
return originalStatus.favourites_count || 0
},
language: ({ originalStatus }) => originalStatus.language,
displayAbsoluteFormattedDate: ({ createdAtDateTS, $isMobileSize }) => (
($isMobileSize ? shortAbsoluteDateFormatter : absoluteDateFormatter)().format(createdAtDateTS)
),

View File

@ -8,6 +8,9 @@
<SettingsListRow>
<SettingsListButton href="/settings/instances" label="{intl.instances}"/>
</SettingsListRow>
<SettingsListRow>
<SettingsListButton href="/settings/languages" label="{intl.languages}"/>
</SettingsListRow>
<SettingsListRow>
<SettingsListButton href="/settings/wellness" label="{intl.wellness}"/>
</SettingsListRow>

View File

@ -0,0 +1,99 @@
<SettingsLayout page='settings/languages/add' label={intl.addLanguage}>
<h1 id="add-an-language-h1">{intl.addLanguage}</h1>
<div class="add-new-language">
<form on:submit='onSubmitLanguage(event)' aria-labelledby="add-an-language-h1">
{#if !hasIndexedDB || !hasLocalStorage}
<div class="form-error form-error-user-error" role="alert">
{intl.storageError}
</div>
{/if}
<noscript>
<div class="form-error" role="alert">
{intl.javaScriptError}
</div>
</noscript>
<label for="languageInput">{intl.languageCode}</label>
<input type="text" autocapitalize="none" spellcheck="false" id="languageInput"
bind:value='$newLanguage' placeholder="{intl.enterLanguageCode}" required
>
<button class="primary" type="submit" id="submitButton"
disabled={!$newLanguage}>
{intl.add}
</button>
</form>
</div>
<p>
{intl.getLanguageCode}
<a rel="noopener" target="_blank" href="https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry">{intl.ianaLanguageRegistry}</a>
</p>
</SettingsLayout>
<style>
.add-new-language {
background: var(--form-bg);
padding: 5px 10px 15px;
margin: 20px auto;
border: 1px solid var(--form-border);
border-radius: 4px;
}
.form-error {
border: 2px solid var(--warn-color);
border-radius: 2px;
padding: 10px;
font-size: 1.3em;
margin: 5px;
background-color: var(--main-bg);
}
input {
min-width: 70%;
max-width: 100%;
background-color: var(--input-bg);
}
label, input, button, :global(.add-new-language-aside) {
display: block;
margin: 20px 5px;
}
@media (max-width: 767px) {
input {
min-width: 95%;
}
}
</style>
<script>
import SettingsLayout from '../../../_components/settings/SettingsLayout.html'
import { store } from '../../../_store/store.js'
import { addLanguage, resetNewLanguage } from '../../../_actions/addLanguage.js'
export default {
async oncreate () {
this.set({
hasIndexedDB: await testHasIndexedDB(),
hasLocalStorage: testHasLocalStorage()
})
resetNewLanguage()
},
components: {
SettingsLayout
},
store: () => store,
data: () => ({
hasIndexedDB: true,
hasLocalStorage: true
}),
methods: {
onSubmitLanguage (event) {
event.preventDefault()
event.stopPropagation()
addLanguage()
}
}
}
</script>

View File

@ -0,0 +1,40 @@
<SettingsLayout page='settings/languages' label="{intl.languages}">
<h1>{intl.languages}</h1>
<SettingsList>
{#each $languages as language}
<SettingsListRow>
<span class="language-list-item">
{language}
</span>
</SettingsListRow>
{/each}
</SettingsList>
<p>
<a rel="prefetch" href="/settings/languages/add" id="add-link-1">{intl.addLanguage}</a>
</p>
</SettingsLayout>
<style>
.language-list-item {
display: flex;
flex-grow: 1;
text-transform: uppercase;
}
</style>
<script>
import { store } from '../../../_store/store.js'
import SettingsLayout from '../../../_components/settings/SettingsLayout.html'
import SettingsList from '../../../_components/settings/SettingsList.html'
import SettingsListRow from '../../../_components/settings/SettingsListRow.html'
import { formatIntl } from '../../../_utils/formatIntl.js'
export default {
components: {
SettingsLayout,
SettingsList,
SettingsListRow
},
store: () => store
}
</script>

View File

@ -44,7 +44,9 @@ const persistedState = {
reduceMotion:
!process.browser ||
matchMedia('(prefers-reduced-motion: reduce)').matches,
underlineLinks: false
underlineLinks: false,
languages: ['en'],
newLanguage: ''
}
const nonPersistedState = {

View File

@ -0,0 +1,21 @@
<Title name="{intl.addLanguage}" settingsPage={true} />
<LazyPage {pageComponent} {params} />
<script>
import Title from '../../_components/Title.html'
import LazyPage from '../../_components/LazyPage.html'
import pageComponent from '../../_pages/settings/languages/add.html'
export default {
components: {
Title,
LazyPage
},
data: () => ({
pageComponent
})
}
</script>

View File

@ -0,0 +1,21 @@
<Title name="{intl.languages}" settingsPage={true} />
<LazyPage {pageComponent} {params} />
<script>
import Title from '../../_components/Title.html'
import LazyPage from '../../_components/LazyPage.html'
import pageComponent from '../../_pages/settings/languages/index.html'
export default {
components: {
Title,
LazyPage
},
data: () => ({
pageComponent
})
}
</script>