Compare commits
12 commits
366b7b6657
...
f16745d9ad
Author | SHA1 | Date | |
---|---|---|---|
f16745d9ad | |||
a439056607 | |||
266728fbf3 | |||
78ed5db561 | |||
118d147835 | |||
075782e988 | |||
5c388d7c44 | |||
136b387a5f | |||
1e62f49e5a | |||
8bb3e76073 | |||
20734fcb66 | |||
9affac5109 |
6 changed files with 237 additions and 26 deletions
201
Arreglando bugs ajenos.md
Normal file
201
Arreglando bugs ajenos.md
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
<small>2021/10/11</small>
|
||||||
|
|
||||||
|
Estoy aprendiendo alemán y mi principal recurso es el "Aprender Alemán" de la Deutsche Welle ([learngerman.dw.com](https://learngerman.dw.com/es/overview)). Sin embargo, en una de las recientes actualizaciones al sitio/app, rompieron _varias_ cosas.
|
||||||
|
|
||||||
|
Les mandé un mail al equipo de desarrollo ¡pero no me contestaron! Así que lo arreglé yo mismo.
|
||||||
|
|
||||||
|
Pero ¿cómo? El código es privativo y no tengo acceso. El _JavaScript_ que llega a mi navegador está minificado, es decir que se le elimina palabras y se comprime a un archivo para ahorrar en banda ancha. Pero **otra de las razones por las que se minifica es para no dar acceso al código.**
|
||||||
|
|
||||||
|
Sin embargo, el equipo de desarrollo cometió un error (¿intencionalmente?) en donde dejaron los "source maps". Los source maps son unos archivos que le permiten a las herramientas de desarrolladorx saber que parte del código minificado es cuál línea de código. ¡Por eso se llaman mapas! Para lograr esto, contienen casi todo el código del sitio.
|
||||||
|
|
||||||
|
Pero hay un último 5% del código que me hace falta para poder aplicar cambios: la configuración de las herramientas que minifican y compilan el código. Para mi suerte, este sitio utiliza una tecnología con la que ya tengo experiencia (React) así que puedo reconstruir esta configuración por mi cuenta.
|
||||||
|
|
||||||
|
Sabiendo esto, vayamos a lo práctico.
|
||||||
|
|
||||||
|
## Extrayendo el código
|
||||||
|
|
||||||
|
Empiezo por abrir la ventana de herramientas de desarrolladorx de mi navegador en el sitio y extraigo los enlaces a los archivos que terminan con `.map`, que son los source maps. Los guardo en un archivo con un enlace por línea y los descargo utilizando un script:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for f in $(cat files); do
|
||||||
|
wget $f
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Ok, ahora necesito extraer los archivos de código de cada uno. Por suerte, ya existen herramientas que lo hagan por mi. Probé con [este script](https://gist.github.com/banyudu/b17a9cb3f05296b76a9f3051f66c3dcd) pero mi mejor experiencia fue con [este](https://github.com/akx/source-from-sourcemaps).
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for f in *.map; do
|
||||||
|
node source-from-sourcemaps.js $f
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
## Levantando el código localmente
|
||||||
|
|
||||||
|
Después configuré una herramienta que compila código de React y hace otras magias por mi llamada [Vite](https://vitejs.dev). Podría haber usado la que lxs desarrolladorxs del sitio usaron originalmente, [webpack](https://webpack.js.org/), pero en mi experiencia es muy tedioso de configurar.
|
||||||
|
|
||||||
|
Tuve varios problemas al intentar levantar un entorno de desarrollo local con el código. Acá están algunos junto a sus soluciones en ningún orden particular:
|
||||||
|
|
||||||
|
- Vite esperaba que los archivos de React tengan la extensión `.jsx` mientras que originalmente tenían la extensión `.js`. Script:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for f in $(find -iname '*.js'); do
|
||||||
|
mv $f ${f}x
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
- Había una parte del código que eran archivos SVG "compilados" a React. Sin embargo, estos esperaban una variable especial de webpack (o algún plugin de webpack) que por supuesto no existía. Para esto, hice otro script para inyectarles esa variable:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for f in $(find -iname '*.svg.js'); do
|
||||||
|
echo 'const __webpack_public_path__ = "https://learngerman.dw.com/";' > ${f}.lol
|
||||||
|
cat $f >> ${f}.lol
|
||||||
|
mv ${f}.lol $f
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
- Tenía que instalar las dependencias de todo el proyecto. Esto lo hice con más script-fu:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
grep -r "from '[\w@].*';" . > imports
|
||||||
|
sort -u imports > imports.uniq
|
||||||
|
xargs pnpm add < imports.uniq
|
||||||
|
# Por alguna razón esto no fue suficiente y tuve que manualmente añadir varias dependencias
|
||||||
|
pnpm add graphql videojs-seek-buttons videojs-contrib-quality-levels videojs-hls-quality-selector videojs-seek-buttons videojs-hls-quality-selector
|
||||||
|
```
|
||||||
|
|
||||||
|
- Faltaba tener el CSS localmente así que lo descargé:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
for f in basestyles stylesheets customBaku; do
|
||||||
|
wget https://learngerman.dw.com/assets/css/${f}.css
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
- En `config.js` se tomaban variables de entorno que asumo existen en el entorno de trabajo de lxs desarrolladorxs. Yo las mentí:
|
||||||
|
|
||||||
|
```js
|
||||||
|
const process = {
|
||||||
|
env: {
|
||||||
|
// REACT_APP_GRAPHQL_BASE_URL: "https://learngerman.dw.com/graphql",
|
||||||
|
REACT_APP_GRAPHQL_BASE_URL: "https://localhost:4002/graphql",
|
||||||
|
REACT_APP_RECAPTCHA_SITEKEY: null,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Notese como originalmente había mentido diciendo que la URL de la API era learngerman.dw.com, pero luego lo cambié para que apunte a una URL local ya que la API no me permitía accederla desde un sitio no legítimo. Esa URL local apunta a un proxy a la API real.
|
||||||
|
|
||||||
|
Es muy probable que haya tenído que hacer más cosas y me haya olvidado. Fue mucho prueba y error hasta que el sitio finalmente cargó.
|
||||||
|
|
||||||
|
## Arreglando bug 1: problema de accesibilidad
|
||||||
|
|
||||||
|
Por un tiempo, tuve una de mis muñecas inaccesibles por unas semanas. Esto me obligo a usar exclusivamente la otra, complicando el uso del mouse y utilizando el teclado lo más posible.
|
||||||
|
|
||||||
|
Recientemente, en el sitio agregaron una solapa de "transcripción" mostrando todo el dialogo que había en el video del ejercicio. Sin embargo, cuando esta solapa estaba cerrada, se podían seguir seleccionando los enlaces dentro de la solapa con el teclado, haciendo la navegación por teclado tediosa.
|
||||||
|
|
||||||
|
<video controls src="Arreglando bugs ajenos.md-details.mp4"></video>
|
||||||
|
|
||||||
|
La solucion es simplemente usar el elemento de solapa que ya viene con el navegador: [`<details>`](https://developer.mozilla.org/es/docs/Web/HTML/Element/details). (La página de `<details>` en MDN esta desactualizada al momento de escribir este artículo.)
|
||||||
|
|
||||||
|
Estos fueron los cambios que hice:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
diff --git a/./components/ContentContainer/AccordionContainer.jsx.orig b/./components/ContentContainer/AccordionContainer.jsx
|
||||||
|
index ed71204..d0ea66e 100644
|
||||||
|
--- a/./components/ContentContainer/AccordionContainer.jsx.orig
|
||||||
|
+++ b/./components/ContentContainer/AccordionContainer.jsx
|
||||||
|
@@ -6,43 +6,26 @@ import { colors } from '../../utils/css';
|
||||||
|
import { useTranslation } from '../../hooks/useTranslation';
|
||||||
|
|
||||||
|
export const AccordionContainer = ({ title, children, className }) => {
|
||||||
|
- const element = useRef(null);
|
||||||
|
- const [isOpen, { toggleOnClick }] = useToggle();
|
||||||
|
- const height = element.current ? element.current.scrollHeight : '0';
|
||||||
|
const titleTranslation = useTranslation(title);
|
||||||
|
|
||||||
|
return (
|
||||||
|
- <div className={className}>
|
||||||
|
- <div className="row noVMargins">
|
||||||
|
+ <details className={className}>
|
||||||
|
+ <summary className="row noVMargins">
|
||||||
|
<div className="col-sm-offset-1 col-sm-10 col-lg-offset-2 col-lg-8">
|
||||||
|
- <button tabIndex={0} onClick={toggleOnClick}>
|
||||||
|
<h4>
|
||||||
|
{titleTranslation}
|
||||||
|
- <ToggleableArrow
|
||||||
|
- className="toggleable-arrow"
|
||||||
|
- fill={colors.LG_BLACK}
|
||||||
|
- {...{
|
||||||
|
- isUp: isOpen,
|
||||||
|
- }}
|
||||||
|
- />
|
||||||
|
</h4>
|
||||||
|
- </button>
|
||||||
|
</div>
|
||||||
|
- </div>
|
||||||
|
+ </summary>
|
||||||
|
<AccordionContainerContent
|
||||||
|
- height={height}
|
||||||
|
- isOpen={isOpen}
|
||||||
|
- ref={element}
|
||||||
|
- aria-expanded={isOpen}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</AccordionContainerContent>
|
||||||
|
- </div>
|
||||||
|
+ </details>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AccordionContainerContent = styled.div`
|
||||||
|
- max-height: ${({ isOpen, height }) => (isOpen ? height : '1')}px;
|
||||||
|
overflow: hidden;
|
||||||
|
background-color: ${colors.LG_WHITE};
|
||||||
|
transition: max-height 0.7s;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Arreglando bug 2: no cargan algunos ejercicios
|
||||||
|
|
||||||
|
Esto tardó mucho más tiempo de arreglar y sinceramente no vale la pena explicarlo. El cambio es bastante simple, lo explico en inglés:
|
||||||
|
|
||||||
|
```diff
|
||||||
|
diff --git a/components/Lesson/LessonExercise/LessonExerciseItem/LessonExerciseItem.jsx.orig b/components/Lesson/LessonExercise/LessonExerciseItem/LessonExerciseItem.jsx
|
||||||
|
index 6d35539..66b5005 100644
|
||||||
|
--- a/components/Lesson/LessonExercise/LessonExerciseItem/LessonExerciseItem.jsx.orig
|
||||||
|
+++ b/components/Lesson/LessonExercise/LessonExerciseItem/LessonExerciseItem.jsx
|
||||||
|
@@ -91,12 +91,20 @@ export const getExerciseMediaInputComponentByType = data => {
|
||||||
|
<MediaInputAudio data={data} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
+ // XXX: Here is the problem. These exercises return inputType == 'VIDEO'
|
||||||
|
+ // even though they don't have any videos (videos == []) causing some
|
||||||
|
+ // code that assumes that there is a video inside videos to fail
|
||||||
|
+ // (the MediaInputVideo component.)
|
||||||
|
case 'VIDEO':
|
||||||
|
- return (
|
||||||
|
- <div className="input-header-video">
|
||||||
|
- <MediaInputVideo data={data} />
|
||||||
|
- </div>
|
||||||
|
- );
|
||||||
|
+ // This "if" is our patch. If it isn't true, it fallsthrough to the default
|
||||||
|
+ // behaviour (returning null.)
|
||||||
|
+ if (data.content.videos.length > 0) {
|
||||||
|
+ return (
|
||||||
|
+ <div className="input-header-video">
|
||||||
|
+ <MediaInputVideo data={data} />
|
||||||
|
+ </div>
|
||||||
|
+ );
|
||||||
|
+ }
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Conclusión
|
||||||
|
|
||||||
|
Más allá del desarrollo técnico que hice, creo que es más importante el político: [**el código desarrollado con dinero público debería ser público**](https://publiccode.eu/es/). Para el caso... todo el código debería ser público, pero eso es un debate para otro día.
|
||||||
|
|
||||||
|
Por otro lado, se me vienen a la mente la necesidad de testear el código para que no pasen cosas como bug 2, y también el testeo de herramientas de accesibilidad para bug 1.
|
||||||
|
|
||||||
|
Gute Nacht!
|
BIN
Arreglando bugs ajenos.md-details.mp4
Normal file
BIN
Arreglando bugs ajenos.md-details.mp4
Normal file
Binary file not shown.
33
build.sh
33
build.sh
|
@ -1,42 +1,43 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
|
|
||||||
template () {
|
template () {
|
||||||
echo -n "<!doctype html>"
|
echo "<!doctype html>"
|
||||||
echo -n "<meta charset=utf-8>"
|
echo "<meta charset=utf-8>"
|
||||||
echo -n "<meta name=viewport content='width=device-width, initial-scale=1.0'>"
|
echo "<meta name=viewport content='width=device-width, initial-scale=1.0'>"
|
||||||
echo -n "<link rel=stylesheet href=drip.css>"
|
echo "<link rel=stylesheet href=drip.css>"
|
||||||
echo -n "<title>$1</title>"
|
echo "<title>$1</title>"
|
||||||
|
: "${inicio:=}"
|
||||||
if test "$inicio" != true; then
|
if test "$inicio" != true; then
|
||||||
echo -n "<a href=.>☚ Volver al inicio</a>"
|
echo "<a href=.>☚ Volver al inicio</a>"
|
||||||
fi
|
fi
|
||||||
if test -n "$2"; then
|
if test -n "$2"; then
|
||||||
echo -n "<header>"
|
echo "<header>"
|
||||||
echo -n "<h1>$1</h1>"
|
echo "<h1>$1</h1>"
|
||||||
echo -n "<p><small>Último cambio: <time datetime='$(git log -1 --format=%ai "$2")'>$(date -d "@$(git log -1 --format=%at "$2")" '+%Y-%m-%d %H:%M')</time></small></p>"
|
echo "<p><small>Último cambio: <time datetime='$(git log -1 --format=%ai "$2")'>$(date -d "@$(git log -1 --format=%at "$2")" '+%Y-%m-%d %H:%M')</time></small></p>"
|
||||||
echo -n "</header>"
|
echo "</header>"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
outdir=build
|
outdir=build
|
||||||
mkdir -p $outdir
|
mkdir -p $outdir
|
||||||
# Autocopiarnos :)
|
# Autocopiarnos :)
|
||||||
cp *.sh *.md *.css *.png "$outdir"
|
cp ./*.sh ./*.md ./*.css ./*.png ./*.mp4 "$outdir"
|
||||||
|
|
||||||
index="$outdir/index.html"
|
index="$outdir/index.html"
|
||||||
inicio=true header=false template "nulo.in" > "$index"
|
inicio=true header=false template "nulo.in" > "$index"
|
||||||
cmark --unsafe index.md >> "$index"
|
cmark --unsafe index.md >> "$index"
|
||||||
echo -n "<h2>Lista de páginas</h2><ul>" >> "$index"
|
echo "<h2>Lista de páginas</h2><ul>" >> "$index"
|
||||||
|
|
||||||
for file in *.md; do
|
for file in *.md; do
|
||||||
test "$(basename $file)" = index.md && continue
|
test "$(basename "$file")" = index.md && continue
|
||||||
title="$(basename "$file" .md)"
|
title="$(basename "$file" .md)"
|
||||||
outfile="$outdir/$title.html"
|
outfile="$outdir/$title.html"
|
||||||
template "$title" "$file" > "$outfile"
|
template "$title" "$file" > "$outfile"
|
||||||
cmark "$file" >> "$outfile"
|
cmark --unsafe "$file" >> "$outfile"
|
||||||
# TODO: hacky
|
# TODO: hacky
|
||||||
sed -i "s/<a /<a rel='noopener noreferrer' /gi" "$outfile"
|
sed -i "s/<a /<a rel='noopener noreferrer' /gi" "$outfile"
|
||||||
sed -i 's/\[\[\(.*\)\]\]/<a href="\1.html">\1<\/a>/g' "$outfile"
|
sed -i 's/\[\[\(.*\)\]\]/<a href="\1.html">\1<\/a>/g' "$outfile"
|
||||||
echo -n "<li><a href='$title.html'>$title</a></li>" >> "$index"
|
echo "<li><a href='$title.html'>$title</a></li>" >> "$index"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo -n "</ul>" >> "$index"
|
echo "</ul>" >> "$index"
|
||||||
|
|
9
drip.css
9
drip.css
|
@ -1,14 +1,19 @@
|
||||||
body {
|
body {
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
max-width: 30rem;
|
max-width: 45rem;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
img {
|
img,
|
||||||
|
video {
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
abbr {
|
abbr {
|
||||||
color: gray;
|
color: gray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
4
index.md
4
index.md
|
@ -14,4 +14,8 @@ Algunos de mis proyectos:
|
||||||
- [Cuarentena](https://cuarentena.nulo.in)
|
- [Cuarentena](https://cuarentena.nulo.in)
|
||||||
- [Este sitio](https://gitea.nulo.in/Nulo/sitio)
|
- [Este sitio](https://gitea.nulo.in/Nulo/sitio)
|
||||||
|
|
||||||
|
Algunas cosas que escribí recientemente:
|
||||||
|
|
||||||
|
- [[Arrelgando bugs ajenos]]
|
||||||
|
|
||||||
Webamigx: [nadie@nulo.in](mailto:nadie@nulo.in) <small>no gods no webmasters</small>
|
Webamigx: [nadie@nulo.in](mailto:nadie@nulo.in) <small>no gods no webmasters</small>
|
||||||
|
|
Reference in a new issue