Podpora easter eggů
This commit is contained in:
parent
98f2b2a1e0
commit
7e4fa236b1
@ -35,8 +35,11 @@ WORKDIR /app
|
|||||||
|
|
||||||
COPY --from=builder /build/node_modules ./node_modules
|
COPY --from=builder /build/node_modules ./node_modules
|
||||||
COPY --from=builder /build/server/dist ./
|
COPY --from=builder /build/server/dist ./
|
||||||
|
COPY server/resources ./server/resources
|
||||||
COPY --from=builder /build/client/build ./public
|
COPY --from=builder /build/client/build ./public
|
||||||
COPY /server/.env.production ./server/src
|
COPY /server/.env.production ./server/src
|
||||||
|
# TODO tohle spadne když nebude existovat!
|
||||||
|
COPY /server/.easter-eggs.json ./server/
|
||||||
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
|
@ -23,13 +23,14 @@
|
|||||||
"react-select-search": "^4.1.6",
|
"react-select-search": "^4.1.6",
|
||||||
"react-snowfall": "^2.2.0",
|
"react-snowfall": "^2.2.0",
|
||||||
"react-toastify": "^10.0.4",
|
"react-toastify": "^10.0.4",
|
||||||
|
"sass": "^1.80.6",
|
||||||
"socket.io-client": "^4.6.1",
|
"socket.io-client": "^4.6.1",
|
||||||
"typescript": "^5.3.3"
|
"typescript": "^5.3.3"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"copy-types": "cp -r ../types ./src",
|
"copy-types": "cp -r ../types ./src",
|
||||||
"start": "yarn copy-types && react-scripts start",
|
"start": "yarn copy-types && react-scripts start",
|
||||||
"build": "yarn copy-types && react-scripts build",
|
"build": "yarn copy-types && GENERATE_SOURCEMAP=false react-scripts build",
|
||||||
"test": "react-scripts test",
|
"test": "react-scripts test",
|
||||||
"eject": "react-scripts eject"
|
"eject": "react-scripts eject"
|
||||||
},
|
},
|
||||||
|
@ -123,4 +123,46 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
font-size: xx-large;
|
font-size: xx-large;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes bounce-in {
|
||||||
|
0% {
|
||||||
|
left: var(--start-left);
|
||||||
|
right: var(--start-right);
|
||||||
|
top: var(--start-top);
|
||||||
|
bottom: var(--start-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
25% {
|
||||||
|
left: var(--end-left);
|
||||||
|
right: var(--end-right);
|
||||||
|
top: var(--end-top);
|
||||||
|
bottom: var(--end-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
left: var(--start-left);
|
||||||
|
right: var(--start-right);
|
||||||
|
top: var(--start-top);
|
||||||
|
bottom: var(--start-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
75% {
|
||||||
|
left: var(--end-left);
|
||||||
|
right: var(--end-right);
|
||||||
|
top: var(--end-top);
|
||||||
|
bottom: var(--end-bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
left: var(--start-left);
|
||||||
|
right: var(--start-right);
|
||||||
|
top: var(--start-top);
|
||||||
|
bottom: var(--start-bottom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO zjistit, zda to nedokážeme lépe - tohle je kvůli overflow easter egg obrázků, ale skrývá to úplně scrollbar
|
||||||
|
html {
|
||||||
|
overflow-x: hidden;
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
|||||||
import PizzaOrderList from './components/PizzaOrderList';
|
import PizzaOrderList from './components/PizzaOrderList';
|
||||||
import SelectSearch, { SelectedOptionValue } from 'react-select-search';
|
import SelectSearch, { SelectedOptionValue } from 'react-select-search';
|
||||||
import 'react-select-search/style.css';
|
import 'react-select-search/style.css';
|
||||||
import './App.css';
|
import './App.scss';
|
||||||
import { SelectSearchOption } from 'react-select-search';
|
import { SelectSearchOption } from 'react-select-search';
|
||||||
import { faCircleCheck, faNoteSticky, faTrashCan } from '@fortawesome/free-regular-svg-icons';
|
import { faCircleCheck, faNoteSticky, faTrashCan } from '@fortawesome/free-regular-svg-icons';
|
||||||
import { useSettings } from './context/settings';
|
import { useSettings } from './context/settings';
|
||||||
@ -22,12 +22,25 @@ import { getData, errorHandler, getQrUrl } from './api/Api';
|
|||||||
import { addChoice, removeChoices, removeChoice, changeDepartureTime, jdemeObed, updateNote } from './api/FoodApi';
|
import { addChoice, removeChoices, removeChoice, changeDepartureTime, jdemeObed, updateNote } from './api/FoodApi';
|
||||||
import { getHumanDateTime } from './Utils';
|
import { getHumanDateTime } from './Utils';
|
||||||
import NoteModal from './components/modals/NoteModal';
|
import NoteModal from './components/modals/NoteModal';
|
||||||
|
import { useEasterEgg } from './context/eggs';
|
||||||
|
import { getImage } from './api/EasterEggApi';
|
||||||
|
|
||||||
const EVENT_CONNECT = "connect"
|
const EVENT_CONNECT = "connect"
|
||||||
|
|
||||||
|
// Fixní styl pro všechny easter egg obrázky
|
||||||
|
const EASTER_EGG_STYLE = {
|
||||||
|
zIndex: 1,
|
||||||
|
animationName: "bounce-in",
|
||||||
|
animationTimingFunction: "ease"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Výchozí doba trvání animace v sekundách, pokud není přetíženo v konfiguračním JSONu
|
||||||
|
const EASTER_EGG_DEFAULT_DURATION = 0.75;
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const auth = useAuth();
|
const auth = useAuth();
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
|
const [easterEgg, easterEggLoading] = useEasterEgg(auth);
|
||||||
const [isConnected, setIsConnected] = useState<boolean>(false);
|
const [isConnected, setIsConnected] = useState<boolean>(false);
|
||||||
const [data, setData] = useState<ClientData>();
|
const [data, setData] = useState<ClientData>();
|
||||||
const [food, setFood] = useState<{ [key in Restaurants]?: DayMenu }>();
|
const [food, setFood] = useState<{ [key in Restaurants]?: DayMenu }>();
|
||||||
@ -43,6 +56,8 @@ function App() {
|
|||||||
const [dayIndex, setDayIndex] = useState<number>();
|
const [dayIndex, setDayIndex] = useState<number>();
|
||||||
const [loadingPizzaDay, setLoadingPizzaDay] = useState<boolean>(false);
|
const [loadingPizzaDay, setLoadingPizzaDay] = useState<boolean>(false);
|
||||||
const [noteModalOpen, setNoteModalOpen] = useState<boolean>(false);
|
const [noteModalOpen, setNoteModalOpen] = useState<boolean>(false);
|
||||||
|
const [eggImage, setEggImage] = useState<Blob>();
|
||||||
|
const eggRef = useRef<HTMLImageElement>(null);
|
||||||
// Prazvláštní workaround, aby socket.io listener viděl aktuální hodnotu
|
// Prazvláštní workaround, aby socket.io listener viděl aktuální hodnotu
|
||||||
// https://medium.com/@kishorkrishna/cant-access-latest-state-inside-socket-io-listener-heres-how-to-fix-it-1522a5abebdb
|
// https://medium.com/@kishorkrishna/cant-access-latest-state-inside-socket-io-listener-heres-how-to-fix-it-1522a5abebdb
|
||||||
const dayIndexRef = useRef<number | undefined>(dayIndex);
|
const dayIndexRef = useRef<number | undefined>(dayIndex);
|
||||||
@ -161,6 +176,23 @@ function App() {
|
|||||||
}
|
}
|
||||||
}, [handleKeyDown]);
|
}, [handleKeyDown]);
|
||||||
|
|
||||||
|
// Stažení a nastavení easter egg obrázku
|
||||||
|
useEffect(() => {
|
||||||
|
if (auth?.login && easterEgg?.url && !eggImage) {
|
||||||
|
getImage(easterEgg.url).then(data => {
|
||||||
|
if (data) {
|
||||||
|
setEggImage(data);
|
||||||
|
// Smazání obrázku z DOMu po animaci
|
||||||
|
setTimeout(() => {
|
||||||
|
if (eggRef?.current) {
|
||||||
|
eggRef.current.remove();
|
||||||
|
}
|
||||||
|
}, (easterEgg.duration || EASTER_EGG_DEFAULT_DURATION) * 1000);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [auth?.login, easterEgg?.duration, easterEgg?.url, eggImage]);
|
||||||
|
|
||||||
const doAddChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
const doAddChoice = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
||||||
const index = Object.keys(Locations).indexOf(event.target.value as unknown as Locations);
|
const index = Object.keys(Locations).indexOf(event.target.value as unknown as Locations);
|
||||||
if (auth?.login) {
|
if (auth?.login) {
|
||||||
@ -361,8 +393,11 @@ function App() {
|
|||||||
const noOrders = data?.pizzaDay?.orders?.length === 0;
|
const noOrders = data?.pizzaDay?.orders?.length === 0;
|
||||||
const canChangeChoice = dayIndex == null || data.todayWeekIndex == null || dayIndex >= data.todayWeekIndex;
|
const canChangeChoice = dayIndex == null || data.todayWeekIndex == null || dayIndex >= data.todayWeekIndex;
|
||||||
|
|
||||||
|
const { path, url, startOffset, endOffset, duration, ...style } = easterEgg || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
{easterEgg && eggImage && <img ref={eggRef} alt='' src={URL.createObjectURL(eggImage)} style={{ position: 'absolute', ...EASTER_EGG_STYLE, ...style, animationDuration: `${duration ?? EASTER_EGG_DEFAULT_DURATION}s` }} />}
|
||||||
<Header />
|
<Header />
|
||||||
<div className='wrapper'>
|
<div className='wrapper'>
|
||||||
{data.isWeekend ? <h4>Užívejte víkend :)</h4> : <>
|
{data.isWeekend ? <h4>Užívejte víkend :)</h4> : <>
|
||||||
|
@ -25,11 +25,36 @@ async function request<TResponse>(
|
|||||||
try {
|
try {
|
||||||
const response = await fetch(getBaseUrl() + url, config);
|
const response = await fetch(getBaseUrl() + url, config);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
|
// TODO tohle je blbě, jelikož automaticky očekáváme, že v případě chyby přijde vždy JSON, což není pravda
|
||||||
const json = await response.json();
|
const json = await response.json();
|
||||||
// Vyhodíme samotnou hlášku z odpovědi, odchytí si jí errorHandler
|
// Vyhodíme samotnou hlášku z odpovědi, odchytí si jí errorHandler
|
||||||
throw new Error(json.error);
|
throw new Error(json.error);
|
||||||
}
|
}
|
||||||
return response.json() as TResponse;
|
const contentType = response.headers.get("content-type");
|
||||||
|
if (contentType && contentType.indexOf("application/json") !== -1) {
|
||||||
|
return response.json() as TResponse;
|
||||||
|
} else {
|
||||||
|
return response.text() as TResponse;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return Promise.reject(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function blobRequest(
|
||||||
|
url: string,
|
||||||
|
config: RequestInit = {}
|
||||||
|
): Promise<Blob> {
|
||||||
|
config.headers = config?.headers ? new Headers(config.headers) : new Headers();
|
||||||
|
config.headers.set("Authorization", `Bearer ${getToken()}`);
|
||||||
|
try {
|
||||||
|
const response = await fetch(getBaseUrl() + url, config);
|
||||||
|
if (!response.ok) {
|
||||||
|
const json = await response.json();
|
||||||
|
// Vyhodíme samotnou hlášku z odpovědi, odchytí si jí errorHandler
|
||||||
|
throw new Error(json.error);
|
||||||
|
}
|
||||||
|
return response.blob()
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return Promise.reject(e);
|
return Promise.reject(e);
|
||||||
}
|
}
|
||||||
@ -37,6 +62,7 @@ async function request<TResponse>(
|
|||||||
|
|
||||||
export const api = {
|
export const api = {
|
||||||
get: <TResponse>(url: string) => request<TResponse>(url),
|
get: <TResponse>(url: string) => request<TResponse>(url),
|
||||||
|
blobGet: (url: string) => blobRequest(url),
|
||||||
post: <TBody extends BodyInit, TResponse>(url: string, body: TBody) => request<TResponse>(url, { method: 'POST', body, headers: { 'Content-Type': 'application/json' } }),
|
post: <TBody extends BodyInit, TResponse>(url: string, body: TBody) => request<TResponse>(url, { method: 'POST', body, headers: { 'Content-Type': 'application/json' } }),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
12
client/src/api/EasterEggApi.ts
Normal file
12
client/src/api/EasterEggApi.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { EasterEgg } from "../types";
|
||||||
|
import { api } from "./Api";
|
||||||
|
|
||||||
|
const EASTER_EGGS_API_PREFIX = '/api/easterEggs';
|
||||||
|
|
||||||
|
export const getEasterEgg = async (): Promise<EasterEgg | undefined> => {
|
||||||
|
return await api.get(`${EASTER_EGGS_API_PREFIX}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getImage = async (url: string) => {
|
||||||
|
return await api.blobGet(`${EASTER_EGGS_API_PREFIX}/${url}`);
|
||||||
|
}
|
@ -5,6 +5,7 @@ import { deleteToken, getToken, storeToken } from "../Utils";
|
|||||||
|
|
||||||
export type AuthContextProps = {
|
export type AuthContextProps = {
|
||||||
login?: string,
|
login?: string,
|
||||||
|
trusted?: boolean,
|
||||||
setToken: (name: string) => void,
|
setToken: (name: string) => void,
|
||||||
logout: () => void,
|
logout: () => void,
|
||||||
}
|
}
|
||||||
@ -26,6 +27,7 @@ export const useAuth = () => {
|
|||||||
|
|
||||||
function useProvideAuth(): AuthContextProps {
|
function useProvideAuth(): AuthContextProps {
|
||||||
const [loginName, setLoginName] = useState<string | undefined>();
|
const [loginName, setLoginName] = useState<string | undefined>();
|
||||||
|
const [trusted, setTrusted] = useState<boolean | undefined>();
|
||||||
const [token, setToken] = useState<string | null>(getToken());
|
const [token, setToken] = useState<string | null>(getToken());
|
||||||
const { decodedToken } = useJwt(token || '');
|
const { decodedToken } = useJwt(token || '');
|
||||||
|
|
||||||
@ -40,8 +42,10 @@ function useProvideAuth(): AuthContextProps {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (decodedToken) {
|
if (decodedToken) {
|
||||||
setLoginName((decodedToken as any).login);
|
setLoginName((decodedToken as any).login);
|
||||||
|
setTrusted((decodedToken as any).trusted);
|
||||||
} else {
|
} else {
|
||||||
setLoginName(undefined);
|
setLoginName(undefined);
|
||||||
|
setTrusted(undefined);
|
||||||
}
|
}
|
||||||
}, [decodedToken]);
|
}, [decodedToken]);
|
||||||
|
|
||||||
@ -50,6 +54,7 @@ function useProvideAuth(): AuthContextProps {
|
|||||||
const logoutUrl = (decodedToken as any).logoutUrl;
|
const logoutUrl = (decodedToken as any).logoutUrl;
|
||||||
setToken(null);
|
setToken(null);
|
||||||
setLoginName(undefined);
|
setLoginName(undefined);
|
||||||
|
setTrusted(undefined);
|
||||||
if (trusted && logoutUrl?.length) {
|
if (trusted && logoutUrl?.length) {
|
||||||
window.location.replace(logoutUrl);
|
window.location.replace(logoutUrl);
|
||||||
}
|
}
|
||||||
@ -57,6 +62,7 @@ function useProvideAuth(): AuthContextProps {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
login: loginName,
|
login: loginName,
|
||||||
|
trusted,
|
||||||
setToken,
|
setToken,
|
||||||
logout,
|
logout,
|
||||||
}
|
}
|
||||||
|
24
client/src/context/eggs.tsx
Normal file
24
client/src/context/eggs.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { getEasterEgg } from "../api/EasterEggApi";
|
||||||
|
import { AuthContextProps } from "./auth";
|
||||||
|
import { EasterEgg } from "../types";
|
||||||
|
|
||||||
|
export const useEasterEgg = (auth?: AuthContextProps | null): [EasterEgg | undefined, boolean] => {
|
||||||
|
const [result, setResult] = useState<EasterEgg | undefined>();
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchEasterEgg() {
|
||||||
|
if (auth?.login) {
|
||||||
|
setLoading(true);
|
||||||
|
const egg = await getEasterEgg();
|
||||||
|
setResult(egg);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchEasterEgg();
|
||||||
|
|
||||||
|
}, [auth?.login]);
|
||||||
|
|
||||||
|
return [result, loading];
|
||||||
|
}
|
3
server/.gitignore
vendored
3
server/.gitignore
vendored
@ -3,4 +3,5 @@
|
|||||||
data.json
|
data.json
|
||||||
.env.production
|
.env.production
|
||||||
.env.development
|
.env.development
|
||||||
.easter-eggs.json
|
.easter-eggs.json
|
||||||
|
/resources/easterEggs
|
@ -11,6 +11,7 @@ import { initWebsocket } from "./websocket";
|
|||||||
import pizzaDayRoutes from "./routes/pizzaDayRoutes";
|
import pizzaDayRoutes from "./routes/pizzaDayRoutes";
|
||||||
import foodRoutes from "./routes/foodRoutes";
|
import foodRoutes from "./routes/foodRoutes";
|
||||||
import votingRoutes from "./routes/votingRoutes";
|
import votingRoutes from "./routes/votingRoutes";
|
||||||
|
import easterEggRoutes from "./routes/easterEggRoutes";
|
||||||
|
|
||||||
const ENVIRONMENT = process.env.NODE_ENV || 'production';
|
const ENVIRONMENT = process.env.NODE_ENV || 'production';
|
||||||
dotenv.config({ path: path.resolve(__dirname, `./.env.${ENVIRONMENT}`) });
|
dotenv.config({ path: path.resolve(__dirname, `./.env.${ENVIRONMENT}`) });
|
||||||
@ -128,6 +129,7 @@ app.get("/api/data", async (req, res) => {
|
|||||||
app.use("/api/pizzaDay", pizzaDayRoutes);
|
app.use("/api/pizzaDay", pizzaDayRoutes);
|
||||||
app.use("/api/food", foodRoutes);
|
app.use("/api/food", foodRoutes);
|
||||||
app.use("/api/voting", votingRoutes);
|
app.use("/api/voting", votingRoutes);
|
||||||
|
app.use("/api/easterEggs", easterEggRoutes);
|
||||||
app.use(express.static('public'))
|
app.use(express.static('public'))
|
||||||
|
|
||||||
// Middleware pro zpracování chyb
|
// Middleware pro zpracování chyb
|
||||||
|
156
server/src/routes/easterEggRoutes.ts
Normal file
156
server/src/routes/easterEggRoutes.ts
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import express, { NextFunction } from "express";
|
||||||
|
import { getLogin, getTrusted } from "../auth";
|
||||||
|
import { parseToken } from "../utils";
|
||||||
|
import path from "path";
|
||||||
|
import fs from "fs";
|
||||||
|
import { EasterEgg } from "../../../types";
|
||||||
|
|
||||||
|
const EASTER_EGGS_JSON_PATH = path.join(__dirname, "../../.easter-eggs.json");
|
||||||
|
const IMAGES_PATH = '../../resources/easterEggs';
|
||||||
|
|
||||||
|
type EasterEggsJson = {
|
||||||
|
[key: string]: EasterEgg[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateUrl() {
|
||||||
|
let result = '';
|
||||||
|
const characters = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
||||||
|
const charactersLength = characters.length;
|
||||||
|
let counter = 0;
|
||||||
|
while (counter < 32) {
|
||||||
|
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||||
|
counter += 1;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vrátí náhodně jeden z definovaných easter egg obrázků pro přihlášeného uživatele.
|
||||||
|
*
|
||||||
|
* @param req request
|
||||||
|
* @param res response
|
||||||
|
* @param next next
|
||||||
|
* @returns náhodný easter egg obrázek, nebo 404 pokud žádný není definován
|
||||||
|
*/
|
||||||
|
function getEasterEggImage(req: any, res: any, next: NextFunction) {
|
||||||
|
const login = getLogin(parseToken(req));
|
||||||
|
const trusted = getTrusted(parseToken(req));
|
||||||
|
try {
|
||||||
|
// TODO vrátit!
|
||||||
|
// if (trusted) {
|
||||||
|
if (true) {
|
||||||
|
if (login in easterEggs) {
|
||||||
|
const imagePath = easterEggs[login][Math.floor(Math.random() * easterEggs[login].length)].path;
|
||||||
|
res.sendFile(path.join(__dirname, IMAGES_PATH, imagePath));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.sendStatus(404);
|
||||||
|
} catch (e: any) { next(e) }
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomPosition(startOffset: number, endOffset: number) {
|
||||||
|
const choice = Math.floor(Math.random() * 4);
|
||||||
|
|
||||||
|
if (choice === 0) {
|
||||||
|
// Vlevo nahoře
|
||||||
|
return {
|
||||||
|
left: `${startOffset}px`,
|
||||||
|
startLeft: `${startOffset}px`,
|
||||||
|
"--start-left": `${startOffset}px`,
|
||||||
|
top: `${startOffset}px`,
|
||||||
|
startTop: `${startOffset}px`,
|
||||||
|
"--start-top": `${startOffset}px`,
|
||||||
|
endLeft: `${endOffset}px`,
|
||||||
|
"--end-left": `${endOffset}px`,
|
||||||
|
endTop: `${endOffset}px`,
|
||||||
|
"--end-top": `${endOffset}px`,
|
||||||
|
rotate: '135deg',
|
||||||
|
}
|
||||||
|
} else if (choice === 1) {
|
||||||
|
// Vpravo nahoře
|
||||||
|
return {
|
||||||
|
right: `${startOffset}px`,
|
||||||
|
startRight: `${startOffset}px`,
|
||||||
|
"--start-right": `${startOffset}px`,
|
||||||
|
top: `${startOffset}px`,
|
||||||
|
startTop: `${startOffset}px`,
|
||||||
|
"--start-top": `${startOffset}px`,
|
||||||
|
endRight: `${endOffset}px`,
|
||||||
|
"--end-right": `${endOffset}px`,
|
||||||
|
endTop: `${endOffset}px`,
|
||||||
|
"--end-top": `${endOffset}px`,
|
||||||
|
rotate: '-135deg',
|
||||||
|
}
|
||||||
|
} else if (choice === 2) {
|
||||||
|
// Vpravo dole
|
||||||
|
return {
|
||||||
|
right: `${startOffset}px`,
|
||||||
|
startRight: `${startOffset}px`,
|
||||||
|
"--start-right": `${startOffset}px`,
|
||||||
|
bottom: `${startOffset}px`,
|
||||||
|
startBottom: `${startOffset}px`,
|
||||||
|
"--start-bottom": `${startOffset}px`,
|
||||||
|
endRight: `${endOffset}px`,
|
||||||
|
"--end-right": `${endOffset}px`,
|
||||||
|
endBottom: `${endOffset}px`,
|
||||||
|
"--end-bottom": `${endOffset}px`,
|
||||||
|
rotate: '-45deg',
|
||||||
|
}
|
||||||
|
} else if (choice === 3) {
|
||||||
|
// Vlevo dole
|
||||||
|
return {
|
||||||
|
left: `${startOffset}px`,
|
||||||
|
startLeft: `${startOffset}px`,
|
||||||
|
"--start-left": `${startOffset}px`,
|
||||||
|
bottom: `${startOffset}px`,
|
||||||
|
startBottom: `${startOffset}px`,
|
||||||
|
"--start-bottom": `${startOffset}px`,
|
||||||
|
endLeft: `${endOffset}px`,
|
||||||
|
"--end-left": `${endOffset}px`,
|
||||||
|
endBottom: `${endOffset}px`,
|
||||||
|
"--end-bottom": `${endOffset}px`,
|
||||||
|
rotate: '45deg',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
let easterEggs: EasterEggsJson;
|
||||||
|
|
||||||
|
// Registrace náhodných URL pro všechny existující easter eggy
|
||||||
|
|
||||||
|
if (fs.existsSync(EASTER_EGGS_JSON_PATH)) {
|
||||||
|
const content = fs.readFileSync(EASTER_EGGS_JSON_PATH, 'utf-8');
|
||||||
|
easterEggs = JSON.parse(content);
|
||||||
|
for (const [key, eggs] of Object.entries(easterEggs)) {
|
||||||
|
for (const easterEgg of eggs) {
|
||||||
|
const url = generateUrl();
|
||||||
|
easterEgg.url = url;
|
||||||
|
router.get(`/${url}`, async (req, res, next) => {
|
||||||
|
return getEasterEggImage(req, res, next);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Získání náhodného easter eggu pro přihlášeného uživatele
|
||||||
|
router.get("/", async (req, res, next) => {
|
||||||
|
const login = getLogin(parseToken(req));
|
||||||
|
const trusted = getTrusted(parseToken(req));
|
||||||
|
try {
|
||||||
|
// TODO vrátit!
|
||||||
|
// if (trusted) {
|
||||||
|
if (true) {
|
||||||
|
if (easterEggs && login in easterEggs) {
|
||||||
|
const randomEasterEgg = easterEggs[login][Math.floor(Math.random() * easterEggs[login].length)];
|
||||||
|
const { path, startOffset, endOffset, ...strippedEasterEgg } = randomEasterEgg; // Path klient k ničemu nepotřebuje a nemá ho znát
|
||||||
|
return res.status(200).json({ ...strippedEasterEgg, ...getRandomPosition(startOffset, endOffset) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res.status(200).send();
|
||||||
|
} catch (e: any) { next(e) }
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router;
|
@ -172,4 +172,43 @@ export enum FeatureRequest {
|
|||||||
SAFETY = "Zvýšená ochrana proti chybám uživatele (potvrzovací dialogy, překliky, ...)",
|
SAFETY = "Zvýšená ochrana proti chybám uživatele (potvrzovací dialogy, překliky, ...)",
|
||||||
UI = "Celkové vylepšení UI/UX",
|
UI = "Celkové vylepšení UI/UX",
|
||||||
DEVELOPMENT = "Zlepšení dokumentace/postupů pro ostatní vývojáře"
|
DEVELOPMENT = "Zlepšení dokumentace/postupů pro ostatní vývojáře"
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface EasterEgg {
|
||||||
|
path: string;
|
||||||
|
url: string;
|
||||||
|
startOffset: number;
|
||||||
|
endOffset: number;
|
||||||
|
duration: number;
|
||||||
|
width?: string;
|
||||||
|
zIndex?: number;
|
||||||
|
position?: "absolute";
|
||||||
|
animationName?: string;
|
||||||
|
animationDuration?: string;
|
||||||
|
animationTimingFunction?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO aktuálně se k ničemu nepoužívá
|
||||||
|
export type AnimationPosition = {
|
||||||
|
left?: string,
|
||||||
|
startLeft?: string,
|
||||||
|
"--start-left"?: string,
|
||||||
|
right?: string,
|
||||||
|
startRight?: string,
|
||||||
|
"--start-right"?: string,
|
||||||
|
top?: string,
|
||||||
|
startTop?: string,
|
||||||
|
"--start-top"?: string,
|
||||||
|
bottom?: string,
|
||||||
|
startBottom?: string,
|
||||||
|
"--start-bottom"?: string,
|
||||||
|
endLeft?: string,
|
||||||
|
"--end-left"?: string,
|
||||||
|
endRight?: string,
|
||||||
|
"--end-right"?: string,
|
||||||
|
endTop?: string,
|
||||||
|
"--end-top"?: string,
|
||||||
|
endBottom?: string,
|
||||||
|
"--end-bottom"?: string,
|
||||||
|
rotate?: string,
|
||||||
}
|
}
|
132
yarn.lock
132
yarn.lock
@ -1876,6 +1876,95 @@
|
|||||||
"@nodelib/fs.scandir" "2.1.5"
|
"@nodelib/fs.scandir" "2.1.5"
|
||||||
fastq "^1.6.0"
|
fastq "^1.6.0"
|
||||||
|
|
||||||
|
"@parcel/watcher-android-arm64@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz#e32d3dda6647791ee930556aee206fcd5ea0fb7a"
|
||||||
|
integrity sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==
|
||||||
|
|
||||||
|
"@parcel/watcher-darwin-arm64@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz#0d9e680b7e9ec1c8f54944f1b945aa8755afb12f"
|
||||||
|
integrity sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==
|
||||||
|
|
||||||
|
"@parcel/watcher-darwin-x64@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz#f9f1d5ce9d5878d344f14ef1856b7a830c59d1bb"
|
||||||
|
integrity sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==
|
||||||
|
|
||||||
|
"@parcel/watcher-freebsd-x64@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz#2b77f0c82d19e84ff4c21de6da7f7d096b1a7e82"
|
||||||
|
integrity sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-arm-glibc@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz#92ed322c56dbafa3d2545dcf2803334aee131e42"
|
||||||
|
integrity sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-arm-musl@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz#cd48e9bfde0cdbbd2ecd9accfc52967e22f849a4"
|
||||||
|
integrity sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-arm64-glibc@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz#7b81f6d5a442bb89fbabaf6c13573e94a46feb03"
|
||||||
|
integrity sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-arm64-musl@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz#dcb8ff01077cdf59a18d9e0a4dff7a0cfe5fd732"
|
||||||
|
integrity sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-x64-glibc@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz#2e254600fda4e32d83942384d1106e1eed84494d"
|
||||||
|
integrity sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==
|
||||||
|
|
||||||
|
"@parcel/watcher-linux-x64-musl@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz#01fcea60fedbb3225af808d3f0a7b11229792eef"
|
||||||
|
integrity sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==
|
||||||
|
|
||||||
|
"@parcel/watcher-win32-arm64@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz#87cdb16e0783e770197e52fb1dc027bb0c847154"
|
||||||
|
integrity sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==
|
||||||
|
|
||||||
|
"@parcel/watcher-win32-ia32@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz#778c39b56da33e045ba21c678c31a9f9d7c6b220"
|
||||||
|
integrity sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==
|
||||||
|
|
||||||
|
"@parcel/watcher-win32-x64@2.5.0":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz#33873876d0bbc588aacce38e90d1d7480ce81cb7"
|
||||||
|
integrity sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==
|
||||||
|
|
||||||
|
"@parcel/watcher@^2.4.1":
|
||||||
|
version "2.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@parcel/watcher/-/watcher-2.5.0.tgz#5c88818b12b8de4307a9d3e6dc3e28eba0dfbd10"
|
||||||
|
integrity sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==
|
||||||
|
dependencies:
|
||||||
|
detect-libc "^1.0.3"
|
||||||
|
is-glob "^4.0.3"
|
||||||
|
micromatch "^4.0.5"
|
||||||
|
node-addon-api "^7.0.0"
|
||||||
|
optionalDependencies:
|
||||||
|
"@parcel/watcher-android-arm64" "2.5.0"
|
||||||
|
"@parcel/watcher-darwin-arm64" "2.5.0"
|
||||||
|
"@parcel/watcher-darwin-x64" "2.5.0"
|
||||||
|
"@parcel/watcher-freebsd-x64" "2.5.0"
|
||||||
|
"@parcel/watcher-linux-arm-glibc" "2.5.0"
|
||||||
|
"@parcel/watcher-linux-arm-musl" "2.5.0"
|
||||||
|
"@parcel/watcher-linux-arm64-glibc" "2.5.0"
|
||||||
|
"@parcel/watcher-linux-arm64-musl" "2.5.0"
|
||||||
|
"@parcel/watcher-linux-x64-glibc" "2.5.0"
|
||||||
|
"@parcel/watcher-linux-x64-musl" "2.5.0"
|
||||||
|
"@parcel/watcher-win32-arm64" "2.5.0"
|
||||||
|
"@parcel/watcher-win32-ia32" "2.5.0"
|
||||||
|
"@parcel/watcher-win32-x64" "2.5.0"
|
||||||
|
|
||||||
"@pkgjs/parseargs@^0.11.0":
|
"@pkgjs/parseargs@^0.11.0":
|
||||||
version "0.11.0"
|
version "0.11.0"
|
||||||
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33"
|
||||||
@ -3645,6 +3734,13 @@ chokidar@^3.4.2, chokidar@^3.5.2, chokidar@^3.5.3:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.2"
|
fsevents "~2.3.2"
|
||||||
|
|
||||||
|
chokidar@^4.0.0:
|
||||||
|
version "4.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.1.tgz#4a6dff66798fb0f72a94f616abbd7e1a19f31d41"
|
||||||
|
integrity sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==
|
||||||
|
dependencies:
|
||||||
|
readdirp "^4.0.1"
|
||||||
|
|
||||||
chrome-trace-event@^1.0.2:
|
chrome-trace-event@^1.0.2:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
|
resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac"
|
||||||
@ -4267,6 +4363,11 @@ destroy@1.2.0:
|
|||||||
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
|
resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
|
||||||
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
|
integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
|
||||||
|
|
||||||
|
detect-libc@^1.0.3:
|
||||||
|
version "1.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b"
|
||||||
|
integrity sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==
|
||||||
|
|
||||||
detect-newline@^3.0.0:
|
detect-newline@^3.0.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651"
|
||||||
@ -5811,6 +5912,11 @@ immer@^9.0.7:
|
|||||||
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
|
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
|
||||||
integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==
|
integrity sha512-bc4NBHqOqSfRW7POMkHd51LvClaeMXpm8dx0e8oE2GORbq5aRK7Bxl4FyzVLdGtLmvLKL7BTDBG5ACQm4HWjTA==
|
||||||
|
|
||||||
|
immutable@^4.0.0:
|
||||||
|
version "4.3.7"
|
||||||
|
resolved "https://registry.yarnpkg.com/immutable/-/immutable-4.3.7.tgz#c70145fc90d89fb02021e65c84eb0226e4e5a381"
|
||||||
|
integrity sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==
|
||||||
|
|
||||||
import-fresh@^3.1.0, import-fresh@^3.2.1:
|
import-fresh@^3.1.0, import-fresh@^3.2.1:
|
||||||
version "3.3.0"
|
version "3.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
|
||||||
@ -7665,6 +7771,11 @@ no-case@^3.0.4:
|
|||||||
lower-case "^2.0.2"
|
lower-case "^2.0.2"
|
||||||
tslib "^2.0.3"
|
tslib "^2.0.3"
|
||||||
|
|
||||||
|
node-addon-api@^7.0.0:
|
||||||
|
version "7.1.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-7.1.1.tgz#1aba6693b0f255258a049d621329329322aad558"
|
||||||
|
integrity sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==
|
||||||
|
|
||||||
node-forge@^1:
|
node-forge@^1:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
|
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
|
||||||
@ -9062,6 +9173,11 @@ readable-stream@^3.0.6:
|
|||||||
string_decoder "^1.1.1"
|
string_decoder "^1.1.1"
|
||||||
util-deprecate "^1.0.1"
|
util-deprecate "^1.0.1"
|
||||||
|
|
||||||
|
readdirp@^4.0.1:
|
||||||
|
version "4.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-4.0.2.tgz#388fccb8b75665da3abffe2d8f8ed59fe74c230a"
|
||||||
|
integrity sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==
|
||||||
|
|
||||||
readdirp@~3.6.0:
|
readdirp@~3.6.0:
|
||||||
version "3.6.0"
|
version "3.6.0"
|
||||||
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
|
||||||
@ -9339,6 +9455,17 @@ sass-loader@^12.3.0:
|
|||||||
klona "^2.0.4"
|
klona "^2.0.4"
|
||||||
neo-async "^2.6.2"
|
neo-async "^2.6.2"
|
||||||
|
|
||||||
|
sass@^1.80.6:
|
||||||
|
version "1.80.6"
|
||||||
|
resolved "https://registry.yarnpkg.com/sass/-/sass-1.80.6.tgz#5d0aa55763984effe41e40019c9571ab73e6851f"
|
||||||
|
integrity sha512-ccZgdHNiBF1NHBsWvacvT5rju3y1d/Eu+8Ex6c21nHp2lZGLBEtuwc415QfiI1PJa1TpCo3iXwwSRjRpn2Ckjg==
|
||||||
|
dependencies:
|
||||||
|
chokidar "^4.0.0"
|
||||||
|
immutable "^4.0.0"
|
||||||
|
source-map-js ">=0.6.2 <2.0.0"
|
||||||
|
optionalDependencies:
|
||||||
|
"@parcel/watcher" "^2.4.1"
|
||||||
|
|
||||||
sax@~1.2.4:
|
sax@~1.2.4:
|
||||||
version "1.2.4"
|
version "1.2.4"
|
||||||
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
|
||||||
@ -9625,6 +9752,11 @@ source-list-map@^2.0.0, source-list-map@^2.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
|
||||||
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
|
||||||
|
|
||||||
|
"source-map-js@>=0.6.2 <2.0.0":
|
||||||
|
version "1.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
|
||||||
|
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
|
||||||
|
|
||||||
source-map-js@^1.0.1, source-map-js@^1.0.2:
|
source-map-js@^1.0.1, source-map-js@^1.0.2:
|
||||||
version "1.0.2"
|
version "1.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user