
Hace poco he publicado una aplicación web que permite publicar mensajes en el fediverso sin salir de tu navegador.
La aplicación se llama How I Feel Today y es una aplicación VueJS que se conecta a tu servidor de Mastodon (en realidad tu servidor del Fediverso pero como todo el mundo lo conoce como Mastodon….) para publicar un diagrama "radar" en formato PNG con tus estados de salud, financiero, social y emocional.
Sin texto, sin explicaciones, solamente una imagen que refleja tu estado de ánimo en ese momento.
El interface de la aplicación es muy sencillo, solamente tienes que seleccionar el "nivel" de cada uno de los estados y la imagen se va ajustando en tiempo real.
Cuando has ajustado los niveles a cómo te sientes en cada uno puedes publicarlos pulsando simplemente en "publish" y la aplicación publicará un toot con la imagen
Como puedes ver, la aplicación es muy sencilla de usar.
Para poder publicar tu HIFT lo primero que debes hacer es identificarte en tu instancia y permitir a la aplicación
obtener un access token
para poder publicar en tu cuenta
La aplicación detecta cuando no está logeada (más detalles a continuación) y te muestra una pantalla para que indiques la instancia que usas (i.e. mastodon.social, jvm.social, etc)
Cuando has puesto tu instancia y pulsado Submit, la aplicación te redirige a tu instancia para que la apruebes
y ella se encarga de completar el proceso de autorización guardando el access token
en tu navegador
En mi opinión, esta es (una de) la "gracia" de esta aplicación: no requiere de un servidor donde guardar secretos ni completar de forma oscura el proceso de autorización. Todo se realiza en tu navegador a base de "redirects"
El código de la aplicación se encuentra publicado en https://github.com/jagedn/hift/
La aplicación está desarrollada con Vue 3 y se ejecuta completamente en el navegador por lo que no se guarda nada en servidores externos (El servidor donde está alojada es de Github pero por tener el código publicado junto con la aplicación web. En realidad podrías descargarte el código y ejecutarla en tu máquina)
Como puedes ver en el fichero package.json
tiene pocas dependencias:
"bootstrap": "^5.3.3",
"chart.js": "^3.9.1",
"masto": "^6.7.0",
"pinia": "^2.1.7",
"vue": "^3.5.10",
"vue-chart-3": "^3.1.8",
Bootstrap para poder "enmaquetar" de una forma responsive la página
Chart.js para generar el diagrama en tiempo real y Vue-Chart-3 para integrarlo en Vue3
Pinia para mantener el estado de la aplicación y compartir los cambios en el modelo de datos
Básicamente la aplicación se compone de un parent
HomeView.vue y dos componentes FeelingForm.vue
y FeelingChart.vue
, así como 2 stores
, uno para mantener la configuración de la instancia a la que te has logeado y otro para mantener
tus "feelings"
Debido a mis escasos conocimientos de Vue una de las cosas donde me he peleado más ha sido en conseguir sincronizar los cambios entre el formulario donde seleccionas el nivel de cada sentimiento y la generación del Chart, pero una vez puesta en marcha la verdad es que muy simple con Vue3
En el parent simplemente usamos "@evento" (@save en este caso) y proporcionamos una funcion a ejecutar ante este evento
<div class="col-lg-4 col-md-12 col-sm-12 px-0">
<FeelingForm @save="publishFeeling" v-model="feeling" @logoff="logoff" :topics="topics"/>
</div>
y en el child emitimos el evento
const submitForm = () => {
emits('save');
};
...
<form @submit.prevent="submitForm">
....
</form>
En este ejemplo, cuando el usuario pulsa Submit se captura el evento y se emite a su vez uno propio "save"
Para compartir el estado entre el padre y los hijos Vue lo hace realmente fácil
En el padre usamos ref
para crear objetos "referenciados"
const feeling = {
a: ref(0),
b: ref(0),
c: ref(0),
d: ref(0),
};
y los pasamos a los hijos
v-model="feeling"
En los hijos simplemente definimos el modelo
defineModel('modelValue')
y los usamos donde sea necesario
<input type="range" class="form-range" id="a" @change="updated" v-model="modelValue.a.value" min="1" max="5"/>
Una vez que tengo la imagen (en memoria) que quieres publicar es muy sencillo subirla a tu instancia. En realidad
el protocolo es muy simple y se podría hacer con unos simples fetch
pero ya que existe una librería masto
que lo
hace por mí, todo se vuelve más fácil
const masto = createRestAPIClient({
url: url,
accessToken: accessToken,
});
const response = await masto.v1.media.create({
file: currentImage,
description:`A radar chart shows four categories:
${topics.a} (light red),
${topics.b} (light teal),
${topics.c} (light gray),
and ${topics.d} (gold).
The ${topics.a} category has a value of ${feeling.a.value}
The ${topics.b} category has a value of ${feeling.b.value}
The ${topics.c} category has a value of ${feeling.c.value}
The ${topics.d} category has a value of ${feeling.d.value}
`
})
const mediaId = response.id;
await masto.v1.statuses.create({
status: `#HIFT`,
visibility: "public",
mediaIds:[mediaId]
});
Básicamente creamos un objeto masto
usando la url+accessToken y subimos la imagen con media.create
añadiendo
un texto alternativo (que no falte)
El servidor nos devuelve el id de la imagen que ha guardado y simplemente creamos un toot añadiendo el mediaId
y eso es todo para publicar un toot con una imágen!!!
Por último simplemente guardamos en el navegador el HIFT publicado para usarlos como punto de partida la próxima vez que abrás la aplicación
La aplicación está publicada en Github (podría estar alojada en cualquier servidor de contenido estático) y esto plantea un pequeño reto para aplicaciones Vue y similares que se basan en la ruta del navegador para mantener el estado además de los redirects necesarios de esta aplicación
En el caso concreto de Github hay dos pequeños "hacks" a tener en cuenta
El index.html generado por Vue hay que añadirle un pequeño script:
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// MIT License
// https://github.com/rafgraph/spa-github-pages
// This script checks to see if a redirect is present in the query string,
// converts it back into the correct url and adds it to the
// browser's history using window.history.replaceState(...),
// which won't cause the browser to attempt to load the new url.
// When the single page app is loaded further down in this file,
// the correct url will be waiting in the browser's history for
// the single page app to route accordingly.
(function(l) {
if (l.search[1] === '/' ) {
var decoded = l.search.slice(1).split('&').map(function(s) {
return s.replace(/~and~/g, '&')
}).join('?');
window.history.replaceState(null, null,
l.pathname.slice(0, -1) + decoded + l.hash
);
}
}(window.location))
</script>
además de añadir un .htaccess
Options All -Indexes
<Files .htaccess>
order allow,deny
deny from all
</Files>
<IfModule mod_rewrite.c>
# Redirect to the public folder
RewriteEngine On
# RewriteBase /
RewriteRule login index.html [L]
# Redirect to HTTPS
# RewriteEngine On
# RewriteCond %{HTTPS} off
# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
</IfModule>
Así conseguimos que cuando la instancia en la que hacemos login nos haga el redirect a login
, página que no existe
en nuestra aplicación, sea redirigida a index.html
y que se ejecute nuestra aplicacion Vue
2019 - 2025 | Mixed with Bootstrap | Baked with JBake v2.6.7 | Terminos Terminos y Privacidad