initial commit
Todas las comprobaciones han sido exitosas
continuous-integration/drone Build is passing
Todas las comprobaciones han sido exitosas
continuous-integration/drone Build is passing
Signed-off-by: ale <ale@manalejandro.com>
Este commit está contenido en:
23
front/.gitignore
vendido
Archivo normal
23
front/.gitignore
vendido
Archivo normal
@@ -0,0 +1,23 @@
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
coverage
|
||||
|
||||
# production
|
||||
build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
70
front/README.md
Archivo normal
70
front/README.md
Archivo normal
@@ -0,0 +1,70 @@
|
||||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||
|
||||
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||
|
||||
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
### Code Splitting
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||
38
front/package.json
Archivo normal
38
front/package.json
Archivo normal
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "front",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"cra-template": "1.2.0",
|
||||
"dayjs": "^1.11.13",
|
||||
"html2canvas": "^1.4.1",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"react-scripts": "5.0.1",
|
||||
"web-vitals": "^4.2.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
BIN
front/public/favicon.ico
Archivo normal
BIN
front/public/favicon.ico
Archivo normal
Archivo binario no mostrado.
|
Después Anchura: | Altura: | Tamaño: 30 KiB |
21
front/public/index.html
Archivo normal
21
front/public/index.html
Archivo normal
@@ -0,0 +1,21 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Fediblock Instance Φ</title>
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta name="description" content="Fediblock Instance Φ - Search in public instances - API docs">
|
||||
<meta name="keywords" content="Fediblock, Fediblock Instance, Fediblock Instance Φ, Free, FreeSoftware">
|
||||
<meta property="og:title" content="Fediblock Instance Φ">
|
||||
<meta property="og:type" content="service">
|
||||
<meta property="og:image" content="favicon.ico">
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
BIN
front/public/logo192.png
Archivo normal
BIN
front/public/logo192.png
Archivo normal
Archivo binario no mostrado.
|
Después Anchura: | Altura: | Tamaño: 5.2 KiB |
BIN
front/public/logo512.png
Archivo normal
BIN
front/public/logo512.png
Archivo normal
Archivo binario no mostrado.
|
Después Anchura: | Altura: | Tamaño: 9.4 KiB |
15
front/public/manifest.json
Archivo normal
15
front/public/manifest.json
Archivo normal
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "New Fediblock Instance",
|
||||
"name": "New Fediblock Instance Front",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "78x90 72x72",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
front/public/robots.txt
Archivo normal
3
front/public/robots.txt
Archivo normal
@@ -0,0 +1,3 @@
|
||||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
405
front/src/App.css
Archivo normal
405
front/src/App.css
Archivo normal
@@ -0,0 +1,405 @@
|
||||
:root[data-theme="dark"] {
|
||||
--background-color: #212529;
|
||||
--color: #fefefe;
|
||||
--instance-list: #333333;
|
||||
--footer: #cccccc;
|
||||
}
|
||||
|
||||
:root[data-theme="light"] {
|
||||
--background-color: #f5f5f5;
|
||||
--color: #333333;
|
||||
--instance-list: #f0f0f0;
|
||||
--footer: #666666;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--background-color);
|
||||
text-align: center;
|
||||
font-family: Verdana, Geneva, Tahoma, sans-serif;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
h1,
|
||||
h3,
|
||||
h4,
|
||||
.scan {
|
||||
margin: 0 auto;
|
||||
color: var(--color);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid var(--footer);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
a,
|
||||
a:hover {
|
||||
color: var(--color);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
padding: 10px;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--footer);
|
||||
background-color: var(--instance-list);
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
font-size: small;
|
||||
margin: 0 auto;
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
.title, .placeholder a, footer a, .download-csv {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
hr {
|
||||
margin: 1rem auto;
|
||||
border-bottom: 1px solid var(--footer);
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.tooltip {
|
||||
visibility: hidden;
|
||||
font-size: small;
|
||||
text-align: left;
|
||||
width: 2.2in;
|
||||
background-color: var(--background-color);
|
||||
color: var(--color);
|
||||
border-radius: 5px;
|
||||
padding: 1rem 1rem 0 0;
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
opacity: 0;
|
||||
transition: 0.5s;
|
||||
border: 1px solid var(--footer);
|
||||
}
|
||||
|
||||
.count:hover .tooltip {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.reverse {
|
||||
margin: 0.5rem;
|
||||
padding: 0.5rem;
|
||||
border: 1px solid var(--footer);
|
||||
background-color: var(--color);
|
||||
color: var(--background-color);
|
||||
}
|
||||
|
||||
.reverse:hover {
|
||||
color: var(--color);
|
||||
background-color: var(--background-color);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.instancelist {
|
||||
list-style-type: none;
|
||||
margin: 20px 0;
|
||||
padding: 0;
|
||||
color: var(--color);
|
||||
max-height: 58vh;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.instancelist li {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.instancelist li:hover {
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.instance,
|
||||
.placeholder,
|
||||
h4 {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 28%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 600px) and (max-width: 992px) {
|
||||
.instancelist li {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.instancelist li:hover {
|
||||
width: 65%;
|
||||
}
|
||||
|
||||
.instance,
|
||||
.placeholder,
|
||||
h4 {
|
||||
width: 60%;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 58%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.instancelist li {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.instancelist li:hover {
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.instance,
|
||||
.placeholder,
|
||||
h4 {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.api {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
width: 88%;
|
||||
}
|
||||
}
|
||||
|
||||
.instancelist li {
|
||||
color: var(--color);
|
||||
padding: 10px;
|
||||
border-bottom: 1px solid var(--footer);
|
||||
margin: 0 auto;
|
||||
max-height: 1.5em;
|
||||
overflow: hidden;
|
||||
min-height: 1.5em;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.instancelist li:hover {
|
||||
background-color: var(--instance-list);
|
||||
cursor: pointer;
|
||||
max-height: fit-content;
|
||||
}
|
||||
|
||||
.instancelist li img {
|
||||
width: 70%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
@keyframes opacity {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
footer {
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
color: var(--footer);
|
||||
}
|
||||
|
||||
.count {
|
||||
text-decoration: underline dotted var(--color);
|
||||
cursor: help;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
|
||||
.blocklist {
|
||||
width: 90%;
|
||||
text-align: left;
|
||||
list-style: none;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.blocklist li {
|
||||
color: var(--color);
|
||||
white-space: nowrap;
|
||||
word-wrap: break-word;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.blocklist li:hover {
|
||||
white-space: inherit;
|
||||
cursor: pointer;
|
||||
color: var(--color);
|
||||
}
|
||||
|
||||
.blocklist li a,
|
||||
.blocklist li a:hover,
|
||||
.blocklist li a:visited {
|
||||
color: var(--color);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.blockinstance a,
|
||||
.blockinstance a:hover {
|
||||
color: var(--color);
|
||||
text-decoration: underline;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.blockcount {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
.modal {
|
||||
display: none;
|
||||
position: fixed;
|
||||
z-index: 1;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
position: relative;
|
||||
background-color: var(--background-color);
|
||||
margin: 10px auto;
|
||||
padding: 0;
|
||||
border: 1px solid var(--footer);
|
||||
-webkit-animation-name: animatetop;
|
||||
-webkit-animation-duration: 0.4s;
|
||||
animation-name: animatetop;
|
||||
animation-duration: 0.4s;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 6px;
|
||||
background-color: var(--color);
|
||||
color: var(--background-color);
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
color: var(--color);
|
||||
background-color: var(--background-color);
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 6px;
|
||||
background-color: var(--color);
|
||||
color: var(--background-color);
|
||||
}
|
||||
|
||||
.closemodal,
|
||||
.download,
|
||||
.capture {
|
||||
color: var(--footer);
|
||||
float: right;
|
||||
font-size: 3rem;
|
||||
font-weight: bolder;
|
||||
margin: 0px 15px;
|
||||
}
|
||||
|
||||
.closemodal:hover,
|
||||
.closemodal:focus,
|
||||
.download:hover,
|
||||
.download:focus,
|
||||
.capture:hover,
|
||||
.capture:focus {
|
||||
color: var(--color);
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@keyframes animatetop {
|
||||
from {
|
||||
top: -300px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes animatebottom {
|
||||
from {
|
||||
top: 0;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
to {
|
||||
top: -300px;
|
||||
opacity: 0
|
||||
}
|
||||
}
|
||||
|
||||
h4 {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-left: 1px solid var(--background-color);
|
||||
border-right: 1px solid var(--background-color);
|
||||
transition: 0.2s;
|
||||
}
|
||||
|
||||
h4 p {
|
||||
margin: 0.3rem 0;
|
||||
}
|
||||
|
||||
h4:hover {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
.bounce {
|
||||
display: inline-block;
|
||||
animation: marquee 90s linear infinite;
|
||||
}
|
||||
|
||||
.bounce:hover {
|
||||
animation-play-state: paused;
|
||||
}
|
||||
|
||||
@keyframes marquee {
|
||||
0% {
|
||||
transform: translateX(100vw);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
.loader-content {
|
||||
z-index: 0;
|
||||
background-color: rgba(0, 0, 0, 0.4);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: none;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
overflow: auto;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.loader {
|
||||
background-color: var(--color);
|
||||
width: fit-content;
|
||||
margin: 50vh auto;
|
||||
z-index: 1;
|
||||
position: relative;
|
||||
color: var(--background-color);
|
||||
}
|
||||
28
front/src/App.js
Archivo normal
28
front/src/App.js
Archivo normal
@@ -0,0 +1,28 @@
|
||||
import { useState } from 'react';
|
||||
import './App.css';
|
||||
import Title from './component/Title';
|
||||
import Bar from './component/Bar';
|
||||
import Count from './component/Count';
|
||||
import Form from './component/Form';
|
||||
import Scan from './component/Scan';
|
||||
import Footer from './component/Footer';
|
||||
import Loader from './component/Loader';
|
||||
|
||||
function App() {
|
||||
const [searchTerm, setSearchTerm] = useState(''),
|
||||
[matrix, setMatrix] = useState('off')
|
||||
return (
|
||||
<>
|
||||
<Title />
|
||||
<Bar setSearch={d => setSearchTerm(d)} />
|
||||
<Count />
|
||||
<Form searchTerm={searchTerm} matrix={matrix} />
|
||||
<Scan />
|
||||
<hr />
|
||||
<Footer setCurrentMatrix={m => setMatrix(m)} />
|
||||
<Loader />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
8
front/src/App.test.js
Archivo normal
8
front/src/App.test.js
Archivo normal
@@ -0,0 +1,8 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import App from './App';
|
||||
|
||||
test('renders learn react link', () => {
|
||||
render(<App />);
|
||||
const linkElement = screen.getByText(/learn react/i);
|
||||
expect(linkElement).toBeInTheDocument();
|
||||
});
|
||||
29
front/src/component/Bar.js
Archivo normal
29
front/src/component/Bar.js
Archivo normal
@@ -0,0 +1,29 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import dayjs from '../../node_modules/dayjs/';
|
||||
import relativeTime from '../../node_modules/dayjs/plugin/relativeTime';
|
||||
dayjs.extend(relativeTime)
|
||||
|
||||
const Bar = (prop) => {
|
||||
const [bounce, setBounce] = useState([]),
|
||||
fillBounce = useCallback(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/outbox'),
|
||||
result = await response.json()
|
||||
if (result && result.length > 0) {
|
||||
setBounce(result)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
useEffect(() => {
|
||||
fillBounce()
|
||||
}, [])
|
||||
return (
|
||||
<h4>
|
||||
<p className="bounce">{bounce && bounce.length > 0 ? bounce.map(b => <a onClick={() => prop.setSearch(b.content.replace(/.*#/g, '').replace(/\<\/a\>\<\/p\>/, ''))}>{b.content.replace(/<[^>]*>/g, '').replace(new RegExp(new URL(window.location.href).host), '')} - {dayjs().to(b.published)}</a>).reduce((prev, curr) => [prev, ' | ', curr]) : []}</p>
|
||||
</h4>
|
||||
)
|
||||
}
|
||||
|
||||
export default Bar;
|
||||
41
front/src/component/Count.js
Archivo normal
41
front/src/component/Count.js
Archivo normal
@@ -0,0 +1,41 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
|
||||
const Count = () => {
|
||||
const [count, setCount] = useState('0'),
|
||||
[statsres, setStatsres] = useState(''),
|
||||
fillStats = useCallback(async () => {
|
||||
try {
|
||||
const [res, stats] = await Promise.all([(await fetch('/api/count')).json(), (await fetch('/api/stats')).json()])
|
||||
setCount(res.count)
|
||||
setStatsres(stats)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
})
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await fillStats()
|
||||
})()
|
||||
}, [])
|
||||
return (
|
||||
<h3>Search in <a href="/api/stats" target="_blank">
|
||||
<span className="count">{count}<div className="tooltip">
|
||||
<u><strong><center>STATS</center></strong></u>
|
||||
<ul><li>Statuses AVG: {Math.round(statsres.status_avg)}</li>
|
||||
<li>Statuses MAX: {statsres.status_max}</li>
|
||||
<li>Domain AVG: {Math.round(statsres.domain_avg)}</li>
|
||||
<li>Domain MAX: {statsres.domain_max}</li>
|
||||
<li>Users AVG: {Math.round(statsres.user_avg)}</li>
|
||||
<li>Users MAX: {statsres.user_max}</li>
|
||||
<li>Stats Instances: {statsres.stats_filtered}</li>
|
||||
<li>Total Instances: {statsres.instance_count}</li>
|
||||
<li>Users by Instance: {(Math.round(statsres.user_avg) / statsres.instance_count).toFixed(2)}</li>
|
||||
<li>Statuses by Domain: {(Math.round(statsres.status_avg) / Math.round(statsres.domain_avg)).toFixed(2)}</li>
|
||||
<li>Statuses by User: {(Math.round(statsres.status_avg) / Math.round(statsres.user_avg)).toFixed(2)}</li>
|
||||
</ul></div></span>
|
||||
</a> public instances<span className="api"> - <a href="/api-docs" target="_blank">API docs</a></span>
|
||||
</h3>
|
||||
)
|
||||
}
|
||||
|
||||
export default Count;
|
||||
67
front/src/component/Footer.js
Archivo normal
67
front/src/component/Footer.js
Archivo normal
@@ -0,0 +1,67 @@
|
||||
import { useEffect, useState, useCallback } from 'react';
|
||||
import Messenger from '../random-text';
|
||||
|
||||
const Footer = (prop) => {
|
||||
const [theme, setTheme] = useState('dark'),
|
||||
[matrix, setMatrix] = useState('off'),
|
||||
[served, setServed] = useState({ served: 0, lastscan: 0, server: 0, instances: 0, peers: 0, created: 0, updated: 0 }),
|
||||
loading = () => {
|
||||
document.querySelector('.loader-content').style.display = 'initial'
|
||||
setTimeout(() => {
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
}, 60 * 1000)
|
||||
},
|
||||
toggleTheme = useCallback(() => {
|
||||
let tm = document.documentElement.dataset.theme
|
||||
if (!theme || tm === 'light') {
|
||||
setTheme('dark')
|
||||
} else {
|
||||
setTheme('light')
|
||||
}
|
||||
}),
|
||||
refreshServed = useCallback(async () => {
|
||||
try {
|
||||
const response = await fetch('/api/served')
|
||||
if (response.ok) {
|
||||
setServed(await response.json())
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
}
|
||||
}),
|
||||
toggleMatrix = useCallback(() => {
|
||||
if (matrix === 'off') {
|
||||
setMatrix('on')
|
||||
prop.setCurrentMatrix('on')
|
||||
} else {
|
||||
setMatrix('off')
|
||||
prop.setCurrentMatrix('off')
|
||||
}
|
||||
})
|
||||
useEffect(() => {
|
||||
setTheme(theme)
|
||||
document.documentElement.dataset.theme = theme
|
||||
refreshServed()
|
||||
if (matrix === 'on') {
|
||||
var walker = document.createTreeWalker(document.getElementById('root'), NodeFilter.SHOW_TEXT)
|
||||
while (walker.nextNode()) {
|
||||
if (walker.currentNode.textContent.length > 1) {
|
||||
new Messenger(walker.currentNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [theme])
|
||||
return (
|
||||
<footer>Served <span className="served">{served.served}</span> times - Last scan <span className="lastscan">{served.lastscan}</span> peers of <span
|
||||
className="server">{served.server}</span><br />
|
||||
Total scanned <span id="instances">{served.instances}</span> instances with <span className="peers">{served.peers}</span> peers - <span
|
||||
className="created">{served.created}</span> created - <span className="updated">{served.updated}</span> updated<br />
|
||||
matrix <a className="matrix" onClick={() => toggleMatrix()}>{matrix}</a> - download json <a className="download_index" href="/api/download_index"
|
||||
onClick={() => loading()} download="fediblock-index.json.gz" target="_blank">index</a> -
|
||||
by <a href="https://about.manalejandro.com" target="_blank">ale</a> <s>©</s>2025
|
||||
<a className="darklight" onClick={() => toggleTheme()}>{!theme || theme === 'dark' ? '☼' : '☽'}</a>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
|
||||
export default Footer;
|
||||
167
front/src/component/Form.js
Archivo normal
167
front/src/component/Form.js
Archivo normal
@@ -0,0 +1,167 @@
|
||||
import { useEffect, useState, useCallback, useRef } from 'react';
|
||||
import Modal from './Modal';
|
||||
import Messenger from '../random-text';
|
||||
|
||||
const Form = (prop) => {
|
||||
let csv
|
||||
const [searchTerm, setSearchTerm] = useState(''),
|
||||
[list, setList] = useState([]),
|
||||
[typeList, setTypeList] = useState('ranking'),
|
||||
[domain, setDomain] = useState({ domain: '' }),
|
||||
[reverse, setReverse] = useState(false),
|
||||
refList = useRef(null),
|
||||
refDownload = useRef(null),
|
||||
loading = () => {
|
||||
document.querySelector('.loader-content').style.display = 'initial'
|
||||
},
|
||||
download = () => {
|
||||
if (csv.split('\n').length > 2) {
|
||||
refDownload.current.href = window.URL.createObjectURL(new Blob([csv], { type: 'text/csv' }))
|
||||
refDownload.current.download = 'fediblock-top100.csv'
|
||||
}
|
||||
},
|
||||
listinstance = useCallback(async content => {
|
||||
loading()
|
||||
if (content && content.length > 0) {
|
||||
try {
|
||||
const result = await fetch('/api/list/' + content),
|
||||
res = await result.json()
|
||||
if (res && Array.isArray(res.instances) && Array.isArray(res.suggests)) {
|
||||
setTypeList('list')
|
||||
setList(res)
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
} else {
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
window.alert('Error: No response')
|
||||
}
|
||||
} catch (e) {
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
window.alert('Error: ' + e.message)
|
||||
}
|
||||
} else {
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
}
|
||||
}),
|
||||
ranking = useCallback(async () => {
|
||||
loading()
|
||||
try {
|
||||
const result = await fetch('/api/ranking'),
|
||||
res = await result.json()
|
||||
if (Array.isArray(res) && res.length > 0) {
|
||||
setTypeList('ranking')
|
||||
setList(res)
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
} else {
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
window.alert('Error: No response')
|
||||
}
|
||||
} catch (e) {
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
window.alert('Error: ' + e.message)
|
||||
}
|
||||
}),
|
||||
filterKeys = useCallback(event => {
|
||||
if (event.key && !event.ctrlKey && !event.altKey) {
|
||||
if ((event.key.length === 1 && /[a-z0-9.\-*:]/i.test(event.key)) || (event.key === 'Backspace' && event.target.value !== '')) {
|
||||
if (event.key === '.' && event.target.value === '') {
|
||||
return
|
||||
} else if (event.key === 'Backspace' && event.target.value !== '') {
|
||||
setSearchTerm(searchTerm.substring(0, searchTerm.length - 1))
|
||||
} else {
|
||||
setSearchTerm(searchTerm + event.key)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
useEffect(() => {
|
||||
if (searchTerm && searchTerm.length > 0) {
|
||||
listinstance(searchTerm)
|
||||
window.location.hash = searchTerm
|
||||
} else {
|
||||
(async () => {
|
||||
await ranking()
|
||||
})()
|
||||
}
|
||||
if (prop.matrix === 'on') {
|
||||
var walker = document.createTreeWalker(refList.current, NodeFilter.SHOW_TEXT)
|
||||
while (walker.nextNode()) {
|
||||
if (walker.currentNode.textContent.length > 1) {
|
||||
new Messenger(walker.currentNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [searchTerm, prop.matrix])
|
||||
useEffect(() => {
|
||||
if (prop.searchTerm && prop.searchTerm.length > 0) {
|
||||
setSearchTerm(prop.searchTerm)
|
||||
}
|
||||
}, [prop.searchTerm])
|
||||
useEffect(() => {
|
||||
if (window.location.hash && window.location.hash !== '#') {
|
||||
setSearchTerm(window.location.hash.substring(1))
|
||||
}
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<section>
|
||||
<input
|
||||
autoFocus
|
||||
type="text"
|
||||
autoComplete="off"
|
||||
className="instance"
|
||||
placeholder="Type the name of the instance"
|
||||
onKeyUp={(e) => filterKeys(e)}
|
||||
value={searchTerm} />
|
||||
<button className="reverse" title="Reverse search..." onClick={() => {
|
||||
if (searchTerm && searchTerm.length > 0) {
|
||||
setReverse(true)
|
||||
setDomain({ domain: searchTerm })
|
||||
loading()
|
||||
}
|
||||
}}>Reverse</button>
|
||||
<div className="placeholder">{typeList === 'list' ? list.suggests && list.suggests.length > 0 ? list.suggests.map(suggest => (<a onClick={() => setSearchTerm(suggest)}>{suggest}</a>)).reduce((prev, curr) => [prev, ', ', curr]) : [] : []}</div>
|
||||
<ul className="instancelist" ref={refList}>{typeList === 'list' ? list.instances.map(r =>
|
||||
(<li><a onClick={() => {
|
||||
if (r.blocks) {
|
||||
setReverse(false)
|
||||
setDomain({ domain: r.domain })
|
||||
loading()
|
||||
} else {
|
||||
var a = document.createElement('a')
|
||||
a.href = '/api/detail_api/' + r.domain
|
||||
a.title = 'API info for ' + r.domain
|
||||
a.target = '_blank'
|
||||
a.dispatchEvent(new MouseEvent('click'))
|
||||
}
|
||||
}}>{r.domain}</a>
|
||||
<span dangerouslySetInnerHTML={{ __html: r.blocks ? ` - ` + r.blocks + ` blocks ` : ` ` }}></span>
|
||||
<span dangerouslySetInnerHTML={{
|
||||
__html: r.nodeinfo ? `<a href="/api/detail_nodeinfo/${r.domain}" title="Nodeinfo for ${r.domain}" target="_blank">ⓘ</a>` + `<br />` : `<br />`
|
||||
}}></span>
|
||||
<span dangerouslySetInnerHTML={{
|
||||
__html: r.api?.title ? `<br /><br />` + r.api.title + ` - ` + r.api.uri + `<br />` : `<br /><br />`
|
||||
}}></span>
|
||||
<span dangerouslySetInnerHTML={{ __html: r.last ? `Last update: ` + (new Date(r.last)).toLocaleString() + `<br />` : `` }}></span>
|
||||
<span dangerouslySetInnerHTML={{ __html: r.api?.email ? `Email: ` + r.api.email + `<br />` : `` }}></span>
|
||||
<span dangerouslySetInnerHTML={{ __html: `Registration: ` + (r.api?.registrations ? `open` : `closed`) + ` - Version: ` + r.api?.version + `<br />` }}></span>
|
||||
<span dangerouslySetInnerHTML={{ __html: r.api?.stats ? `Users: ` + r.api.stats.user_count + ` - Statuses: ` + r.api.stats.status_count + ` - Domains: ` + r.api.stats.domain_count + `<br />` : `` }}></span>
|
||||
<span dangerouslySetInnerHTML={{ __html: r.api?.description ? `Description: ` + r.api.description + `<br />` : `` }}></span>
|
||||
<span dangerouslySetInnerHTML={{ __html: r.api?.thumbnail ? `<img domain="${r.domain}" loading="lazy" src="${r.api.thumbnail}" />` + `<br />` : `` }}></span>
|
||||
</li>
|
||||
)) : typeList === 'ranking' ? list.map((r, i) => {
|
||||
if (i === 0) {
|
||||
csv = '#domain,#severity,#reject_media,#reject_reports,#public_comment,#obfuscate\n'
|
||||
return (<><li><strong>Top 100</strong><br /><small className="download-top"><a ref={refDownload} onClick={() => download()}>(Import CSV)</a></small></li><li>{i + 1} - {r.domain} - {r.count} blocks</li></>)
|
||||
} else {
|
||||
csv += !r.domain.match(/\*/) ? r.domain + ',suspend,False,False,"suspended by top 100 of ' + new URL(window.location.href).host + '",False\n' : ''
|
||||
return (<li>{i + 1} - {r.domain} - {r.count} blocks</li>)
|
||||
}
|
||||
}) : ''}
|
||||
</ul>
|
||||
</section >
|
||||
<Modal domain={domain} reverse={reverse} setSearch={d => setSearchTerm(d)} matrix={prop.matrix} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Form;
|
||||
19
front/src/component/Loader.js
Archivo normal
19
front/src/component/Loader.js
Archivo normal
@@ -0,0 +1,19 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import '../loaders.css';
|
||||
|
||||
const Loader = () => {
|
||||
const loaders = ['loader-pong', 'loader-pacman', 'loader-abyss', 'loader-jump', 'loader-loading', 'loader-avenger', 'loader-mario'],
|
||||
[load, setLoad] = useState('load ' + loaders[Math.floor(Math.random() * loaders.length)])
|
||||
useEffect(() => {
|
||||
setLoad('load ' + loaders[Math.floor(Math.random() * loaders.length)])
|
||||
}, [])
|
||||
return (
|
||||
<div className="loader-content">
|
||||
<div className="loader">
|
||||
<div className={load}></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Loader;
|
||||
142
front/src/component/Modal.js
Archivo normal
142
front/src/component/Modal.js
Archivo normal
@@ -0,0 +1,142 @@
|
||||
import { useEffect, useCallback, useState, useRef } from 'react';
|
||||
import html2canvas from '../../node_modules/html2canvas/dist/html2canvas.js';
|
||||
import Messenger from '../random-text.js';
|
||||
|
||||
const Modal = (prop) => {
|
||||
let csv
|
||||
const [blocktitle, setBlocktitle] = useState(''),
|
||||
[blockcount, setBlockcount] = useState(''),
|
||||
[blockinstance, setBlockinstance] = useState(''),
|
||||
[blocktook, setBlocktook] = useState(''),
|
||||
[blocklist, setBlocklist] = useState([]),
|
||||
hrefCanvas = useRef(null),
|
||||
refList = useRef(null),
|
||||
loading = () => {
|
||||
document.querySelector('.loader-content').style.display = 'initial'
|
||||
},
|
||||
closeModal = event => {
|
||||
if (event.target.classList.contains('modal') || event.target.classList.contains('closemodal')) {
|
||||
document.querySelector('.modal-content').style.animationName = 'animatebottom'
|
||||
}
|
||||
},
|
||||
animationEnd = event => {
|
||||
if (event.animationName === 'animatebottom') {
|
||||
document.querySelector('.modal').style.display = 'none'
|
||||
}
|
||||
},
|
||||
capture = useCallback(async () => {
|
||||
const canvas = await html2canvas(hrefCanvas.current, { useCORS: true }),
|
||||
capture = document.querySelector('.capture')
|
||||
capture.download = 'fediblock-' + Date.now() + '.png'
|
||||
capture.href = canvas.toDataURL('image/png', 1.0)
|
||||
capture.dispatchEvent(new MouseEvent('click'))
|
||||
}),
|
||||
download = () => {
|
||||
const download = document.querySelector('.download')
|
||||
if (csv.split('\n').length > 2) {
|
||||
download.href = window.URL.createObjectURL(new Blob([csv], { type: 'text/csv' }))
|
||||
download.download = 'fediblock-' + prop.domain.domain + '.csv'
|
||||
}
|
||||
},
|
||||
reverse = useCallback(async content => {
|
||||
if (content && content.length > 0) {
|
||||
loading()
|
||||
const result = await fetch('/api/block_count/' + content),
|
||||
res = await result.json()
|
||||
if (res && Array.isArray(res.instances)) {
|
||||
setBlocktitle('Reverse List')
|
||||
setBlockcount(res.block_count)
|
||||
setBlockinstance('ing ' + content)
|
||||
setBlocktook('took ' + res.took + 'ms')
|
||||
setBlocklist(res.instances)
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
document.querySelector('.modal-content').style.animationName = 'animatetop'
|
||||
document.querySelector('.modal').style.display = 'block'
|
||||
if (prop.matrix === 'on') {
|
||||
var walker = document.createTreeWalker(refList.current, NodeFilter.SHOW_TEXT)
|
||||
while (walker.nextNode()) {
|
||||
if (walker.currentNode.textContent.length > 1) {
|
||||
new Messenger(walker.currentNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}),
|
||||
listblock = useCallback(async (domain) => {
|
||||
const result = await fetch('/api/detail/' + domain),
|
||||
res = await result.json()
|
||||
if (res.blocks && Array.isArray(res.blocks)) {
|
||||
csv = '#domain,#severity,#reject_media,#reject_reports,#public_comment,#obfuscate\n'
|
||||
setBlocktitle('Blocked List')
|
||||
setBlockcount(res.blocks.length)
|
||||
setBlocktook('took ' + res.took + 'ms')
|
||||
setBlockinstance(`ed by ` + (res.api ? `<a href="/api/detail_api/${res.instance}" title="API info for ${res.instance}" target="_blank">${res.instance}</a>`
|
||||
: res.instance) + (res.nodeinfo ? ` <a href="/api/detail_nodeinfo/${res.instance}" title="Nodeinfo for ${res.instance}" target="_blank">ⓘ</a><br />` : `<br />`)
|
||||
+ `Last update: ` + (new Date(res.last)).toLocaleString())
|
||||
setBlocklist(res.blocks)
|
||||
if (prop.matrix === 'on') {
|
||||
var walker = document.createTreeWalker(refList.current, NodeFilter.SHOW_TEXT)
|
||||
while (walker.nextNode()) {
|
||||
if (walker.currentNode.textContent.length > 1) {
|
||||
new Messenger(walker.currentNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
var a = document.createElement('a')
|
||||
a.href = '/api/detail_api/' + domain
|
||||
a.title = 'API info for ' + domain
|
||||
a.target = '_blank'
|
||||
a.dispatchEvent(new MouseEvent('click'))
|
||||
}
|
||||
document.querySelector('.loader-content').style.display = 'none'
|
||||
document.querySelector('.modal-content').style.animationName = 'animatetop'
|
||||
document.querySelector('.modal').style.display = 'block'
|
||||
})
|
||||
useEffect(() => {
|
||||
if (prop.domain && prop.domain.domain.length > 0) {
|
||||
if (prop.reverse) {
|
||||
reverse(prop.domain.domain)
|
||||
} else {
|
||||
listblock(prop.domain.domain)
|
||||
}
|
||||
}
|
||||
}, [prop.domain, prop.reverse])
|
||||
return (
|
||||
<div className="modal" onClick={(e) => closeModal(e)}>
|
||||
<div className="modal-content" onAnimationEnd={(e) => animationEnd(e)} ref={refList}>
|
||||
<div className="modal-header">
|
||||
<span className="closemodal" title="Close" onClick={(e) => closeModal(e)}>×</span>
|
||||
<a className="capture" title="Take snapshot" onClick={capture} type="image/png" target="_blank">📷</a>
|
||||
{!prop.reverse ? <a className="download" title="Download fediblock CSV mastodon file" onClick={download} type="text/csv" target="_blank">⇩</a> : ''}
|
||||
<h2>{blocktitle}</h2>
|
||||
</div>
|
||||
<div className="modal-body" ref={hrefCanvas}>
|
||||
<p>
|
||||
<span className="blockcount">{blockcount}</span> public instances are block<span className="blockinstance" dangerouslySetInnerHTML={{ __html: blockinstance }}></span>
|
||||
<br /><small className="blocktook">{blocktook}</small>
|
||||
</p>
|
||||
<ul className="blocklist">{prop.reverse ? blocklist.map((instance, index) => {
|
||||
return (<li>{index + 1}. <a onClick={() => {
|
||||
prop.setSearch(instance.instance)
|
||||
document.querySelector('.modal-content').style.animationName = 'animatebottom'
|
||||
}}>{instance.instance}</a>{instance.comment ? ' - ' + instance.comment : ''}</li>)
|
||||
}) : blocklist.map((r, i) => {
|
||||
if (r?.domain) {
|
||||
csv += !r.domain.match(/\*/) ? r.domain + ',' + (r.severity ? r.severity : '') + ',False,False,' + (r.comment ? '"' + r.comment + '"' : '') + ',False\n' : ''
|
||||
return (<li>{i + 1}. <a onClick={() => {
|
||||
prop.setSearch(r.domain)
|
||||
document.querySelector('.modal-content').style.animationName = 'animatebottom'
|
||||
}}>{r.domain}</a>{r.severity ? ' - ' + r.severity : ''}{r.comment ? ' - ' + r.comment : ''}</li>)
|
||||
}
|
||||
})}</ul>
|
||||
</div>
|
||||
<div className="modal-footer">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Modal;
|
||||
31
front/src/component/Scan.js
Archivo normal
31
front/src/component/Scan.js
Archivo normal
@@ -0,0 +1,31 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
|
||||
const Scan = () => {
|
||||
const [scan, setScan] = useState('Scanning...'),
|
||||
load = useRef(false)
|
||||
useEffect(() => {
|
||||
if (!load.current) {
|
||||
const source = new window.EventSource('/api/scan')
|
||||
source.onmessage = event => {
|
||||
if (event.data) {
|
||||
if (event.data.length > 0) {
|
||||
setScan('Async Scanning' + event.data)
|
||||
} else {
|
||||
setScan('Async Scanning...')
|
||||
}
|
||||
}
|
||||
}
|
||||
source.onerror = error => {
|
||||
console.error(error)
|
||||
}
|
||||
load.current = true
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div>
|
||||
<span className="scan">{scan}</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Scan;
|
||||
13
front/src/component/Title.js
Archivo normal
13
front/src/component/Title.js
Archivo normal
@@ -0,0 +1,13 @@
|
||||
import { useCallback } from 'react';
|
||||
|
||||
const Title = () => {
|
||||
const click = useCallback(() => {
|
||||
window.location.hash = ''
|
||||
window.location.reload(false)
|
||||
})
|
||||
return (
|
||||
<h1><a onClick={() => click()} className="title">Fediblock Instance Φ</a></h1>
|
||||
)
|
||||
}
|
||||
|
||||
export default Title;
|
||||
12
front/src/index.js
Archivo normal
12
front/src/index.js
Archivo normal
@@ -0,0 +1,12 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
reportWebVitals();
|
||||
411
front/src/loaders.css
Archivo normal
411
front/src/loaders.css
Archivo normal
@@ -0,0 +1,411 @@
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.loader-pong {
|
||||
width: 80px;
|
||||
height: 70px;
|
||||
border: 5px solid #000;
|
||||
padding: 0 8px;
|
||||
box-sizing: border-box;
|
||||
background:
|
||||
linear-gradient(#fff 0 0) 0 0/8px 20px,
|
||||
linear-gradient(#fff 0 0) 100% 0/8px 20px,
|
||||
radial-gradient(farthest-side, #fff 90%, #0000) 0 5px/8px 8px content-box,
|
||||
#000;
|
||||
background-repeat: no-repeat;
|
||||
animation: l3 2s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes l3 {
|
||||
25% {
|
||||
background-position: 0 0, 100% 100%, 100% calc(100% - 5px)
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: 0 100%, 100% 100%, 0 calc(100% - 5px)
|
||||
}
|
||||
|
||||
75% {
|
||||
background-position: 0 100%, 100% 0, 100% 5px
|
||||
}
|
||||
}
|
||||
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.loader-pacman {
|
||||
width: 90px;
|
||||
height: 24px;
|
||||
padding: 2px 0;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
animation: l5-0 3s infinite steps(6);
|
||||
background:
|
||||
linear-gradient(#000 0 0) 0 0/0% 100% no-repeat,
|
||||
radial-gradient(circle 3px, #eeee89 90%, #0000) 0 0/20% 100% #000;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.loader-pacman::before {
|
||||
content: "";
|
||||
width: 20px;
|
||||
transform: translate(-100%);
|
||||
border-radius: 50%;
|
||||
background: #ffff2d;
|
||||
animation:
|
||||
l5-1 .25s .153s infinite steps(5) alternate,
|
||||
l5-2 3s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes l5-1 {
|
||||
0% {
|
||||
clip-path: polygon(50% 50%, 100% 0, 100% 0, 0 0, 0 100%, 100% 100%, 100% 100%)
|
||||
}
|
||||
|
||||
100% {
|
||||
clip-path: polygon(50% 50%, 100% 65%, 100% 0, 0 0, 0 100%, 100% 100%, 100% 35%)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l5-2 {
|
||||
100% {
|
||||
transform: translate(90px)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l5-0 {
|
||||
100% {
|
||||
background-size: 120% 100%, 20% 100%
|
||||
}
|
||||
}
|
||||
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.loader-abyss {
|
||||
width: 80px;
|
||||
height: 60px;
|
||||
box-sizing: border-box;
|
||||
background:
|
||||
linear-gradient(#fff 0 0) left /calc(50% - 15px) 8px no-repeat,
|
||||
linear-gradient(#fff 0 0) right/calc(50% - 15px) 8px no-repeat,
|
||||
conic-gradient(from 135deg at top, #0000, red 1deg 90deg, #0000 91deg) bottom/14px 8px repeat-x,
|
||||
#000;
|
||||
border-bottom: 2px solid red;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
animation: l6-0 1s infinite linear;
|
||||
}
|
||||
|
||||
.loader-abyss::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 14px;
|
||||
background: lightblue;
|
||||
left: -5px;
|
||||
animation:
|
||||
l6-1 2s infinite cubic-bezier(0, 100, 1, 100),
|
||||
l6-2 2s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes l6-0 {
|
||||
50% {
|
||||
background-position: left, right, bottom -2px left -4px
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l6-1 {
|
||||
|
||||
0%,
|
||||
27% {
|
||||
bottom: calc(50% + 4px)
|
||||
}
|
||||
|
||||
65%,
|
||||
100% {
|
||||
bottom: calc(50% + 4.1px)
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l6-2 {
|
||||
100% {
|
||||
left: 100%
|
||||
}
|
||||
}
|
||||
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.loader-jump {
|
||||
width: 70px;
|
||||
height: 50px;
|
||||
box-sizing: border-box;
|
||||
background:
|
||||
conic-gradient(from 135deg at top, #0000, #fff 1deg 90deg, #0000 91deg) right -20px bottom 8px/18px 9px,
|
||||
linear-gradient(#fff 0 0) bottom/100% 8px,
|
||||
#000;
|
||||
background-repeat: no-repeat;
|
||||
border-bottom: 8px solid #000;
|
||||
position: relative;
|
||||
animation: l7-0 2s infinite linear;
|
||||
}
|
||||
|
||||
.loader-jump::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 14px;
|
||||
background: lightblue;
|
||||
left: 10px;
|
||||
animation: l7-1 2s infinite cubic-bezier(0, 200, 1, 200);
|
||||
}
|
||||
|
||||
@keyframes l7-0 {
|
||||
100% {
|
||||
background-position: left -20px bottom 8px, bottom
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l7-1 {
|
||||
|
||||
0%,
|
||||
50% {
|
||||
bottom: 8px
|
||||
}
|
||||
|
||||
90%,
|
||||
100% {
|
||||
bottom: 8.1px
|
||||
}
|
||||
}
|
||||
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.loader-loading {
|
||||
width: fit-content;
|
||||
font-size: 17px;
|
||||
font-family: monospace;
|
||||
line-height: 1.4;
|
||||
font-weight: bold;
|
||||
--c: no-repeat linear-gradient(#000 0 0);
|
||||
background: var(--c), var(--c), var(--c), var(--c), var(--c), var(--c), var(--c);
|
||||
background-size: calc(1ch + 1px) 100%;
|
||||
border-bottom: 10px solid #0000;
|
||||
position: relative;
|
||||
animation: l8-0 3s infinite linear;
|
||||
clip-path: inset(-20px 0);
|
||||
}
|
||||
|
||||
.loader-loading::before {
|
||||
content: "Loading";
|
||||
}
|
||||
|
||||
.loader-loading::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 14px;
|
||||
background: #25adda;
|
||||
left: -10px;
|
||||
bottom: 100%;
|
||||
animation: l8-1 3s infinite linear;
|
||||
}
|
||||
|
||||
@keyframes l8-0 {
|
||||
|
||||
0%,
|
||||
12.5% {
|
||||
background-position: calc(0*100%/6) 0, calc(1*100%/6) 0, calc(2*100%/6) 0, calc(3*100%/6) 0, calc(4*100%/6) 0, calc(5*100%/6) 0, calc(6*100%/6) 0
|
||||
}
|
||||
|
||||
25% {
|
||||
background-position: calc(0*100%/6) 40px, calc(1*100%/6) 0, calc(2*100%/6) 0, calc(3*100%/6) 0, calc(4*100%/6) 0, calc(5*100%/6) 0, calc(6*100%/6) 0
|
||||
}
|
||||
|
||||
37.5% {
|
||||
background-position: calc(0*100%/6) 40px, calc(1*100%/6) 40px, calc(2*100%/6) 0, calc(3*100%/6) 0, calc(4*100%/6) 0, calc(5*100%/6) 0, calc(6*100%/6) 0
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: calc(0*100%/6) 40px, calc(1*100%/6) 40px, calc(2*100%/6) 40px, calc(3*100%/6) 0, calc(4*100%/6) 0, calc(5*100%/6) 0, calc(6*100%/6) 0
|
||||
}
|
||||
|
||||
62.5% {
|
||||
background-position: calc(0*100%/6) 40px, calc(1*100%/6) 40px, calc(2*100%/6) 40px, calc(3*100%/6) 40px, calc(4*100%/6) 0, calc(5*100%/6) 0, calc(6*100%/6) 0
|
||||
}
|
||||
|
||||
75% {
|
||||
background-position: calc(0*100%/6) 40px, calc(1*100%/6) 40px, calc(2*100%/6) 40px, calc(3*100%/6) 40px, calc(4*100%/6) 40px, calc(5*100%/6) 0, calc(6*100%/6) 0
|
||||
}
|
||||
|
||||
87.4% {
|
||||
background-position: calc(0*100%/6) 40px, calc(1*100%/6) 40px, calc(2*100%/6) 40px, calc(3*100%/6) 40px, calc(4*100%/6) 40px, calc(5*100%/6) 40px, calc(6*100%/6) 0
|
||||
}
|
||||
|
||||
100% {
|
||||
background-position: calc(0*100%/6) 40px, calc(1*100%/6) 40px, calc(2*100%/6) 40px, calc(3*100%/6) 40px, calc(4*100%/6) 40px, calc(5*100%/6) 40px, calc(6*100%/6) 40px
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l8-1 {
|
||||
100% {
|
||||
left: 115%
|
||||
}
|
||||
}
|
||||
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.loader-avenger {
|
||||
width: fit-content;
|
||||
font-size: 17px;
|
||||
font-family: monospace;
|
||||
line-height: 1.4;
|
||||
font-weight: bold;
|
||||
background:
|
||||
linear-gradient(#000 0 0) left,
|
||||
linear-gradient(#000 0 0) right;
|
||||
background-repeat: no-repeat;
|
||||
border-right: 5px solid #0000;
|
||||
border-left: 5px solid #0000;
|
||||
background-origin: border-box;
|
||||
position: relative;
|
||||
animation: l9-0 2s infinite;
|
||||
}
|
||||
|
||||
.loader-avenger::before {
|
||||
content: "Loading";
|
||||
}
|
||||
|
||||
.loader-avenger::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
width: 22px;
|
||||
height: 60px;
|
||||
background:
|
||||
linear-gradient(90deg, #000 4px, #0000 0 calc(100% - 4px), #000 0) bottom /22px 20px,
|
||||
linear-gradient(90deg, red 4px, #0000 0 calc(100% - 4px), red 0) bottom 10px left 0/22px 6px,
|
||||
linear-gradient(#000 0 0) bottom 3px left 0 /22px 8px,
|
||||
linear-gradient(#000 0 0) bottom 0 left 50%/8px 16px;
|
||||
background-repeat: no-repeat;
|
||||
animation: l9-1 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes l9-0 {
|
||||
|
||||
0%,
|
||||
25% {
|
||||
background-size: 50% 100%
|
||||
}
|
||||
|
||||
25.1%,
|
||||
75% {
|
||||
background-size: 0 0, 50% 100%
|
||||
}
|
||||
|
||||
75.1%,
|
||||
100% {
|
||||
background-size: 0 0, 0 0
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l9-1 {
|
||||
25% {
|
||||
background-position: bottom, bottom 54px left 0, bottom 3px left 0, bottom 0 left 50%;
|
||||
left: 0
|
||||
}
|
||||
|
||||
25.1% {
|
||||
background-position: bottom, bottom 10px left 0, bottom 3px left 0, bottom 0 left 50%;
|
||||
left: 0
|
||||
}
|
||||
|
||||
50% {
|
||||
background-position: bottom, bottom 10px left 0, bottom 3px left 0, bottom 0 left 50%;
|
||||
left: calc(100% - 22px)
|
||||
}
|
||||
|
||||
75% {
|
||||
background-position: bottom, bottom 54px left 0, bottom 3px left 0, bottom 0 left 50%;
|
||||
left: calc(100% - 22px)
|
||||
}
|
||||
|
||||
75.1% {
|
||||
background-position: bottom, bottom 10px left 0, bottom 3px left 0, bottom 0 left 50%;
|
||||
left: calc(100% - 22px)
|
||||
}
|
||||
}
|
||||
|
||||
/* HTML: <div class="loader"></div> */
|
||||
.loader-mario {
|
||||
width: fit-content;
|
||||
font-size: 17px;
|
||||
font-family: monospace;
|
||||
line-height: 1.4;
|
||||
font-weight: bold;
|
||||
padding: 30px 2px 50px;
|
||||
background: linear-gradient(#000 0 0) 0 0/100% 100% content-box padding-box no-repeat;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
animation: l10-0 2s infinite cubic-bezier(1, 175, .5, 175);
|
||||
}
|
||||
|
||||
.loader-mario::before {
|
||||
content: "Loading";
|
||||
display: inline-block;
|
||||
animation: l10-2 2s infinite;
|
||||
}
|
||||
|
||||
.loader-mario::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
width: 34px;
|
||||
height: 28px;
|
||||
top: 110%;
|
||||
left: calc(50% - 16px);
|
||||
background:
|
||||
linear-gradient(90deg, #0000 12px, #f92033 0 22px, #0000 0 26px, #fdc98d 0 32px, #0000) bottom 26px left 50%,
|
||||
linear-gradient(90deg, #0000 10px, #f92033 0 28px, #fdc98d 0 32px, #0000 0) bottom 24px left 50%,
|
||||
linear-gradient(90deg, #0000 10px, #643700 0 16px, #fdc98d 0 20px, #000 0 22px, #fdc98d 0 24px, #000 0 26px, #f92033 0 32px, #0000 0) bottom 22px left 50%,
|
||||
linear-gradient(90deg, #0000 8px, #643700 0 10px, #fdc98d 0 12px, #643700 0 14px, #fdc98d 0 20px, #000 0 22px, #fdc98d 0 28px, #f92033 0 32px, #0000 0) bottom 20px left 50%,
|
||||
linear-gradient(90deg, #0000 8px, #643700 0 10px, #fdc98d 0 12px, #643700 0 16px, #fdc98d 0 22px, #000 0 24px, #fdc98d 0 30px, #f92033 0 32px, #0000 0) bottom 18px left 50%,
|
||||
linear-gradient(90deg, #0000 8px, #643700 0 12px, #fdc98d 0 20px, #000 0 28px, #f92033 0 30px, #0000 0) bottom 16px left 50%,
|
||||
linear-gradient(90deg, #0000 12px, #fdc98d 0 26px, #f92033 0 30px, #0000 0) bottom 14px left 50%,
|
||||
linear-gradient(90deg, #fdc98d 6px, #f92033 0 14px, #222a87 0 16px, #f92033 0 22px, #222a87 0 24px, #f92033 0 28px, #0000 0 32px, #643700 0) bottom 12px left 50%,
|
||||
linear-gradient(90deg, #fdc98d 6px, #f92033 0 16px, #222a87 0 18px, #f92033 0 24px, #f92033 0 26px, #0000 0 30px, #643700 0) bottom 10px left 50%,
|
||||
linear-gradient(90deg, #0000 10px, #f92033 0 16px, #222a87 0 24px, #feee49 0 26px, #222a87 0 30px, #643700 0) bottom 8px left 50%,
|
||||
linear-gradient(90deg, #0000 12px, #222a87 0 18px, #feee49 0 20px, #222a87 0 30px, #643700 0) bottom 6px left 50%,
|
||||
linear-gradient(90deg, #0000 8px, #643700 0 12px, #222a87 0 30px, #643700 0) bottom 4px left 50%,
|
||||
linear-gradient(90deg, #0000 6px, #643700 0 14px, #222a87 0 26px, #0000 0) bottom 2px left 50%,
|
||||
linear-gradient(90deg, #0000 6px, #643700 0 10px, #0000 0) bottom 0px left 50%;
|
||||
background-size: 34px 2px;
|
||||
background-repeat: no-repeat;
|
||||
animation: inherit;
|
||||
animation-name: l10-1;
|
||||
}
|
||||
|
||||
@keyframes l10-0 {
|
||||
|
||||
0%,
|
||||
30% {
|
||||
background-position: 0 0px
|
||||
}
|
||||
|
||||
50%,
|
||||
100% {
|
||||
background-position: 0 -0.1px
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l10-1 {
|
||||
|
||||
50%,
|
||||
100% {
|
||||
top: 109.5%
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes l10-2 {
|
||||
|
||||
0%,
|
||||
30% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
80%,
|
||||
100% {
|
||||
transform: translateY(-260%);
|
||||
}
|
||||
}
|
||||
1
front/src/logo.svg
Archivo normal
1
front/src/logo.svg
Archivo normal
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"><g fill="#61DAFB"><path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/><circle cx="420.9" cy="296.5" r="45.7"/><path d="M520.5 78.1z"/></g></svg>
|
||||
|
Después Anchura: | Altura: | Tamaño: 2.6 KiB |
69
front/src/random-text.js
Archivo normal
69
front/src/random-text.js
Archivo normal
@@ -0,0 +1,69 @@
|
||||
var Messenger = function (el) {
|
||||
'use strict';
|
||||
var m = this;
|
||||
|
||||
m.init = function () {
|
||||
m.codeletters = "&#*+%?£@§$";
|
||||
m.current_length = 0;
|
||||
m.fadeBuffer = false;
|
||||
m.message = el.textContent.length > 0 ? el.textContent : ''
|
||||
|
||||
setTimeout(m.animateIn, 300);
|
||||
};
|
||||
|
||||
m.generateRandomString = function (length) {
|
||||
var random_text = '';
|
||||
while (random_text.length < length) {
|
||||
random_text += m.codeletters.charAt(Math.floor(Math.random() * m.codeletters.length));
|
||||
}
|
||||
|
||||
return random_text;
|
||||
};
|
||||
|
||||
m.animateIn = function () {
|
||||
if (m.current_length < m.message.length) {
|
||||
m.current_length = m.current_length + 2;
|
||||
if (m.current_length > m.message.length) {
|
||||
m.current_length = m.message.length;
|
||||
}
|
||||
|
||||
var message = m.generateRandomString(m.current_length);
|
||||
el.textContent = message;
|
||||
|
||||
setTimeout(m.animateIn, 60);
|
||||
} else {
|
||||
setTimeout(m.animateFadeBuffer, 60);
|
||||
}
|
||||
};
|
||||
|
||||
m.animateFadeBuffer = function () {
|
||||
if (m.fadeBuffer === false) {
|
||||
m.fadeBuffer = [];
|
||||
for (var i = 0; i < m.message.length; i++) {
|
||||
m.fadeBuffer.push({ c: (Math.floor(Math.random() * 12)) + 1, l: m.message.charAt(i) });
|
||||
}
|
||||
}
|
||||
|
||||
var do_cycles = false;
|
||||
var message = '';
|
||||
|
||||
for (var i = 0; i < m.fadeBuffer.length; i++) {
|
||||
var fader = m.fadeBuffer[i];
|
||||
if (fader.c > 0) {
|
||||
do_cycles = true;
|
||||
fader.c--;
|
||||
message += m.codeletters.charAt(Math.floor(Math.random() * m.codeletters.length));
|
||||
} else {
|
||||
message += fader.l;
|
||||
}
|
||||
}
|
||||
|
||||
el.textContent = message;
|
||||
|
||||
setTimeout(m.animateFadeBuffer, 150);
|
||||
};
|
||||
|
||||
m.init();
|
||||
}
|
||||
|
||||
module.exports = Messenger;
|
||||
13
front/src/reportWebVitals.js
Archivo normal
13
front/src/reportWebVitals.js
Archivo normal
@@ -0,0 +1,13 @@
|
||||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
||||
5
front/src/setupTests.js
Archivo normal
5
front/src/setupTests.js
Archivo normal
@@ -0,0 +1,5 @@
|
||||
// jest-dom adds custom jest matchers for asserting on DOM nodes.
|
||||
// allows you to do things like:
|
||||
// expect(element).toHaveTextContent(/react/i)
|
||||
// learn more: https://github.com/testing-library/jest-dom
|
||||
import '@testing-library/jest-dom';
|
||||
Referencia en una nueva incidencia
Block a user