Porównaj commity

...

10 Commity

Autor SHA1 Wiadomość Data
Nolan Lawson 750235cd8f 2.5.1 2022-12-26 11:35:49 -08:00
Nolan Lawson b5cad87aaf
fix: lighten button colors on some themes (#2331) 2022-12-26 11:29:12 -08:00
Nick Colley a85ff62d48
fix: pitchback svgs not being visible (#2328) 2022-12-26 11:27:53 -08:00
Nick Colley e06f63684e
fix: improve dark theme icons (#2327) 2022-12-26 11:26:58 -08:00
Nick Colley f81778d37f
fix: improve icon readability in light theme (#2323)
Boost contrast of the default colour theme, to be closer to
the other theme's saturation then boost the unpressed state for action
buttons.

This brings the icons to 3:1 contrast while keeping colour in themes.

Co-authored-by: Nolan Lawson <nolan@nolanlawson.com>
2022-12-18 12:07:53 -08:00
Nick Colley 746298a1f7
fix: pitchblack theme unpressed icons readability (#2324)
increase contrast so they're more readable.
2022-12-18 11:20:58 -08:00
Nolan Lawson 02f1dad098
fix: handle status edit events (#2325) 2022-12-18 11:20:17 -08:00
Nick Colley 3edfed971f
fix: notification page contrast (#2302)
Use lowest possible contrast gray that meets WCAG requirements for
very deemphasised text.

Makes the notification page more readable without compromising access.

Co-authored-by: Nolan Lawson <nolan@nolanlawson.com>

Co-authored-by: Nolan Lawson <nolan@nolanlawson.com>
2022-12-17 18:12:13 +00:00
Noelia Ruiz Martínez d71430f86d
feat: translation into Spanish (#2281)
Co-authored-by: Nolan Lawson <nolan@nolanlawson.com>
Co-authored-by: Noelia Ruiz Martínez <n4m1977@gmail.com>
2022-12-17 09:47:51 -08:00
Noelia Ruiz Martínez 6124c948de
fix: communicate expanded state of tooltips to screenreaders (#2322)
Co-authored-by: Nolan Lawson <nolan@nolanlawson.com>
2022-12-17 09:47:02 -08:00
22 zmienionych plików z 874 dodań i 45 usunięć

Wyświetl plik

@ -4,12 +4,14 @@ import { DEFAULT_LOCALE, LOCALE } from '../src/routes/_static/intl.js'
import enUS from '../src/intl/en-US.js'
import fr from '../src/intl/fr.js'
import de from '../src/intl/de.js'
import es from '../src/intl/es.js'
// TODO: make it so we don't have to explicitly list these out
const locales = {
'en-US': enUS,
fr,
de
de,
es
}
const intl = locales[LOCALE]

Wyświetl plik

@ -1,7 +1,7 @@
{
"name": "pinafore",
"description": "Alternative web client for Mastodon",
"version": "2.5.0",
"version": "2.5.1",
"type": "module",
"engines": {
"node": "^12.20.0 || ^14.13.1 || ^16.0.0 || ^18.0.0"

696
src/intl/es.js 100644
Wyświetl plik

@ -0,0 +1,696 @@
export default {
// Home page, basic <title> and <description>
appName: 'Pinafore',
appDescription: 'Un cliente web alternativo para Mastodon, centrado en la velocidad y la sencillez.',
homeDescription: `
<p>
Pinafore es un cliente web para
<a rel="noopener" target="_blank" href="https://joinmastodon.org">Mastodon</a>,
diseñado para ser rápido y sencillo.
</p>
<p>
Lee el
<a rel="noopener" target="_blank"
href="https://nolanlawson.com/2018/04/09/introducing-pinafore-for-mastodon/">artículo introductorio en el blog</a>,
o comienza iniciando sesión en una instancia:
</p>`,
logIn: 'Iniciar sesión',
footer: `
<p>
Pinafore es
<a rel="noopener" target="_blank" href="https://github.com/nolanlawson/pinafore">software de código abierto</a>
creado por
<a rel="noopener" target="_blank" href="https://nolanlawson.com">Nolan Lawson</a>
y distribuido bajo la
<a rel="noopener" target="_blank"
href="https://github.com/nolanlawson/pinafore/blob/master/LICENSE">Licencia AGPL</a>.
Aquí está la <a href="/settings/about#privacy-policy" rel="prefetch">política de privacidad</a>.
</p>
`,
// Manifest
longAppName: 'Pinafore para Mastodon',
newStatus: 'Nuevo toot',
// Generic UI
loading: 'Cargando',
okay: 'OK',
cancel: 'Cancelar',
alert: 'Alerta',
close: 'Cerrar',
error: 'Error: {error}',
errorShort: 'Error:',
// Relative timestamps
justNow: 'ahora mismo',
// Navigation, page titles
navItemLabel: `
{label} {selected, select,
true {(página actual)}
other {}
} {name, select,
notifications {{count, plural,
=0 {}
one {(1 notificación)}
other {({count} notificaciones)}
}}
community {{count, plural,
=0 {}
one {(1 solicitud de seguimiento)}
other {({count} solicitudes de seguimiento)}
}}
other {}
}
`,
blockedUsers: 'Usuarios bloqueados',
bookmarks: 'Marcadores',
directMessages: 'Mensajes directos',
favorites: 'Favoritos',
federated: 'Federada',
home: 'Inicio',
local: 'Local',
notifications: 'Notificaciones',
mutedUsers: 'Usuarios silenciados',
pinnedStatuses: 'Toots fijados',
followRequests: 'Solicitudes de seguimiento',
followRequestsLabel: `Solicitudes de seguimiento {hasFollowRequests, select,
true {({count})}
other {}
}`,
list: 'Lista',
search: 'Buscar',
pageHeader: 'Encabezado de página',
goBack: 'Retroceder',
back: 'Atrás',
profile: 'Perfil',
federatedTimeline: 'Cronología federada',
localTimeline: 'Cronología local',
// community page
community: 'Comunidad',
pinnableTimelines: 'Cronologías que puedes fijar',
timelines: 'Cronologías',
lists: 'Listas',
instanceSettings: 'Opciones para instancia',
notificationMentions: 'Notificación de menciones',
profileWithMedia: 'Perfil con multimedia',
profileWithReplies: 'Perfil con respuestas',
hashtag: 'Hashtag',
// not logged in
profileNotLoggedIn: 'Aquí se mostrará una cronología de usuario cuando hayas iniciado sesión.',
bookmarksNotLoggedIn: 'Tus marcadores se mostrarán aquí cuando hayas iniciado sesión.',
directMessagesNotLoggedIn: 'Tus mensajes directos se mostrarán aquí cuando hayas iniciado sesión.',
favoritesNotLoggedIn: 'Tus favoritos se mostrarán aquí cuando hayas iniciado sesión.',
federatedTimelineNotLoggedIn: 'Tu cronología federada se mostrará aquí cuando hayas iniciado sesión.',
localTimelineNotLoggedIn: 'Tu cronología localse mostrará aquí cuando hayas iniciado sesión.',
searchNotLoggedIn: 'Puedes buscar una vez que inicias sesión en una instancia.',
communityNotLoggedIn: 'Las opciones para comunidad se mostrarán aquí cuando hayas iniciado sesión.',
listNotLoggedIn: 'Aquí se mostrará una lista cuando hayas iniciado sesión.',
notificationsNotLoggedIn: 'Tus notificaciones se mostrarán aquí cuando hayas iniciado sesión.',
notificationMentionsNotLoggedIn: 'Las notificaciones de tus menciones se mostrarán aquí cuando hayas iniciado sesión.',
statusNotLoggedIn: 'Aquí se mostrará un hilo de toots cuando hayas iniciado sesión.',
tagNotLoggedIn: 'Aquí se mostrará una cronología de hashtags cuando hayas iniciado sesión.',
// Notification subpages
filters: 'Filtros',
all: 'Todo',
mentions: 'Menciones',
// Follow requests
approve: 'Aceptar',
reject: 'Rechazar',
// Hotkeys
hotkeys: 'Atajos de teclado',
global: 'Globales',
timeline: 'Cronología',
media: 'Multimedia',
globalHotkeys: `
{leftRightChangesFocus, select,
true {
<li><kbd></kbd> para ir al elemento enfocable siguiente</li>
<li><kbd></kbd> para ir al elemento enfocable anterior</li>
}
other {}
}
<li>
<kbd>1</kbd> - <kbd>6</kbd>
{leftRightChangesFocus, select,
true {}
other {o <kbd></kbd>/<kbd></kbd>}
}
para cambiar de columna
</li>
<li><kbd>7</kbd> o <kbd>c</kbd> para redactar un nuevo toot</li>
<li><kbd>s</kbd> o <kbd>/</kbd> para buscar</li>
<li><kbd>g</kbd> + <kbd>h</kbd> para ir a inicio</li>
<li><kbd>g</kbd> + <kbd>n</kbd> para ir a notificaciones</li>
<li><kbd>g</kbd> + <kbd>l</kbd> to para ir a la cronología local</li>
<li><kbd>g</kbd> + <kbd>t</kbd> para ir a la cronología federada</li>
<li><kbd>g</kbd> + <kbd>c</kbd> para ir a la página comunidad</li>
<li><kbd>g</kbd> + <kbd>d</kbd> para ir a la página de mensajes directos</li>
<li><kbd>h</kbd> o <kbd>?</kbd> para abrir o cerrar el diálogo de ayuda</li>
<li><kbd>Backspace</kbd> para retroceder, cerrar diálogos</li>
`,
timelineHotkeys: `
<li><kbd>j</kbd> o <kbd></kbd> para activar el toot siguiente</li>
<li><kbd>k</kbd> o <kbd></kbd> para activar el toot anterior</li>
<li><kbd>.</kbd> para mostrar más y desplazarse al principio</li>
<li><kbd>o</kbd> para abrir</li>
<li><kbd>f</kbd> para marcar como favorito</li>
<li><kbd>b</kbd> para reenviar</li>
<li><kbd>r</kbd> para responder</li>
<li><kbd>Escape</kbd> para cerrar respuesta</li>
<li><kbd>a</kbd> para marcador</li>
<li><kbd>i</kbd> para abrir imágenes, vídeo o audio</li>
<li><kbd>y</kbd> para mostrar u ocultar multimedia sensible</li>
<li><kbd>m</kbd> para mencionar al autor</li>
<li><kbd>p</kbd> para abrir el perfil del autor</li>
<li><kbd>l</kbd> para abrir el enlace de la publicación en una nueva pestaña</li>
<li><kbd>x</kbd> para mostrar u ocultar el texto tras una advertencia de contenido</li>
<li><kbd>z</kbd> para mostrar u ocultar todas las advertencias de contenido en un hilo</li>
`,
mediaHotkeys: `
<li><kbd></kbd> / <kbd></kbd> para ir a siguiente o anterior</li>
`,
// Community page, tabs
tabLabel: `{label} {current, select,
true {(Actual)}
other {}
}`,
pageTitle: `
{hasNotifications, select,
true {({count})}
other {}
}
{name}
·
{showInstanceName, select,
true {{instanceName}}
other {Pinafore}
}
`,
pinLabel: `{label} {pinnable, select,
true {
{pinned, select,
true {(página fijada)}
other {(Página no fijada)}
}
}
other {}
}`,
pinPage: 'Fijar {label}',
// Status composition
composeStatus: 'Redactar toot',
postStatus: 'Toot!',
contentWarning: 'Advertencia de contenido',
dropToUpload: 'Soltar para subir',
invalidFileType: 'Tipo de fichero no válido',
composeLabel: '¿En qué estás pensando?',
autocompleteDescription: 'Cuando haya disponibles resultados de autocompletado, pulsa las flechas arriba o abajo y enter para seleccionar.',
mediaUploads: 'Subidas multimedia',
edit: 'Editar',
delete: 'Borrar',
description: 'Descripción',
descriptionLabel: 'Describir para las personas con discapacidad visual (imagen, vídeo) o con discapacidad auditiva (audio, vídeo)',
markAsSensitive: 'Marcar multimedia como sensible',
// Polls
createPoll: 'Crear encuesta',
removePollChoice: 'Eliminar opción {index}',
pollChoiceLabel: 'Opción {index}',
multipleChoice: 'Selección múltiple',
pollDuration: 'Duración de la encuesta',
fiveMinutes: '5 minutos',
thirtyMinutes: '30 minutos',
oneHour: '1 hora',
sixHours: '6 horas',
twelveHours: '12 horas',
oneDay: '1 día',
threeDays: '3 días',
sevenDays: '7 días',
never: 'Nunca',
addEmoji: 'Insertar emoji',
addMedia: 'Añadir multimedia (imágenes, vídeo, audio)',
addPoll: 'Añadir encuesta',
removePoll: 'Eliminar encuesta',
postPrivacyLabel: 'Ajustar privacidad (actualmente {label})',
addContentWarning: 'Añadir advertencia de contenido',
removeContentWarning: 'Eliminar advertencia de contenido',
altLabel: 'Describir para las personas con discapacidad visual',
extractText: 'Extraer texto de imagen',
extractingText: 'Extrayendo texto…',
extractingTextCompletion: 'Extrayendo texto ({percent}% completado)…',
unableToExtractText: 'No se puede extraer texto.',
// Account options
followAccount: 'Seguir a {account}',
unfollowAccount: 'Dejar de seguir a {account}',
blockAccount: 'Bloquear a {account}',
unblockAccount: 'Desbloquear a {account}',
muteAccount: 'Silenciar a {account}',
unmuteAccount: 'Dejar de silenciar a Unmute {account}',
showReblogsFromAccount: 'Mostrar toots reenviados por {account}',
hideReblogsFromAccount: 'Ocultar toots reenviados por {account}',
showDomain: 'Dejar de ocultar {domain}',
hideDomain: 'Ocultar {domain}',
reportAccount: 'Denunciar a {account}',
mentionAccount: 'Mencionar a {account}',
copyLinkToAccount: 'Copiar enlace a cuenta',
copiedToClipboard: 'Copiado al portapapeles',
// Media dialog
navigateMedia: 'Navegar por elementos multimedia',
showPreviousMedia: 'Mostrar multimedia anterior',
showNextMedia: 'Mostrar multimedia siguiente',
enterPinchZoom: 'Modo pinch-zoom',
exitPinchZoom: 'Salir del modo pinch-zoom',
showMedia: `Mostrar {index, select,
1 {primer}
2 {segundo}
3 {tercero}
other {cuarto}
} multimedia {current, select,
true {(actual)}
other {}
}`,
previewFocalPoint: 'Previsualizar (punto focal)',
enterFocalPoint: 'Introducir el punto focal (X, Y) para este multimedia',
muteNotifications: 'Silenciar también las notificaciones',
muteAccountConfirm: '¿Silenciar a {account}?',
mute: 'Silenciar',
unmute: 'Dejar de silenciar',
zoomOut: 'Alejar',
zoomIn: 'Acercar',
// Reporting
reportingLabel: 'Estás denunciando a {account} a los moderadores de {instance}.',
additionalComments: 'Comentarios adicionales',
forwardDescription: '?Reenviar también a los moderadores de {instance}?',
forwardLabel: 'Reenviar a {instance}',
unableToLoadStatuses: 'No se pueden cargar los toots recientes: {error}',
report: 'Denunciar',
noContent: '(Sin contenido)',
noStatuses: 'No hay toots para denunciar',
// Status options
unpinFromProfile: 'Dejar de fijar en el perfil',
pinToProfile: 'Fijar en el perfil',
muteConversation: 'Silenciar conversación',
unmuteConversation: 'Dejar de silenciar conversación',
bookmarkStatus: 'Poner marcador al toot',
unbookmarkStatus: 'Quitar marcador al toot',
deleteAndRedraft: 'Borrar y volver a redactar',
reportStatus: 'Denunciar toot',
shareStatus: 'Compartir toot',
copyLinkToStatus: 'Copiar enlace al toot',
// Account profile
profileForAccount: 'Perfil para {account}',
statisticsAndMoreOptions: 'Estadísticas y más opciones',
statuses: 'Toots',
follows: 'Siguiendo',
followers: 'Seguidores',
moreOptions: 'Más opciones',
followersLabel: 'Te han seguido {count}',
followingLabel: 'Has seguido a {count}',
followLabel: `Seguimiento {requested, select,
true {(solicitud de seguimiento)}
other {}
}`,
unfollowLabel: `Dejar de seguir {requested, select,
true {(solicitud de seguimiento)}
other {}
}`,
notify: 'Suscribirse a {account}',
denotify: 'Cancelar suscripción a {account}',
subscribedAccount: 'Te has suscrito a la cuenta',
unsubscribedAccount: 'Has cancelado tu suscripción a la cuenta',
unblock: 'Desbloquear',
nameAndFollowing: 'Nombre y seguimientos',
clickToSeeAvatar: 'Haz clic para ver el avatar',
opensInNewWindow: '{label} (Se abre en nueva ventana)',
blocked: 'Bloqueado',
domainHidden: 'Dominio oculto',
muted: 'Silenciado',
followsYou: 'Te está siguiendo',
avatarForAccount: 'Avatar para {account}',
fields: 'Campos',
accountHasMoved: '{account} se ha trasladado:',
profilePageForAccount: 'Página de perfil para {account}',
// About page
about: 'Acerca de',
aboutApp: 'Acerca de Pinafore',
aboutAppDescription: `
<p>
Pinafore es
<a rel="noopener" target="_blank"
href="https://github.com/nolanlawson/pinafore">software libre y de código abierto</a>
creado por
<a rel="noopener" target="_blank" href="https://nolanlawson.com">Nolan Lawson</a>
y distribuido bajo la
<a rel="noopener" target="_blank"
href="https://github.com/nolanlawson/pinafore/blob/master/LICENSE">GNU Affero General Public License</a>.
</p>
<h2 id="privacy-policy">Política de privacidad</h2>
<p>
Pinafore no almacena ninguna información personal en sus servidores,
incluyendo, pero no limitándose a nombres, direcciones de correo electrónico,
direcciones IP, posts y fotos.
</p>
<p>
Pinafore es un sitio estático. Todos los datos son almacenados en tu navegador y compartidos con las instancias del fediverso
a las que te conectas.
</p>
<h2>Créditos</h2>
<p>
Iconos proporcionados por <a rel="noopener" target="_blank" href="http://fontawesome.io/">Font Awesome</a>.
</p>
<p>
Logo gracias a "sailboat" por Gregor Cresnar, de
<a rel="noopener" target="_blank" href="https://thenounproject.com/">the Noun Project</a>.
</p>`,
// Settings
settings: 'Opciones de configuración',
general: 'General',
generalSettings: 'Opciones generales',
showSensitive: 'Mostrar multimedia sensible por defecto',
showPlain: 'Mostrar un color gris liso para multimedia sensible',
allSensitive: 'Tratar todo multimedia como sensible',
largeMedia: 'Mostrar imágenes y vídeos grandes incrustados',
autoplayGifs: 'Reproducir automáticamente GIFs animados',
hideCards: 'Ocultar paneles de previsualización de enlaces',
underlineLinks: 'Subrayar enlaces en toots y perfiles',
accessibility: 'Accesibilidad',
reduceMotion: 'Reducir movimiento en animaciones de la interfaz',
disableTappable: 'Deshabilitar área para tocar en todo el toot',
removeEmoji: 'Eliminar emoji de nombres de usuario',
shortAria: 'Usar etiquetas ARIA cortas para artículos',
theme: 'Diseño visual',
themeForInstance: 'Diseño visual para {instance}',
disableCustomScrollbars: 'Deshabilitar barras deslizantes personalizadas',
bottomNav: 'Situar la barra de navegación al final de la pantalla',
centerNav: 'Centrar la barra de navegación',
preferences: 'Preferencias',
hotkeySettings: 'Opciones para atajos de teclado',
disableHotkeys: 'Deshabilitar todos los atajos de teclado',
leftRightArrows: 'Las flechas izquierda/derecha cambian el foco en vez de columnas/multimedia',
guide: 'Guía',
reload: 'Recargar',
// Wellness settings
wellness: 'Bienestar',
wellnessSettings: 'Opciones para el bienestar',
wellnessDescription: `Las opciones para el bienestar están diseñadas para reducir los aspectos que inducen adicción o ansiedad en las redes sociales.
Elige cualquier opción que vaya bien para ti.`,
enableAll: 'Habilitar todos',
metrics: 'Métricas',
hideFollowerCount: 'Ocultar recuento de seguidores (hasta 10)',
hideReblogCount: 'Ocultar recuento de reenvíos',
hideFavoriteCount: 'Ocultar recuento de favoritos',
hideUnread: 'Ocultar recuento de notificaciones sin leer (es decir, el punto rojo)',
// The quality that makes something seem important or interesting because it seems to be happening now
immediacy: 'Inmediatez',
showAbsoluteTimestamps: 'Mostrar marcas de tiempo absolutas (p.ej., "3 de marzo") en vez de marcas de tiempo relativas (p. ej., "hace 5 minutos")',
ui: 'Interfaz',
grayscaleMode: 'Modo escala de grises',
wellnessFooter: `Estas opciones están parcialmente basadas en pautas del
<a rel="noopener" target="_blank" href="https://humanetech.com">Center for Humane Technology</a>.`,
// This is a link: "You can filter or disable notifications in the _instance settings_"
filterNotificationsPre: 'Puedes filtrar o deshabilitar notificaciones en',
filterNotificationsText: 'opciones para instancia',
filterNotificationsPost: '',
// Custom tooltips, like "Disable _infinite scroll_", where you can click _infinite scroll_
// to see a description. It's hard to properly internationalize, so we just break up the strings.
disableInfiniteScrollPre: 'Deshabilitar',
disableInfiniteScrollText: 'desplazamiento infinito',
disableInfiniteScrollDescription: `Cuando el desplazamiento infinito esté deshabilitado, los nuevos toots no se mostrarán automáticamente al final o al principio de la cronología. En vez de esto, habrá botones que te permitirán
cargar más contenido a demanda.`,
disableInfiniteScrollPost: '',
// Instance settings
loggedInAs: 'Iniciaste sesión como',
homeTimelineFilters: 'Filtros para la cronología Inicio',
notificationFilters: 'Filtros para notificaciones',
pushNotifications: 'Notificaciones Push',
// Add instance page
storageError: `Parece que Pinafore no puede almacenar datos localmente. ¿Está tu navegador en modo privado
o bloqueando las cookies? Pinafore almacena todos los datos localmente, y requiere LocalStorage e
IndexedDB para funcionar correctamente.`,
javaScriptError: 'Debes habilitar JavaScript para iniciar sesión.',
enterInstanceName: 'Introducir nombre de instancia',
instanceColon: 'Instancia:',
// Custom tooltip, concatenated together
getAnInstancePre: '¿No tienes una',
getAnInstanceText: 'instancia',
getAnInstanceDescription: 'Una instancia es tu servidor de inicio de Mastodon, por ejemplo, mastodon.social o cybre.space.',
getAnInstancePost: '?',
joinMastodon: '¡Unirse a Mastodon!',
instancesYouveLoggedInTo: 'Instancias en las que has iniciado sesión:',
addAnotherInstance: 'Añadir otra instancia',
youreNotLoggedIn: 'No has iniciado sesión en ninguna instancia.',
currentInstanceLabel: `{instance} {current, select,
true {(instancia actual)}
other {}
}`,
// Link text
logInToAnInstancePre: '',
logInToAnInstanceText: 'Inicia sesión en una instancia',
logInToAnInstancePost: 'para empezar a usar Pinafore.',
// Another custom tooltip
showRingPre: 'Mostrar siempre',
showRingText: 'anillo del foco',
showRingDescription: 'El anillo del foco es el contorno que muestra el elemento que actualmente tiene el foco. Por defecto solo se muestra cuando se usa el teclado (no el ratón o un dispositivo táctil), pero puedes elegir mostrarlo siempre.',
showRingPost: '',
instances: 'Instancias',
addInstance: 'Añadir instancia',
homeTimelineFilterSettings: 'Opciones para filtros de la cronología Inicio',
showReblogs: 'Mostrar reenvíos',
showReplies: 'Mostrar respuestas',
switchOrLogOut: 'Seleccionar o cerrar sesión en esta instancia',
switchTo: 'Seleccionar esta instancia',
switchToInstance: 'Seleccionar instancia',
switchToNameOfInstance: 'Seleccionar {instance}',
logOut: 'Cerrar sesión',
logOutOfInstanceConfirm: '¿Cerrar sesión en {instance}?',
notificationFilterSettings: 'Opciones para filtros de notificaciones',
// Push notifications
browserDoesNotSupportPush: 'Tu navegador no admite notificaciones Push.',
deniedPush: 'Has denegado el permiso para mostrar notificaciones.',
pushNotificationsNote: 'Observa que solo puedes recibir notificaciones Push para una instancia al mismo tiempo.',
pushSettings: 'Opciones para notificaciones Push',
newFollowers: 'Nuevos seguidores',
reblogs: 'Reenvíos',
pollResults: 'Resultados de encuesta',
subscriptions: 'Suscripción a toots',
needToReauthenticate: 'Tienes que volver a autenticarte para habilitar las notificaciones Push. ¿Cerrr sesión en {instance}?',
failedToUpdatePush: 'Se ha producido un fallo al actualizar las opciones para notificaciones Push: {error}',
// Themes
chooseTheme: 'Elegir un diseño visual',
darkBackground: 'Fondo oscuro',
lightBackground: 'Fondo claro',
themeLabel: `{label} {default, select,
true {(por defecto)}
other {}
}`,
animatedImage: 'Imagen animada: {description}',
showImage: `Mostrar {animated, select,
true {animated}
other {}
} imagen: {description}`,
playVideoOrAudio: `Reproducir {audio, select,
true {audio}
other {vídeo}
}: {description}`,
accountFollowedYou: '{name} te siguió, {account}',
accountSignedUp: '{name} inició sesión, {account}',
accountRequestedFollow: '{name} solicitó seguirte, {account}',
accountReported: '{name} creó una denuncia, {account}',
reblogCountsHidden: 'Recuento de reenvíos oculto',
favoriteCountsHidden: 'Recuento de favoritos oculto',
rebloggedTimes: `Reenviado {count, plural,
one {1 vez}
other {{count} veces}
}`,
favoritedTimes: `Marcado como favorito {count, plural,
one {1 vez}
other {{count} veces}
}`,
pinnedStatus: 'Toot fijado',
rebloggedYou: 'reenvió tu toot',
favoritedYou: 'marcó como favorito tu toot',
followedYou: 'te siguió',
edited: 'editó su toot',
requestedFollow: 'solicitó seguirte',
reported: 'creó una denuncia',
signedUp: 'sesión iniciada',
posted: 'publicado',
pollYouCreatedEnded: 'Una encuesta que creaste ha finalizado',
pollYouVotedEnded: 'Una encuesta en la que votaste ha finalizado',
reblogged: 'reenviado',
favorited: 'marcado como favorito',
unreblogged: 'no reenviado',
unfavorited: 'no marcado como favorito',
showSensitiveMedia: 'Mostrar multimedia sensible',
hideSensitiveMedia: 'Ocultar multimedia sensible',
clickToShowSensitive: 'Contenido sensible. Haz clic para mostrar.',
longPost: 'Publicación larga',
// Accessible status labels
accountRebloggedYou: '{account} reenvió tu toot',
accountFavoritedYou: '{account} marcó como favorito tu toot',
accountEdited: '{account} editó su toot',
rebloggedByAccount: 'reenviado por {account}',
contentWarningContent: 'Advertencia de contenido: {spoiler}',
hasMedia: 'tiene multimedia',
hasPoll: 'tiene encuesta',
shortStatusLabel: '{privacy} toot de {account}',
// Privacy types
public: 'Público',
unlisted: 'No listado',
followersOnly: 'Solo seguidores',
direct: 'Directo',
// Themes
themeRoyal: 'Royal',
themeScarlet: 'Escarlata',
themeSeafoam: 'Espuma de mar',
themeHotpants: 'Hotpants',
themeOaken: 'Roble',
themeMajesty: 'Majesty',
themeGecko: 'Gecko',
themeGrayscale: 'Escala de grises',
themeOzark: 'Ozark',
themeCobalt: 'Cobalto',
themeSorcery: 'Sorcery',
themePunk: 'Punk',
themeRiot: 'Riot',
themeHacker: 'Hacker',
themeMastodon: 'Mastodon',
themePitchBlack: 'Tono negro',
themeDarkGrayscale: 'Escala de gris oscuro',
// Polls
voteOnPoll: 'Votar en encuesta',
pollChoices: 'Opciones de la encuesta',
vote: 'Votar',
pollDetails: 'Detalles de la encuesta',
refresh: 'Actualizar',
expires: 'Finaliza',
expired: 'Finalizada',
voteCount: `{count, plural,
one {1 voto}
other {{count} votos}
}`,
// Status interactions
clickToShowThread: '{time} - haz clic para mostrar el hilo',
showMore: 'Mostrar más',
showLess: 'Mostrar menos',
closeReply: 'Cerrar respuesta',
cannotReblogFollowersOnly: 'No se puede reenviar porque es solo para seguidores',
cannotReblogDirectMessage: 'No se puede reenviar porque es un mensaje directo',
reblog: 'Reenviar',
reply: 'Responder',
replyToThread: 'Responder al hilo',
favorite: 'Favorito',
unfavorite: 'No favorito',
// timeline
loadingMore: 'Cargando más…',
loadMore: 'Cargar más',
showCountMore: 'Mostrar {count} más',
nothingToShow: 'Nada para mostrar.',
// status thread page
statusThreadPage: 'Página de hilo de toots',
status: 'Toot',
// toast messages
blockedAccount: 'Cuenta bloqueada',
unblockedAccount: 'Cuenta desbloqueada',
unableToBlock: 'No se puede bloquear la cuenta: {error}',
unableToUnblock: 'No se puede desbloquear la cuenta: {error}',
bookmarkedStatus: 'Toot con marcador',
unbookmarkedStatus: 'Toot sin marcador',
unableToBookmark: 'No se puede poner marcador: {error}',
unableToUnbookmark: 'No se puede quitar marcador: {error}',
cannotPostOffline: 'No puedes publicar mientras estás sin conexión',
unableToPost: 'No se puede publicar el toot: {error}',
statusDeleted: 'Toot borrado',
unableToDelete: 'No se puede borrar el toot: {error}',
cannotFavoriteOffline: 'No puedes marcar como favorito mientras estás sin conexión',
cannotUnfavoriteOffline: 'No puedes quitar marca de favorito mientras estás sin conexión',
unableToFavorite: 'No se puede marcar como favorito: {error}',
unableToUnfavorite: 'No se puede quitar marca de favorito: {error}',
followedAccount: 'Cuenta seguida',
unfollowedAccount: 'Cuenta no seguida',
unableToFollow: 'No se puede seguir a la cuenta: {error}',
unableToUnfollow: 'No se puede dejar de seguir a la cuenta: {error}',
accessTokenRevoked: 'El token de acceso fue anulado, se cerró sesión en {instance}',
loggedOutOfInstance: 'Se cerró sesión en {instance}',
failedToUploadMedia: 'Falló la subida del multimedia: {error}',
mutedAccount: 'Cuenta silenciada',
unmutedAccount: 'Cuenta no silenciada',
unableToMute: 'No se puede silenciar la cuenta: {error}',
unableToUnmute: 'No se puede dejar de silenciar la cuenta: {error}',
mutedConversation: 'Conversación silenciada',
unmutedConversation: 'Conversación no silenciada',
unableToMuteConversation: 'No se puede silenciar la conversación: {error}',
unableToUnmuteConversation: 'No se puede dejar de silenciar la conversación: {error}',
unpinnedStatus: 'Toot no fijado',
unableToPinStatus: 'No se puede fijar el toot: {error}',
unableToUnpinStatus: 'No se puede dejar de fijar el toot: {error}',
unableToRefreshPoll: 'No se puede actualizar la encuesta: {error}',
unableToVoteInPoll: 'No se puede votar en la encuesta: {error}',
cannotReblogOffline: 'No puedes reenviar mientras estás sin conexión.',
cannotUnreblogOffline: 'No puedes deshacer reenvíos mientras estás sin conexión.',
failedToReblog: 'Fallo al reenviar: {error}',
failedToUnreblog: 'Fallo al deshacer reenvío: {error}',
submittedReport: 'Denuncia enviada',
failedToReport: 'Fallo al enviar denuncia: {error}',
approvedFollowRequest: 'Solicitud de seguimiento aceptada',
rejectedFollowRequest: 'Solicitud de seguimiento rechazada',
unableToApproveFollowRequest: 'No se puede aceptar la solicitud de seguimiento: {error}',
unableToRejectFollowRequest: 'No se puede rechazar la solicitud de seguimiento: {error}',
searchError: 'Error durante la búsqueda: {error}',
hidDomain: 'Dominio oculto',
unhidDomain: 'Dominio no oculto',
unableToHideDomain: 'No se puede ocultar el dominio: {error}',
unableToUnhideDomain: 'No se puede dejar de ocultar el dominio: {error}',
showingReblogs: 'Mostrando reenvíos',
hidingReblogs: 'Ocultando reenvíos',
unableToShowReblogs: 'No se puede mostrar los reenvíos: {error}',
unableToHideReblogs: 'No se puede ocultar los reenvíos: {error}',
unableToShare: 'No se puede compartir: {error}',
unableToSubscribe: 'Imposible suscribirse: {error}',
unableToUnsubscribe: 'Imposible dejar de suscribirse: {error}',
showingOfflineContent: 'La petición a internet falló. Mostrando contenido sin conexión.',
youAreOffline: 'Parece que estás sin conexión. Puedes leer contenido incluso sin conexión.',
// Snackbar UI
updateAvailable: 'Actualización de la aplicación disponible.',
// Word/phrase filters
wordFilters: 'Filtros de palabras',
noFilters: 'No tienes ningún filtro de palabras.',
wordOrPhrase: 'Palabra o frase',
contexts: 'Contextos',
addFilter: 'Añadir filtro',
editFilter: 'Editar filtro',
filterHome: 'Inicio y listas',
filterNotifications: 'Notificaciones',
filterPublic: 'Cronologías públicas',
filterThread: 'Conversaciones',
filterAccount: 'Perfiles',
filterUnknown: 'Desconocido',
expireAfter: 'Expira al cabo de',
whereToFilter: 'Dónde filtrar',
irreversible: 'Irreversible',
wholeWord: 'Palabra completa',
save: 'Guardar',
updatedFilter: 'Filtro actualizado',
createdFilter: 'Filtro creado',
failedToModifyFilter: 'Fallo al modificar el filtro: {error}',
deletedFilter: 'Filtro borrado',
required: 'Requerido',
// Dialogs
profileOptions: 'Opciones de perfil',
copyLink: 'Copiar enlace',
emoji: 'Emoji',
editMedia: 'Editar multimedia',
shortcutHelp: 'Ayuda sobre atajos de teclado',
statusOptions: 'Opciones de estado',
confirm: 'Confirmar',
closeDialog: 'Cerrar diálogo',
postPrivacy: 'Privacidad del post',
homeOnInstance: 'Inicio en {instance}',
statusesTimelineOnInstance: 'Estados: {timeline} cronología en {instance}',
statusesHashtag: 'Estados: #{hashtag} hashtag',
statusesThread: 'Estados: hilo',
statusesAccountTimeline: 'Estado: cronología de cuenta',
statusesList: 'Estado: lista',
notificationsOnInstance: 'Notificaciones en {instance}'
}

Wyświetl plik

@ -2,8 +2,9 @@ import { mark, stop } from '../../_utils/marks.js'
import { deleteStatus } from '../deleteStatuses.js'
import { addStatusOrNotification } from '../addStatusOrNotification.js'
import { emit } from '../../_utils/eventBus.js'
import { updateStatus } from '../updateStatus.js'
const KNOWN_EVENTS = ['update', 'delete', 'notification', 'conversation', 'filters_changed']
const KNOWN_EVENTS = ['update', 'delete', 'notification', 'conversation', 'filters_changed', 'status.update']
export function processMessage (instanceName, timelineName, message) {
let { event, payload } = (message || {})
@ -12,7 +13,7 @@ export function processMessage (instanceName, timelineName, message) {
return
}
mark('processMessage')
if (['update', 'notification', 'conversation'].includes(event)) {
if (['update', 'notification', 'conversation', 'status.update'].includes(event)) {
payload = JSON.parse(payload) // only these payloads are JSON-encoded for some reason
}
@ -43,6 +44,9 @@ export function processMessage (instanceName, timelineName, message) {
case 'filters_changed':
emit('wordFiltersChanged', instanceName)
break
case 'status.update':
updateStatus(instanceName, payload)
break
}
stop('processMessage')
}

Wyświetl plik

@ -0,0 +1,13 @@
import { database } from '../_database/database.js'
import { scheduleIdleTask } from '../_utils/scheduleIdleTask.js'
async function doUpdateStatus (instanceName, newStatus) {
console.log('updating status', newStatus)
await database.updateStatus(instanceName, newStatus)
}
export function updateStatus (instanceName, newStatus) {
scheduleIdleTask(() => {
/* no await */ doUpdateStatus(instanceName, newStatus)
})
}

Wyświetl plik

@ -1,18 +1,20 @@
import { auth, basename } from './utils.js'
import { DEFAULT_TIMEOUT, get, post, WRITE_TIMEOUT } from '../_utils/ajax.js'
import { DEFAULT_TIMEOUT, get, post, put, WRITE_TIMEOUT } from '../_utils/ajax.js'
export async function postStatus (instanceName, accessToken, text, inReplyToId, mediaIds,
// post is create, put is edit
async function postOrPutStatus (url, accessToken, method, text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll) {
const url = `${basename(instanceName)}/api/v1/statuses`
const body = {
status: text,
in_reply_to_id: inReplyToId,
media_ids: mediaIds,
sensitive,
spoiler_text: spoilerText,
visibility,
poll
poll,
...(method === 'post' && {
// you can't change these properties when editing
in_reply_to_id: inReplyToId,
visibility
})
}
for (const key of Object.keys(body)) {
@ -23,7 +25,23 @@ export async function postStatus (instanceName, accessToken, text, inReplyToId,
}
}
return post(url, body, auth(accessToken), { timeout: WRITE_TIMEOUT })
const func = method === 'post' ? post : put
return func(url, body, auth(accessToken), { timeout: WRITE_TIMEOUT })
}
export async function postStatus (instanceName, accessToken, text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll) {
const url = `${basename(instanceName)}/api/v1/statuses`
return postOrPutStatus(url, accessToken, 'post', text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll)
}
export async function putStatus (instanceName, accessToken, id, text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll) {
const url = `${basename(instanceName)}/api/v1/statuses/${id}`
return postOrPutStatus(url, accessToken, 'put', text, inReplyToId, mediaIds,
sensitive, spoilerText, visibility, poll)
}
export async function getStatusContext (instanceName, accessToken, statusId) {

Wyświetl plik

@ -3,6 +3,8 @@
-->
<span class="tooltip-button"
aria-describedby="tooltip-{id}"
aria-expanded={shown}
aria-controls="tooltip-{id}"
role="button"
tabindex="0"
on:mouseover="set({shown: true, mouseover: true})"

Wyświetl plik

@ -39,7 +39,9 @@
{#if isStatusInOwnThread}
<StatusDetails {...params} {...timestampParams} />
{/if}
<StatusToolbar {...params} {replyShown} on:recalculateHeight on:focusArticle="focusArticle()" />
{#if !isStatusInNotification}
<StatusToolbar {...params} {replyShown} on:recalculateHeight on:focusArticle="focusArticle()" />
{/if}
{#if replyShown}
<StatusComposeBox {...params} on:recalculateHeight />
{/if}

Wyświetl plik

@ -122,7 +122,7 @@
}
.status-in-notification svg {
opacity: 0.5;
stroke: var(--very-deemphasized-text-color);
}
.status-in-own-thread .option-text {

Wyświetl plik

@ -3,12 +3,13 @@ import { getInCache, hasInCache, statusesCache } from '../cache.js'
import { STATUSES_STORE } from '../constants.js'
import { cacheStatus } from './cacheStatus.js'
import { putStatus } from './insertion.js'
import { cloneForStorage } from '../helpers.js'
//
// update statuses
//
async function updateStatus (instanceName, statusId, updateFunc) {
async function doUpdateStatus (instanceName, statusId, updateFunc) {
const db = await getDatabase(instanceName)
if (hasInCache(statusesCache, instanceName, statusId)) {
const status = getInCache(statusesCache, instanceName, statusId)
@ -25,7 +26,7 @@ async function updateStatus (instanceName, statusId, updateFunc) {
}
export async function setStatusFavorited (instanceName, statusId, favorited) {
return updateStatus(instanceName, statusId, status => {
return doUpdateStatus(instanceName, statusId, status => {
const delta = (favorited ? 1 : 0) - (status.favourited ? 1 : 0)
status.favourited = favorited
status.favourites_count = (status.favourites_count || 0) + delta
@ -33,7 +34,7 @@ export async function setStatusFavorited (instanceName, statusId, favorited) {
}
export async function setStatusReblogged (instanceName, statusId, reblogged) {
return updateStatus(instanceName, statusId, status => {
return doUpdateStatus(instanceName, statusId, status => {
const delta = (reblogged ? 1 : 0) - (status.reblogged ? 1 : 0)
status.reblogged = reblogged
status.reblogs_count = (status.reblogs_count || 0) + delta
@ -41,19 +42,36 @@ export async function setStatusReblogged (instanceName, statusId, reblogged) {
}
export async function setStatusPinned (instanceName, statusId, pinned) {
return updateStatus(instanceName, statusId, status => {
return doUpdateStatus(instanceName, statusId, status => {
status.pinned = pinned
})
}
export async function setStatusMuted (instanceName, statusId, muted) {
return updateStatus(instanceName, statusId, status => {
return doUpdateStatus(instanceName, statusId, status => {
status.muted = muted
})
}
export async function setStatusBookmarked (instanceName, statusId, bookmarked) {
return updateStatus(instanceName, statusId, status => {
return doUpdateStatus(instanceName, statusId, status => {
status.bookmarked = bookmarked
})
}
// For the full list, see https://docs.joinmastodon.org/methods/statuses/#edit
const PROPS_THAT_CAN_BE_EDITED = ['content', 'spoiler_text', 'sensitive', 'language', 'media_ids', 'poll']
export async function updateStatus (instanceName, newStatus) {
const clonedNewStatus = cloneForStorage(newStatus)
return doUpdateStatus(instanceName, newStatus.id, status => {
// We can't use a simple Object.assign() to merge because a prop might have been deleted
for (const prop of PROPS_THAT_CAN_BE_EDITED) {
if (!(prop in clonedNewStatus)) {
delete status[prop]
} else {
status[prop] = clonedNewStatus[prop]
}
}
})
}

Wyświetl plik

@ -42,12 +42,12 @@
--nav-svg-fill-hover: #{$secondary-text-color};
--nav-text-color-hover: #{$secondary-text-color};
--action-button-fill-color: #{lighten($main-theme-color, 18%)};
--action-button-fill-color-hover: #{lighten($main-theme-color, 22%)};
--action-button-fill-color-active: #{lighten($main-theme-color, 5%)};
--action-button-fill-color-pressed: #{darken($main-theme-color, 7%)};
--action-button-fill-color-pressed-hover: #{darken($main-theme-color, 2%)};
--action-button-fill-color-pressed-active: #{darken($main-theme-color, 15%)};
--action-button-fill-color: #{lighten($main-theme-color, 11.5%)};
--action-button-fill-color-hover: #{lighten($main-theme-color, 6%)};
--action-button-fill-color-active: #{$main-theme-color};
--action-button-fill-color-pressed: #{darken(saturate($main-theme-color, 5%), 6%)};
--action-button-fill-color-pressed-hover: #{darken(saturate($main-theme-color, 5%), 12%)};
--action-button-fill-color-pressed-active: #{darken(saturate($main-theme-color, 5%), 15%)};
--action-button-deemphasized-fill-color: #{$deemphasized-color};
--action-button-deemphasized-fill-color-hover: #{lighten($deemphasized-color, 22%)};
@ -81,8 +81,8 @@
--deemphasized-text-color: #{$deemphasized-color};
--focus-outline: #{$focus-outline};
--very-deemphasized-link-color: #{rgba($anchor-color, 0.6)};
--very-deemphasized-text-color: #{rgba(#666, 0.6)};
--very-deemphasized-text-color: #757575;
--very-deemphasized-link-color: var(--very-deemphasized-text-color);
--status-direct-background: #{darken($body-bg-color, 5%)};
--main-theme-color: #{$main-theme-color};

Wyświetl plik

@ -1,5 +1,5 @@
:root {
$deemphasized-color: lighten($main-bg-color, 45%);
$deemphasized-color: lighten($main-bg-color, 54%);
--action-button-deemphasized-fill-color: #{$deemphasized-color};
--action-button-deemphasized-fill-color-hover: #{lighten($deemphasized-color, 22%)};
@ -12,8 +12,8 @@
--deemphasized-text-color: #{$deemphasized-color};
--very-deemphasized-link-color: #{rgba($anchor-color, 0.8)};
--very-deemphasized-text-color: #{lighten($main-bg-color, 32%)};
--very-deemphasized-text-color: #{lighten($main-bg-color, 44%)};
--very-deemphasized-link-color: var(--very-deemphasized-text-color);
--status-direct-background: #{darken($body-bg-color, 5%)};
--main-theme-color: #{$main-theme-color};

Wyświetl plik

@ -12,3 +12,10 @@ $compose-background: lighten($main-theme-color, 17%);
@import "_base.scss";
@import "_light_scrollbars.scss";
:root {
// make the action buttons a bit lighter
--action-button-fill-color: #{lighten($main-theme-color, 17%)};
--action-button-fill-color-hover: #{lighten($main-theme-color, 10%)};
--action-button-fill-color-active: #{lighten($main-theme-color, 5%)};
}

Wyświetl plik

@ -11,4 +11,11 @@ $focus-outline: lighten($main-theme-color, 30%);
$compose-background: lighten($main-theme-color, 32%);
@import "_base.scss";
@import "_light_scrollbars.scss";
@import "_light_scrollbars.scss";
:root {
// make the action buttons a bit lighter
--action-button-fill-color: #{lighten($main-theme-color, 17%)};
--action-button-fill-color-hover: #{lighten($main-theme-color, 10%)};
--action-button-fill-color-active: #{lighten($main-theme-color, 5%)};
}

Wyświetl plik

@ -23,6 +23,11 @@ $compose-background: darken($main-theme-color, 12%);
--button-primary-bg-hover: #56a7e1;
--button-primary-border: transparent;
--action-button-fill-color: #{lighten($main-theme-color, 30%)};
--action-button-fill-color-hover: #{lighten($main-theme-color, 36%)};
--action-button-fill-color-active: #{lighten($main-theme-color, 42%)};
--action-button-fill-color-pressed: #2b90d9;
--action-button-fill-color-pressed-hover: #2b90d9;
--action-button-fill-color-pressed-hover: #{darken(#2b90d9, 6%)};
--action-button-fill-color-pressed-active: #{darken(#2b90d9, 12%)};
}

Wyświetl plik

@ -12,4 +12,10 @@ $compose-background: darken($main-theme-color, 12%);
@import "_base.scss";
@import "_dark.scss";
@import "_dark_scrollbars.scss";
@import "_dark_scrollbars.scss";
:root {
--action-button-fill-color-pressed: #{lighten(saturate($main-theme-color, 25%), 8%)};
--action-button-fill-color-pressed-hover: #{lighten(saturate($main-theme-color, 25%), 12%)};
--action-button-fill-color-pressed-active: #{lighten(saturate($main-theme-color, 25%), 15%)};
}

Wyświetl plik

@ -33,10 +33,12 @@ $compose-background: darken($main-theme-color, 12%);
--form-bg: #{$body-bg-color};
--form-border: #{darken($border-color, 10%)};
--action-button-fill-color: #{lighten($main-theme-color, 20%)};
--action-button-fill-color-hover: #{lighten($main-theme-color, 30%)};
--action-button-fill-color-active: #{darken($main-theme-color, 40%)};
--action-button-fill-color: #{lighten($main-theme-color, 50%)};
--action-button-fill-color-hover: #{lighten($main-theme-color, 60%)};
--action-button-fill-color-active: #{darken($main-theme-color, 70%)};
--action-button-fill-color-pressed: #{lighten($main-theme-color, 85%)};
--action-button-fill-color-pressed-hover: #{lighten($main-theme-color, 100%)};
--action-button-fill-color-pressed-active: #{lighten($main-theme-color, 80%)};
--svg-fill: #{lighten($main-theme-color, 50%)};
}

Wyświetl plik

@ -2,7 +2,7 @@ import { favoriteStatus } from '../src/routes/_api/favorite.js'
import fetch from 'node-fetch'
import FileApi from 'file-api'
import { users } from './users.js'
import { postStatus } from '../src/routes/_api/statuses.js'
import { postStatus, putStatus } from '../src/routes/_api/statuses.js'
import { deleteStatus } from '../src/routes/_api/delete.js'
import { authorizeFollowRequest, getFollowRequests } from '../src/routes/_api/followRequests.js'
import { followAccount, unfollowAccount } from '../src/routes/_api/follow.js'
@ -33,6 +33,11 @@ export async function postAs (username, text) {
null, null, false, null, 'public')
}
export async function putAs (username, text, statusId) {
return putStatus(instanceName, users[username].accessToken, statusId, text,
null, null, false, null, 'public')
}
export async function postWithSpoilerAndPrivacyAs (username, text, spoiler, privacy) {
return postStatus(instanceName, users[username].accessToken, text,
null, null, true, spoiler, privacy)

Wyświetl plik

@ -35,3 +35,10 @@ test('shows direct vs followers-only vs regular in notifications', async t => {
.eql('Cannot be boosted because this is a direct message')
.expect($(`${getNthStatusSelector(5)} .status-toolbar button:nth-child(2)`).hasAttribute('disabled')).ok()
})
test('hides status toolbar on notification page', async t => {
await loginAsFoobar(t)
await t
.navigateTo('/notifications')
.expect($(`${getNthStatusSelector(1)} .status-toolbar`).exists).notOk()
})

Wyświetl plik

@ -1,10 +1,10 @@
import {
closeDialogButton,
composeModalInput,
getNthFavoritedLabel,
getNthStatus,
getUrl, modalDialog, notificationsNavButton,
isNthStatusActive, goBack
isNthStatusActive, goBack,
getNthFavoritedLabel
} from '../utils'
import { loginAsFoobar } from '../roles'
@ -12,16 +12,22 @@ fixture`026-shortcuts-notification.js`
.page`http://localhost:4002`
test('Shortcut f toggles favorite status in notification', async t => {
const idx = 0
const idx = 6 // "hello foobar"
await loginAsFoobar(t)
await t
.expect(getUrl()).eql('http://localhost:4002/')
.click(notificationsNavButton)
.expect(getUrl()).contains('/notifications')
.expect(getNthStatus(1 + idx).exists).ok({ timeout: 30000 })
.expect(getNthStatus(1).exists).ok({ timeout: 30000 })
for (let i = 0; i < idx + 1; i++) {
await t.pressKey('j')
.expect(getNthStatus(1 + i).exists).ok()
.expect(isNthStatusActive(1 + i)()).ok()
}
await t
.expect(getNthFavoritedLabel(1 + idx)).eql('Favorite')
.pressKey('j '.repeat(idx + 1))
.expect(isNthStatusActive(1 + idx)()).ok()
.pressKey('f')
.expect(getNthFavoritedLabel(1 + idx)).eql('Unfavorite')
.pressKey('f')

Wyświetl plik

@ -0,0 +1,28 @@
import {
getNthStatus, getUrl, goBack,
sleep
} from '../utils'
import { loginAsFoobar } from '../roles'
import { postAs, putAs } from '../serverActions'
fixture`140-editing.js`
.page`http://localhost:4002`
test('Edited toots are updated in the UI', async t => {
const { id: statusId } = await postAs('admin', 'yolo')
await sleep(500)
await loginAsFoobar(t)
await t.expect(getNthStatus(1).innerText).contains('yolo', { timeout: 20000 })
await putAs('admin', 'wait I mean YOLO', statusId)
await sleep(500)
await t.click(getNthStatus(1))
.expect(getUrl()).contains('/statuses')
.expect(getNthStatus(1).innerText).contains('wait I mean YOLO', { timeout: 20000 })
await goBack()
await t
.expect(getUrl()).eql('http://localhost:4002/')
.expect(getNthStatus(1).innerText).contains('wait I mean YOLO', { timeout: 20000 })
})

Wyświetl plik

@ -12,8 +12,9 @@ import urlRegex from '../src/routes/_utils/urlRegexSource.js'
// TODO: make it so we don't have to list these out explicitly
import fr from 'emoji-picker-element/i18n/fr.js'
import de from 'emoji-picker-element/i18n/de.js'
import es from 'emoji-picker-element/i18n/es.js'
const emojiPickerLocales = { fr, de }
const emojiPickerLocales = { fr, de, es }
const emojiPickerI18n = LOCALE !== DEFAULT_LOCALE && emojiPickerLocales[LOCALE]