Merge remote-tracking branch 'origin/develop' into user-search-better-error-handling

This commit is contained in:
lain 2019-03-22 13:00:11 +01:00
commit bd247adc5a
21 changed files with 386 additions and 65 deletions

View File

@ -154,7 +154,7 @@ input, textarea, .select {
background: transparent;
border: none;
color: $fallback--text;
color: var(--text, $fallback--text);
color: var(--inputText, --text, $fallback--text);
margin: 0;
padding: 0 2em 0 .2em;
font-family: sans-serif;

View File

@ -203,6 +203,12 @@ const getNodeInfo = async ({ store }) => {
const suggestions = metadata.suggestions
store.dispatch('setInstanceOption', { name: 'suggestionsEnabled', value: suggestions.enabled })
store.dispatch('setInstanceOption', { name: 'suggestionsWeb', value: suggestions.web })
const software = data.software
store.dispatch('setInstanceOption', { name: 'backendVersion', value: software.version })
const frontendVersion = window.___pleromafe_commit_hash
store.dispatch('setInstanceOption', { name: 'frontendVersion', value: frontendVersion })
} else {
throw (res)
}

View File

@ -1,4 +1,5 @@
import BasicUserCard from '../basic_user_card/basic_user_card.vue'
import RemoteFollow from '../remote_follow/remote_follow.vue'
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
const FollowCard = {
@ -14,13 +15,17 @@ const FollowCard = {
}
},
components: {
BasicUserCard
BasicUserCard,
RemoteFollow
},
computed: {
isMe () { return this.$store.state.users.currentUser.id === this.user.id },
following () { return this.updated ? this.updated.following : this.user.following },
showFollow () {
return !this.following || this.updated && !this.updated.following
},
loggedIn () {
return this.$store.state.users.currentUser
}
},
methods: {

View File

@ -4,9 +4,12 @@
<span class="faint" v-if="!noFollowsYou && user.follows_you">
{{ isMe ? $t('user_card.its_you') : $t('user_card.follows_you') }}
</span>
<div class="btn-follow" v-if="showFollow && !loggedIn">
<RemoteFollow :user="user" />
</div>
<button
v-if="showFollow"
class="btn btn-default"
v-if="showFollow && loggedIn"
class="btn btn-default btn-follow"
@click="followUser"
:disabled="inProgress"
:title="requestSent ? $t('user_card.follow_again') : ''"
@ -44,7 +47,7 @@
flex-wrap: wrap;
line-height: 1.5em;
.btn {
.btn-follow {
margin-top: 0.5em;
margin-left: auto;
width: 10em;

View File

@ -31,6 +31,9 @@ const ImageCropper = {
saveButtonLabel: {
type: String
},
saveWithoutCroppingButtonlabel: {
type: String
},
cancelButtonLabel: {
type: String
}
@ -48,6 +51,9 @@ const ImageCropper = {
saveText () {
return this.saveButtonLabel || this.$t('image_cropper.save')
},
saveWithoutCroppingText () {
return this.saveWithoutCroppingButtonlabel || this.$t('image_cropper.save_without_cropping')
},
cancelText () {
return this.cancelButtonLabel || this.$t('image_cropper.cancel')
},
@ -76,6 +82,18 @@ const ImageCropper = {
this.submitting = false
})
},
submitWithoutCropping () {
this.submitting = true
this.avatarUploadError = null
this.submitHandler(false, this.dataUrl)
.then(() => this.destroy())
.catch((err) => {
this.submitError = err
})
.finally(() => {
this.submitting = false
})
},
pickImage () {
this.$refs.input.click()
},

View File

@ -7,6 +7,7 @@
<div class="image-cropper-buttons-wrapper">
<button class="btn" type="button" :disabled="submitting" @click="submit" v-text="saveText"></button>
<button class="btn" type="button" :disabled="submitting" @click="destroy" v-text="cancelText"></button>
<button class="btn" type="button" :disabled="submitting" @click="submitWithoutCropping" v-text="saveWithoutCroppingText"></button>
<i class="icon-spin4 animate-spin" v-if="submitting"></i>
</div>
<div class="alert error" v-if="submitError">
@ -36,7 +37,11 @@
}
&-buttons-wrapper {
margin-top: 15px;
margin-top: 10px;
button {
margin-top: 5px;
}
}
}
</style>

View File

@ -0,0 +1,10 @@
export default {
props: [ 'user' ],
computed: {
subscribeUrl () {
// eslint-disable-next-line no-undef
const serverUrl = new URL(this.user.statusnet_profile_url)
return `${serverUrl.protocol}//${serverUrl.host}/main/ostatus`
}
}
}

View File

@ -0,0 +1,24 @@
<template>
<div class="remote-follow">
<form method="POST" :action='subscribeUrl'>
<input type="hidden" name="nickname" :value="user.screen_name">
<input type="hidden" name="profile" value="">
<button click="submit" class="remote-button">
{{ $t('user_card.remote_follow') }}
</button>
</form>
</div>
</template>
<script src="./remote_follow.js"></script>
<style lang="scss">
.remote-follow {
max-width: 220px;
.remote-button {
width: 100%;
min-height: 28px;
}
}
</style>

View File

@ -1,8 +1,13 @@
/* eslint-env browser */
import { filter, trim } from 'lodash'
import TabSwitcher from '../tab_switcher/tab_switcher.js'
import StyleSwitcher from '../style_switcher/style_switcher.vue'
import InterfaceLanguageSwitcher from '../interface_language_switcher/interface_language_switcher.vue'
import { filter, trim } from 'lodash'
import { extractCommit } from '../../services/version/version.service'
const pleromaFeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
const pleromaBeCommitUrl = 'https://git.pleroma.social/pleroma/pleroma/commit/'
const settings = {
data () {
@ -78,7 +83,10 @@ const settings = {
// Future spec, still not supported in Nightly 63 as of 08/2018
Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'audioTracks'),
playVideosInModal: user.playVideosInModal,
useContainFit: user.useContainFit
useContainFit: user.useContainFit,
backendVersion: instance.backendVersion,
frontendVersion: instance.frontendVersion
}
},
components: {
@ -96,7 +104,13 @@ const settings = {
postFormats () {
return this.$store.state.instance.postFormats || []
},
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel }
instanceSpecificPanelPresent () { return this.$store.state.instance.showInstanceSpecificPanel },
frontendVersionLink () {
return pleromaFeCommitUrl + this.frontendVersion
},
backendVersionLink () {
return pleromaBeCommitUrl + extractCommit(this.backendVersion)
}
},
watch: {
hideAttachmentsLocal (value) {

View File

@ -261,6 +261,28 @@
</div>
</div>
</div>
<div :label="$t('settings.version.title')" >
<div class="setting-item">
<ul class="setting-list">
<li>
<p>{{$t('settings.version.backend_version')}}</p>
<ul class="option-list">
<li>
<a :href="backendVersionLink" target="_blank">{{backendVersion}}</a>
</li>
</ul>
</li>
<li>
<p>{{$t('settings.version.frontend_version')}}</p>
<ul class="option-list">
<li>
<a :href="frontendVersionLink" target="_blank">{{frontendVersion}}</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</tab-switcher>
</keep-alive>
</div>

View File

@ -1,4 +1,5 @@
import UserAvatar from '../user_avatar/user_avatar.vue'
import RemoteFollow from '../remote_follow/remote_follow.vue'
import { hex2rgb } from '../../services/color_convert/color_convert.js'
import { requestFollow, requestUnfollow } from '../../services/follow_manipulate/follow_manipulate'
import generateProfileLink from 'src/services/user_profile_link_generator/user_profile_link_generator'
@ -99,7 +100,8 @@ export default {
}
},
components: {
UserAvatar
UserAvatar,
RemoteFollow
},
methods: {
followUser () {

View File

@ -84,14 +84,8 @@
</button>
</span>
</div>
<div class="remote-follow" v-if='!loggedIn && user.is_local'>
<form method="POST" :action='subscribeUrl'>
<input type="hidden" name="nickname" :value="user.screen_name">
<input type="hidden" name="profile" value="">
<button click="submit" class="remote-button">
{{ $t('user_card.remote_follow') }}
</button>
</form>
<div v-if='!loggedIn && user.is_local'>
<RemoteFollow :user="user" />
</div>
<div class='block' v-if='isOtherUser && loggedIn'>
<span v-if='user.statusnet_blocking'>
@ -375,11 +369,6 @@
min-height: 28px;
}
.remote-follow {
max-width: 220px;
min-height: 28px;
}
.follow {
max-width: 220px;
min-height: 28px;

View File

@ -158,7 +158,13 @@ const UserSettings = {
reader.readAsDataURL(file)
},
submitAvatar (cropper, file) {
const img = cropper.getCroppedCanvas().toDataURL(file.type)
let img
if (cropper) {
img = cropper.getCroppedCanvas().toDataURL(file.type)
} else {
img = file
}
return this.$store.state.api.backendInteractor.updateAvatar({ params: { img } }).then((user) => {
if (!user.error) {
this.$store.commit('addNewUsers', [user])

View File

@ -25,6 +25,7 @@
"image_cropper": {
"crop_picture": "Crop picture",
"save": "Save",
"save_without_cropping": "Save without cropping",
"cancel": "Cancel"
},
"login": {
@ -347,6 +348,11 @@
"checkbox": "I have skimmed over terms and conditions",
"link": "a nice lil' link"
}
},
"version": {
"title": "Version",
"backend_version": "Backend Version",
"frontend_version": "Frontend Version"
}
},
"timeline": {

View File

@ -59,19 +59,21 @@
"broken_favorite": "Estatut desconegut, sèm a lo cercar...",
"favorited_you": "a aimat vòstre estatut",
"followed_you": "vos a seguit",
"load_older": "Cargar las notificaciones mai ancianas",
"load_older": "Cargar las notificacions mai ancianas",
"notifications": "Notficacions",
"read": "Legit !",
"read": "Legit!",
"repeated_you": "a repetit vòstre estatut",
"no_more_notifications": "Pas mai de notificacions"
},
"post_status": {
"new_status": "Publicar destatuts novèls",
"account_not_locked_warning": "Vòstre compte es pas {0}. Qual que siá pòt vos seguir per veire vòstras publicacions destinadas pas qu'a vòstres seguidors.",
"account_not_locked_warning": "Vòstre compte es pas {0}. Qual que siá pòt vos seguir per veire vòstras publicacions destinadas pas qua vòstres seguidors.",
"account_not_locked_warning_link": "clavat",
"attachments_sensitive": "Marcar las pèças juntas coma sensiblas",
"content_type": {
"text/plain": "Tèxte brut"
"text/plain": "Tèxte brut",
"text/html": "HTML",
"text/markdown": "Markdown"
},
"content_warning": "Avís de contengut (opcional)",
"default": "Escrivètz aquí vòstre estatut.",
@ -118,12 +120,12 @@
"blocks_tab": "Blocatges",
"btnRadius": "Botons",
"cBlue": "Blau (Respondre, seguir)",
"cGreen": "Verd (Repartajar)",
"cGreen": "Verd (Repertir)",
"cOrange": "Irange (Aimar)",
"cRed": "Roge (Anullar)",
"change_password": "Cambiar lo senhal",
"change_password_error": "Una error ses producha en cambiant lo senhal.",
"changed_password": "Senhal corrèctament cambiat !",
"changed_password": "Senhal corrèctament cambiat!",
"collapse_subject": "Replegar las publicacions amb de subjèctes",
"composing": "Escritura",
"confirm_new_password": "Confirmatz lo nòu senhal",
@ -134,7 +136,7 @@
"default_vis": "Nivèl de visibilitat per defaut",
"delete_account": "Suprimir lo compte",
"delete_account_description": "Suprimir vòstre compte e los messatges per sempre.",
"delete_account_error": "Una error ses producha en suprimir lo compte. Saquò ten darribar mercés de contactar vòstre administrador dinstància.",
"delete_account_error": "Una error ses producha en suprimir lo compte. Saquò ten darribar mercés de contactar vòstre administrator dinstància.",
"delete_account_instructions": "Picatz vòstre senhal dins lo camp tèxte çai-jos per confirmar la supression del compte.",
"avatar_size_instruction": "La talha minimum recomandada pels imatges davatar es 150x150 pixèls.",
"export_theme": "Enregistrar la preconfiguracion",
@ -154,14 +156,14 @@
"hide_isp": "Amagar lo panèl especial instància",
"preload_images": "Precargar los imatges",
"use_one_click_nsfw": "Dobrir las pèças juntas NSFW amb un clic",
"hide_post_stats": "Amagar los estatistics de publicacion (ex. lo ombre de favorits)",
"hide_post_stats": "Amagar las estatisticas de publicacion (ex. lo nombre de favorits)",
"hide_user_stats": "Amagar las estatisticas de lutilizaire (ex. lo nombre de seguidors)",
"hide_filtered_statuses": "Amagar los estatuts filtrats",
"import_followers_from_a_csv_file": "Importar los seguidors dun fichièr csv",
"import_theme": "Cargar un tèma",
"inputRadius": "Camps tèxte",
"checkboxRadius": "Casas de marcar",
"instance_default": "(defaut : {value})",
"instance_default": "(defaut: {value})",
"instance_default_simple": "(defaut)",
"interface": "Interfàcia",
"interfaceLanguage": "Lenga de linterfàcia",
@ -172,7 +174,7 @@
"loop_video": "Bocla vidèo",
"loop_video_silent_only": "Legir en bocla solament las vidèos sens son (coma los « Gifs » de Mastodon)",
"mutes_tab": "Agamats",
"play_videos_in_modal": "Legir las vidèoas dirèctament dins la visualizaira mèdia",
"play_videos_in_modal": "Legir las vidèos dirèctament dins la visualizaira mèdia",
"use_contain_fit": "Talhar pas las pèças juntas per las vinhetas",
"name": "Nom",
"name_bio": "Nom & Bio",
@ -223,7 +225,7 @@
"post_status_content_type": "Publicar lo tipe de contengut dels estatuts",
"stop_gifs": "Lançar los GIFs al subrevòl",
"streaming": "Activar lo cargament automatic dels novèls estatus en anar amont",
"text": "Tèxt",
"text": "Tèxte",
"theme": "Tèma",
"theme_help_v2_1": "You can also override certain component's colors and opacity by toggling the checkbox, use \"Clear all\" button to clear all overrides.",
"theme_help_v2_2": "Icons underneath some entries are background/text contrast indicators, hover over for detailed info. Please keep in mind that when using transparency contrast indicators show the worst possible case.",
@ -234,6 +236,117 @@
"values": {
"false": "non",
"true": "òc"
},
"notifications": "Notificacions",
"enable_web_push_notifications": "Activar las notificacions web push",
"style": {
"switcher": {
"keep_color": "Gardar las colors",
"keep_shadows": "Gardar las ombras",
"keep_opacity": "Gardar lopacitat",
"keep_roundness": "Gardar la redondetat",
"keep_fonts": "Gardar las polissas",
"save_load_hint": "Las opcions « Gardar » permeton de servar las opcions configuradas actualament quand seleccionatz o cargatz un tèma, permeton tanben denregistrar aquelas opcions quand exportatz un tèma. Quand totas las casas son pas marcadas, lexportacion de tèma o enregistrarà tot.",
"reset": "Restablir",
"clear_all": "O escafar tot",
"clear_opacity": "Escafar lopacitat"
},
"common": {
"color": "Color",
"opacity": "Opacitat",
"contrast": {
"hint": "Lo coeficient de contraste es de {ratio}. Dòna {level} {context}",
"level": {
"aa": "un nivèl AA minimum recomandat",
"aaa": "un nivèl AAA recomandat",
"bad": "pas un nivèl daccessibilitat recomandat"
},
"context": {
"18pt": "pel tèxte grand (18pt+)",
"text": "pel tèxte"
}
}
},
"common_colors": {
"_tab_label": "Comun",
"main": "Colors comunas",
"foreground_hint": "Vejatz « Avançat » per mai de paramètres detalhats",
"rgbo": "Icònas, accents, badges"
},
"advanced_colors": {
"_tab_label": "Avançat",
"alert": "Rèire plan dalèrtas",
"alert_error": "Error",
"badge": "Rèire plan dels badges",
"badge_notification": "Notificacion",
"panel_header": "Bandièra del tablèu de bòrd",
"top_bar": "Barra amont",
"borders": "Caires",
"buttons": "Botons",
"inputs": "Camps tèxte",
"faint_text": "Tèxte descolorit"
},
"radii": {
"_tab_label": "Redondetat"
},
"shadows": {
"_tab_label": "Ombra e luminositat",
"component": "Compausant",
"override": "Subrecargar",
"shadow_id": "Ombra #{value}",
"blur": "Fosc",
"spread": "Espandiment",
"inset": "Incrustacion",
"hint": "Per las ombras podètz tanben utilizar --variable coma valor de color per emplegar una variable CSS3. Notatz que lo paramètre dopacitat foncionarà pas dins aquel cas.",
"filter_hint": {
"always_drop_shadow": "Avertiment, aquel ombra utiliza totjorn {0} quand lo navigator es compatible.",
"drop_shadow_syntax": "{0} es pas compatible amb lo paramètre {1} e lo mot clau {2}.",
"avatar_inset": "Notatz que combinar dombras incrustadas e pas incrustadas pòt donar de resultats inesperats amb los avatars transparents.",
"spread_zero": "Lombra amb un espandiment de > 0 apareisserà coma reglat a zèro",
"inset_classic": "Lombra dincrustacion utilizarà {0}"
},
"components": {
"panel": "Tablèu",
"panelHeader": "Bandièra del tablèu",
"topBar": "Barra amont",
"avatar": "Utilizar lavatar (vista perfil)",
"avatarStatus": "Avatar de lutilizaire (afichatge publicacion)",
"popup": "Fenèstras sorgissentas e astúcias",
"button": "Boton",
"buttonHover": "Boton (en passar la mirga)",
"buttonPressed": "Boton (en quichar)",
"buttonPressedHover": "Boton (en quichar e passar)",
"input": "Camp tèxte"
}
},
"fonts": {
"_tab_label": "Polissas",
"help": "Selecionatz la polissa dutilizar pels elements de lUI. Per « Personalizada » vos cal picar lo nom exacte tal coma apareis sul sistèma.",
"components": {
"interface": "Interfàcia",
"input": "Camps tèxte",
"post": "Tèxte de publicacion",
"postCode": "Tèxte Monospaced dins las publicacion (tèxte formatat)"
},
"family": "Nom de la polissa",
"size": "Talha (en px)",
"weight": "Largor (gras)",
"custom": "Personalizada"
},
"preview": {
"header": "Apercebut",
"content": "Contengut",
"error": "Error dexemple",
"button": "Boton",
"text": "A tròç de mai de {0} e {1}",
"mono": "contengut",
"input": "arribada al país.",
"faint_link": "manual dajuda",
"fine_print": "Legissètz nòstre {0} per legir pas res dutil!",
"header_faint": "Va plan",
"checkbox": "Ai legit los tèrmes e condicions dutilizacion",
"link": "un pichon ligam simpatic"
}
}
},
"timeline": {
@ -241,19 +354,21 @@
"conversation": "Conversacion",
"error_fetching": "Error en cercant de mesas a jorn",
"load_older": "Ne veire mai",
"no_retweet_hint": "Las publicacions marcadas pels seguidors solament o dirèctas se pòdon pas repetir",
"repeated": "repetit",
"show_new": "Ne veire mai",
"up_to_date": "A jorn",
"no_retweet_hint": "La publicacion marcada coma pels seguidors solament o dirècte pòt pas èsser repetida"
"no_more_statuses": "Pas mai destatuts",
"no_statuses": "Cap destatuts"
},
"status": {
"reply_to": "Respondre à",
"reply_to": "Respond a",
"replies_list": "Responsas:"
},
"user_card": {
"approve": "Validar",
"block": "Blocar",
"blocked": "Blocat !",
"blocked": "Blocat!",
"deny": "Refusar",
"favorites": "Favorits",
"follow": "Seguir",
@ -263,8 +378,8 @@
"follow_unfollow": "Quitar de seguir",
"followees": "Abonaments",
"followers": "Seguidors",
"following": "Seguit !",
"follows_you": "Vos sèc !",
"following": "Seguit!",
"follows_you": "Vos sèc!",
"its_you": "Sètz vos!",
"media": "Mèdia",
"mute": "Amagar",

View File

@ -51,7 +51,7 @@
"public_tl": "Linha do tempo pública",
"timeline": "Linha do tempo",
"twkn": "Toda a rede conhecida",
"user_search": "Busca de usuário",
"user_search": "Buscar usuários",
"who_to_follow": "Quem seguir",
"preferences": "Preferências"
},
@ -67,8 +67,8 @@
},
"post_status": {
"new_status": "Postar novo status",
"account_not_locked_warning": "Sua conta não está {0}. Qualquer pessoa pode te seguir para ver seus posts restritos.",
"account_not_locked_warning_link": "fechada",
"account_not_locked_warning": "Sua conta não é {0}. Qualquer pessoa pode te seguir e ver seus posts privados (só para seguidores).",
"account_not_locked_warning_link": "restrita",
"attachments_sensitive": "Marcar anexos como sensíveis",
"content_type": {
"text/plain": "Texto puro"
@ -115,7 +115,7 @@
"avatarRadius": "Avatares",
"background": "Pano de Fundo",
"bio": "Biografia",
"blocks_tab": "Blocos",
"blocks_tab": "Bloqueios",
"btnRadius": "Botões",
"cBlue": "Azul (Responder, seguir)",
"cGreen": "Verde (Repetir)",
@ -125,7 +125,7 @@
"change_password_error": "Houve um erro ao modificar sua senha.",
"changed_password": "Senha modificada com sucesso!",
"collapse_subject": "Esconder posts com assunto",
"composing": "Escrevendo",
"composing": "Escrita",
"confirm_new_password": "Confirmar nova senha",
"current_avatar": "Seu avatar atual",
"current_password": "Sua senha atual",
@ -139,7 +139,7 @@
"avatar_size_instruction": "O tamanho mínimo recomendado para imagens de avatar é 150x150 pixels.",
"export_theme": "Salvar predefinições",
"filtering": "Filtragem",
"filtering_explanation": "Todas as postagens contendo estas palavras serão silenciadas, uma por linha.",
"filtering_explanation": "Todas as postagens contendo estas palavras serão silenciadas; uma palavra por linha.",
"follow_export": "Exportar quem você segue",
"follow_export_button": "Exportar quem você segue para um arquivo CSV",
"follow_export_processing": "Processando. Em breve você receberá a solicitação de download do arquivo",
@ -178,7 +178,7 @@
"name_bio": "Nome & Biografia",
"new_password": "Nova senha",
"notification_visibility": "Tipos de notificação para mostrar",
"notification_visibility_follows": "Seguidos",
"notification_visibility_follows": "Seguidas",
"notification_visibility_likes": "Favoritos",
"notification_visibility_mentions": "Menções",
"notification_visibility_repeats": "Repetições",
@ -187,7 +187,7 @@
"no_mutes": "Sem silenciados",
"hide_follows_description": "Não mostrar quem estou seguindo",
"hide_followers_description": "Não mostrar quem me segue",
"show_admin_badge": "Mostrar distintivo de Administrador em meu perfil",
"show_admin_badge": "Mostrar título de Administrador em meu perfil",
"show_moderator_badge": "Mostrar título de Moderador em meu perfil",
"nsfw_clickthrough": "Habilitar clique para ocultar anexos sensíveis",
"oauth_tokens": "Token OAuth",
@ -201,9 +201,9 @@
"profile_background": "Pano de fundo de perfil",
"profile_banner": "Capa de perfil",
"profile_tab": "Perfil",
"radii_help": "Arredondar arestas da interface (em píxeis)",
"radii_help": "Arredondar arestas da interface (em pixel)",
"replies_in_timeline": "Respostas na linha do tempo",
"reply_link_preview": "Habilitar a pré-visualização de link de respostas ao passar o mouse.",
"reply_link_preview": "Habilitar a pré-visualização de de respostas ao passar o mouse.",
"reply_visibility_all": "Mostrar todas as respostas",
"reply_visibility_following": "Só mostrar respostas direcionadas a mim ou a usuários que sigo",
"reply_visibility_self": "Só mostrar respostas direcionadas a mim",
@ -212,7 +212,7 @@
"security_tab": "Segurança",
"scope_copy": "Copiar opções de privacidade ao responder (Mensagens diretas sempre copiam)",
"set_new_avatar": "Alterar avatar",
"set_new_profile_background": "Alterar o plano de fundo de perfil",
"set_new_profile_background": "Alterar o pano de fundo de perfil",
"set_new_profile_banner": "Alterar capa de perfil",
"settings": "Configurações",
"subject_input_always_show": "Sempre mostrar campo de assunto",
@ -220,9 +220,9 @@
"subject_line_email": "Como em email: \"re: assunto\"",
"subject_line_mastodon": "Como o Mastodon: copiar como está",
"subject_line_noop": "Não copiar",
"post_status_content_type": "Postar tipo de conteúdo do status",
"stop_gifs": "Reproduzir GIFs ao passar o cursor em cima",
"streaming": "Habilitar o fluxo automático de postagens quando ao topo da página",
"post_status_content_type": "Tipo de conteúdo do status",
"stop_gifs": "Reproduzir GIFs ao passar o cursor",
"streaming": "Habilitar o fluxo automático de postagens no topo da página",
"text": "Texto",
"theme": "Tema",
"theme_help": "Use cores em código hexadecimal (#rrggbb) para personalizar seu esquema de cores.",
@ -235,7 +235,7 @@
"false": "não",
"true": "sim"
},
"notifications": "Notifications",
"notifications": "Notificações",
"enable_web_push_notifications": "Habilitar notificações web push",
"style": {
"switcher": {
@ -245,7 +245,7 @@
"keep_roundness": "Manter arredondado",
"keep_fonts": "Manter fontes",
"save_load_hint": "Manter as opções preserva as opções atuais ao selecionar ou carregar temas; também salva as opções ao exportar um tempo. Quanto todos os campos estiverem desmarcados, tudo será salvo ao exportar o tema.",
"reset": "Voltar ao padrão",
"reset": "Restaurar o padrão",
"clear_all": "Limpar tudo",
"clear_opacity": "Limpar opacidade"
},
@ -319,7 +319,7 @@
},
"fonts": {
"_tab_label": "Fontes",
"help": "Selecionar fonte dos elementos da interface. Para fonte \"personalizada\" você deve entrar exatamente o nome da fonte no sistema.",
"help": "Selecione as fontes dos elementos da interface. Para fonte \"personalizada\" você deve inserir o mesmo nome da fonte no sistema.",
"components": {
"interface": "Interface",
"input": "Campo de entrada",
@ -383,7 +383,7 @@
"mute": "Silenciar",
"muted": "Silenciado",
"per_day": "por dia",
"remote_follow": "Seguidor Remoto",
"remote_follow": "Seguir remotamente",
"statuses": "Postagens",
"unblock": "Desbloquear",
"unblock_progress": "Desbloqueando...",

View File

@ -48,7 +48,11 @@ const defaultState = {
// Html stuff
instanceSpecificPanelContent: '',
tos: ''
tos: '',
// Version Information
backendVersion: '',
frontendVersion: ''
}
const instance = {

View File

@ -561,7 +561,12 @@ const fetchOAuthTokens = ({credentials}) => {
return fetch(url, {
headers: authHeaders(credentials)
}).then((data) => data.json())
}).then((data) => {
if (data.ok) {
return data.json()
}
throw new Error('Error fetching auth tokens', data)
})
}
const revokeOAuthToken = ({id, credentials}) => {

View File

@ -40,10 +40,10 @@ export const parseUser = (data) => {
}
// output.name = ??? missing
output.name_html = data.display_name
output.name_html = addEmojis(data.display_name, data.emojis)
// output.description = ??? missing
output.description_html = data.note
output.description_html = addEmojis(data.note, data.emojis)
// Utilize avatar_static for gif avatars?
output.profile_image_url = data.avatar
@ -146,6 +146,14 @@ const parseAttachment = (data) => {
return output
}
export const addEmojis = (string, emojis) => {
return emojis.reduce((acc, emoji) => {
return acc.replace(
new RegExp(`:${emoji.shortcode}:`, 'g'),
`<img src='${emoji.url}' alt='${emoji.shortcode}' class='emoji' />`
)
}, string)
}
export const parseStatus = (data) => {
const output = {}
@ -161,7 +169,7 @@ export const parseStatus = (data) => {
output.type = data.reblog ? 'retweet' : 'status'
output.nsfw = data.sensitive
output.statusnet_html = data.content
output.statusnet_html = addEmojis(data.content, data.emojis)
// Not exactly the same but works?
output.text = data.content
@ -180,7 +188,7 @@ export const parseStatus = (data) => {
}
output.summary = data.spoiler_text
output.summary_html = data.spoiler_text
output.summary_html = addEmojis(data.spoiler_text, data.emojis)
output.external_url = data.url
// output.is_local = ??? missing

View File

@ -0,0 +1,6 @@
export const extractCommit = versionString => {
const regex = /-g(\w+)$/i
const matches = versionString.match(regex)
return matches ? matches[1] : ''
}

View File

@ -1,4 +1,4 @@
import { parseStatus, parseUser, parseNotification } from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js'
import { parseStatus, parseUser, parseNotification, addEmojis } from '../../../../../src/services/entity_normalizer/entity_normalizer.service.js'
import mastoapidata from '../../../../fixtures/mastoapi.json'
import qvitterapidata from '../../../../fixtures/statuses.json'
@ -143,6 +143,23 @@ const makeMockNotificationQvitter = (overrides = {}) => {
}, overrides)
}
const makeMockEmojiMasto = (overrides = [{}]) => {
return [
Object.assign({
shortcode: 'image',
static_url: 'https://example.com/image.png',
url: 'https://example.com/image.png',
visible_in_picker: false
}, overrides[0]),
Object.assign({
shortcode: 'thinking',
static_url: 'https://example.com/think.png',
url: 'https://example.com/think.png',
visible_in_picker: false
}, overrides[1])
]
}
parseNotification
parseUser
parseStatus
@ -218,6 +235,22 @@ describe('API Entities normalizer', () => {
expect(parsedRepeat).to.have.property('retweeted_status')
expect(parsedRepeat).to.have.deep.property('retweeted_status.id', 'deadbeef')
})
it('adds emojis to post content', () => {
const post = makeMockStatusMasto({ emojis: makeMockEmojiMasto(), content: 'Makes you think :thinking:' })
const parsedPost = parseStatus(post)
expect(parsedPost).to.have.property('statusnet_html').that.contains('<img')
})
it('adds emojis to subject line', () => {
const post = makeMockStatusMasto({ emojis: makeMockEmojiMasto(), spoiler_text: 'CW: 300 IQ :thinking:' })
const parsedPost = parseStatus(post)
expect(parsedPost).to.have.property('summary_html').that.contains('<img')
})
})
})
@ -230,6 +263,22 @@ describe('API Entities normalizer', () => {
expect(parseUser(local)).to.have.property('is_local', true)
expect(parseUser(remote)).to.have.property('is_local', false)
})
it('adds emojis to user name', () => {
const user = makeMockUserMasto({ emojis: makeMockEmojiMasto(), display_name: 'The :thinking: thinker' })
const parsedUser = parseUser(user)
expect(parsedUser).to.have.property('name_html').that.contains('<img')
})
it('adds emojis to user bio', () => {
const user = makeMockUserMasto({ emojis: makeMockEmojiMasto(), note: 'Hello i like to :thinking: a lot' })
const parsedUser = parseUser(user)
expect(parsedUser).to.have.property('description_html').that.contains('<img')
})
})
// We currently use QvitterAPI notifications only, and especially due to MastoAPI lacking is_seen, support for MastoAPI
@ -267,4 +316,28 @@ describe('API Entities normalizer', () => {
expect(parseNotification(notif)).to.have.deep.property('from_profile.id', 'spurdo')
})
})
describe('MastoAPI emoji adder', () => {
const emojis = makeMockEmojiMasto()
const imageHtml = '<img src="https://example.com/image.png" alt="image" class="emoji" />'
.replace(/"/g, '\'')
const thinkHtml = '<img src="https://example.com/think.png" alt="thinking" class="emoji" />'
.replace(/"/g, '\'')
it('correctly replaces shortcodes in supplied string', () => {
const result = addEmojis('This post has :image: emoji and :thinking: emoji', emojis)
expect(result).to.include(thinkHtml)
expect(result).to.include(imageHtml)
})
it('handles consecutive emojis correctly', () => {
const result = addEmojis('Lelel emoji spam :thinking::thinking::thinking::thinking:', emojis)
expect(result).to.include(thinkHtml + thinkHtml + thinkHtml + thinkHtml)
})
it('Doesn\'t replace nonexistent emojis', () => {
const result = addEmojis('Admin add the :tenshi: emoji', emojis)
expect(result).to.equal('Admin add the :tenshi: emoji')
})
})
})