Add action log viewer

This commit is contained in:
Brian Beck 2024-10-27 13:56:21 -07:00
parent e38b3678cb
commit 175534b788
20 changed files with 215 additions and 28 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[522],{3051:(t,L,r)=>{r.d(L,{qGf:()=>e});var a=r(1810);function e(t){return(0,a.w_)({tag:"svg",attr:{viewBox:"0 0 24 24",fill:"currentColor"},child:[{tag:"path",attr:{d:"M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 10.5858L9.17157 7.75736L7.75736 9.17157L10.5858 12L7.75736 14.8284L9.17157 16.2426L12 13.4142L14.8284 16.2426L16.2426 14.8284L13.4142 12L16.2426 9.17157L14.8284 7.75736L12 10.5858Z"},child:[]}]})(t)}}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
"use strict";(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[950],{2655:(l,a,t)=>{t.d(a,{m2k:()=>c});var r=t(1810);function c(l){return(0,r.w_)({tag:"svg",attr:{viewBox:"0 0 24 24",fill:"currentColor"},child:[{tag:"path",attr:{d:"M12 1.67c.955 0 1.845 .467 2.39 1.247l.105 .16l8.114 13.548a2.914 2.914 0 0 1 -2.307 4.363l-.195 .008h-16.225a2.914 2.914 0 0 1 -2.582 -4.2l.099 -.185l8.11 -13.538a2.914 2.914 0 0 1 2.491 -1.403zm.01 13.33l-.127 .007a1 1 0 0 0 0 1.986l.117 .007l.127 -.007a1 1 0 0 0 0 -1.986l-.117 -.007zm-.01 -7a1 1 0 0 0 -.993 .883l-.007 .117v4l.007 .117a1 1 0 0 0 1.986 0l.007 -.117v-4l-.007 -.117a1 1 0 0 0 -.993 -.883z"},child:[]}]})(l)}}}]);

View file

@ -1 +1 @@
(()=>{"use strict";var e={},t={};function r(o){var n=t[o];if(void 0!==n)return n.exports;var a=t[o]={id:o,loaded:!1,exports:{}},i=!0;try{e[o].call(a.exports,a,a.exports,r),i=!1}finally{i&&delete t[o]}return a.loaded=!0,a.exports}r.m=e,(()=>{var e=[];r.O=(t,o,n,a)=>{if(o){a=a||0;for(var i=e.length;i>0&&e[i-1][2]>a;i--)e[i]=e[i-1];e[i]=[o,n,a];return}for(var l=1/0,i=0;i<e.length;i++){for(var[o,n,a]=e[i],u=!0,d=0;d<o.length;d++)(!1&a||l>=a)&&Object.keys(r.O).every(e=>r.O[e](o[d]))?o.splice(d--,1):(u=!1,a<l&&(l=a));if(u){e.splice(i--,1);var c=n();void 0!==c&&(t=c)}}return t}})(),r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;r.t=function(o,n){if(1&n&&(o=this(o)),8&n||"object"==typeof o&&o&&(4&n&&o.__esModule||16&n&&"function"==typeof o.then))return o;var a=Object.create(null);r.r(a);var i={};e=e||[null,t({}),t([]),t(t)];for(var l=2&n&&o;"object"==typeof l&&!~e.indexOf(l);l=t(l))Object.getOwnPropertyNames(l).forEach(e=>i[e]=()=>o[e]);return i.default=()=>o,r.d(a,i),a}})(),r.d=(e,t)=>{for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((t,o)=>(r.f[o](e,t),t),[])),r.u=e=>{},r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={},t="_N_E:";r.l=(o,n,a,i)=>{if(e[o]){e[o].push(n);return}if(void 0!==a)for(var l,u,d=document.getElementsByTagName("script"),c=0;c<d.length;c++){var s=d[c];if(s.getAttribute("src")==o||s.getAttribute("data-webpack")==t+a){l=s;break}}l||(u=!0,(l=document.createElement("script")).charset="utf-8",l.timeout=120,r.nc&&l.setAttribute("nonce",r.nc),l.setAttribute("data-webpack",t+a),l.src=r.tu(o)),e[o]=[n];var f=(t,r)=>{l.onerror=l.onload=null,clearTimeout(p);var n=e[o];if(delete e[o],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(r)),t)return t(r)},p=setTimeout(f.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=f.bind(null,l.onerror),l.onload=f.bind(null,l.onload),u&&document.head.appendChild(l)}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:e=>e},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("nextjs#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="/vl2-forge/_next/",(()=>{var e={272:0,465:0,113:0};r.f.j=(t,o)=>{var n=r.o(e,t)?e[t]:void 0;if(0!==n){if(n)o.push(n[2]);else if(/^(113|272|465)$/.test(t))e[t]=0;else{var a=new Promise((r,o)=>n=e[t]=[r,o]);o.push(n[2]=a);var i=r.p+r.u(t),l=Error();r.l(i,o=>{if(r.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var a=o&&("load"===o.type?"missing":o.type),i=o&&o.target&&o.target.src;l.message="Loading chunk "+t+" failed.\n("+a+": "+i+")",l.name="ChunkLoadError",l.type=a,l.request=i,n[1](l)}},"chunk-"+t,t)}}},r.O.j=t=>0===e[t];var t=(t,o)=>{var n,a,[i,l,u]=o,d=0;if(i.some(t=>0!==e[t])){for(n in l)r.o(l,n)&&(r.m[n]=l[n]);if(u)var c=u(r)}for(t&&t(o);d<i.length;d++)a=i[d],r.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return r.O(c)},o=self.webpackChunk_N_E=self.webpackChunk_N_E||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})()})(); (()=>{"use strict";var e={},t={};function r(o){var n=t[o];if(void 0!==n)return n.exports;var a=t[o]={id:o,loaded:!1,exports:{}},i=!0;try{e[o].call(a.exports,a,a.exports,r),i=!1}finally{i&&delete t[o]}return a.loaded=!0,a.exports}r.m=e,(()=>{var e=[];r.O=(t,o,n,a)=>{if(o){a=a||0;for(var i=e.length;i>0&&e[i-1][2]>a;i--)e[i]=e[i-1];e[i]=[o,n,a];return}for(var l=1/0,i=0;i<e.length;i++){for(var[o,n,a]=e[i],u=!0,d=0;d<o.length;d++)(!1&a||l>=a)&&Object.keys(r.O).every(e=>r.O[e](o[d]))?o.splice(d--,1):(u=!1,a<l&&(l=a));if(u){e.splice(i--,1);var c=n();void 0!==c&&(t=c)}}return t}})(),r.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return r.d(t,{a:t}),t},(()=>{var e,t=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__;r.t=function(o,n){if(1&n&&(o=this(o)),8&n||"object"==typeof o&&o&&(4&n&&o.__esModule||16&n&&"function"==typeof o.then))return o;var a=Object.create(null);r.r(a);var i={};e=e||[null,t({}),t([]),t(t)];for(var l=2&n&&o;"object"==typeof l&&!~e.indexOf(l);l=t(l))Object.getOwnPropertyNames(l).forEach(e=>i[e]=()=>o[e]);return i.default=()=>o,r.d(a,i),a}})(),r.d=(e,t)=>{for(var o in t)r.o(t,o)&&!r.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:t[o]})},r.f={},r.e=e=>Promise.all(Object.keys(r.f).reduce((t,o)=>(r.f[o](e,t),t),[])),r.u=e=>{},r.miniCssF=e=>{},r.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||Function("return this")()}catch(e){if("object"==typeof window)return window}}(),r.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),(()=>{var e={},t="_N_E:";r.l=(o,n,a,i)=>{if(e[o]){e[o].push(n);return}if(void 0!==a)for(var l,u,d=document.getElementsByTagName("script"),c=0;c<d.length;c++){var s=d[c];if(s.getAttribute("src")==o||s.getAttribute("data-webpack")==t+a){l=s;break}}l||(u=!0,(l=document.createElement("script")).charset="utf-8",l.timeout=120,r.nc&&l.setAttribute("nonce",r.nc),l.setAttribute("data-webpack",t+a),l.src=r.tu(o)),e[o]=[n];var f=(t,r)=>{l.onerror=l.onload=null,clearTimeout(p);var n=e[o];if(delete e[o],l.parentNode&&l.parentNode.removeChild(l),n&&n.forEach(e=>e(r)),t)return t(r)},p=setTimeout(f.bind(null,void 0,{type:"timeout",target:l}),12e4);l.onerror=f.bind(null,l.onerror),l.onload=f.bind(null,l.onload),u&&document.head.appendChild(l)}})(),r.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),(()=>{var e;r.tt=()=>(void 0===e&&(e={createScriptURL:e=>e},"undefined"!=typeof trustedTypes&&trustedTypes.createPolicy&&(e=trustedTypes.createPolicy("nextjs#bundler",e))),e)})(),r.tu=e=>r.tt().createScriptURL(e),r.p="/vl2-forge/_next/",(()=>{var e={272:0,465:0,995:0};r.f.j=(t,o)=>{var n=r.o(e,t)?e[t]:void 0;if(0!==n){if(n)o.push(n[2]);else if(/^(272|465|995)$/.test(t))e[t]=0;else{var a=new Promise((r,o)=>n=e[t]=[r,o]);o.push(n[2]=a);var i=r.p+r.u(t),l=Error();r.l(i,o=>{if(r.o(e,t)&&(0!==(n=e[t])&&(e[t]=void 0),n)){var a=o&&("load"===o.type?"missing":o.type),i=o&&o.target&&o.target.src;l.message="Loading chunk "+t+" failed.\n("+a+": "+i+")",l.name="ChunkLoadError",l.type=a,l.request=i,n[1](l)}},"chunk-"+t,t)}}},r.O.j=t=>0===e[t];var t=(t,o)=>{var n,a,[i,l,u]=o,d=0;if(i.some(t=>0!==e[t])){for(n in l)r.o(l,n)&&(r.m[n]=l[n]);if(u)var c=u(r)}for(t&&t(o);d<i.length;d++)a=i[d],r.o(e,a)&&e[a]&&e[a][0](),e[a]=0;return r.O(c)},o=self.webpackChunk_N_E=self.webpackChunk_N_E||[];o.forEach(t.bind(null,0)),o.push=t.bind(null,o.push.bind(o))})()})();

File diff suppressed because one or more lines are too long

View file

@ -1,7 +1,7 @@
7:"$Sreact.fragment" 7:"$Sreact.fragment"
8:I[9275,[],""] 8:I[9275,[],""]
9:I[1343,[],""] 9:I[1343,[],""]
a:I[7341,["665","static/chunks/f97e080b-d92374f1605c166e.js","51","static/chunks/795d4814-df14aa386162ffad.js","212","static/chunks/59650de3-3851382c06f0a1a6.js","516","static/chunks/f7333993-0b21ce6d3b378608.js","675","static/chunks/b563f954-1a29a90b42165810.js","240","static/chunks/53c13509-7351503a4af8a85a.js","699","static/chunks/8e1d74a4-900578ac2c71efa7.js","515","static/chunks/515-35a068dc133b7a18.js","931","static/chunks/app/page-198e743de2f7340f.js"],"Forge",1] a:I[4538,["950","static/chunks/f8025e75-eec57ce6fbd95417.js","522","static/chunks/94730671-6ced0342e6a25b8f.js","665","static/chunks/f97e080b-d92374f1605c166e.js","51","static/chunks/795d4814-df14aa386162ffad.js","212","static/chunks/59650de3-3851382c06f0a1a6.js","516","static/chunks/f7333993-0b21ce6d3b378608.js","675","static/chunks/b563f954-1a29a90b42165810.js","240","static/chunks/53c13509-7351503a4af8a85a.js","699","static/chunks/8e1d74a4-900578ac2c71efa7.js","515","static/chunks/515-a62e2db5bca20cf3.js","931","static/chunks/app/page-2dac80ee609f3188.js"],"Forge",1]
b:I[3120,[],"OutletBoundary"] b:I[3120,[],"OutletBoundary"]
d:I[3120,[],"MetadataBoundary"] d:I[3120,[],"MetadataBoundary"]
f:I[3120,[],"ViewportBoundary"] f:I[3120,[],"ViewportBoundary"]
@ -11,8 +11,8 @@ f:I[3120,[],"ViewportBoundary"]
3:HL["/vl2-forge/_next/static/media/a521fcc400cf9065-s.p.ttf","font",{"crossOrigin":"","type":"font/ttf"}] 3:HL["/vl2-forge/_next/static/media/a521fcc400cf9065-s.p.ttf","font",{"crossOrigin":"","type":"font/ttf"}]
4:HL["/vl2-forge/_next/static/media/e9ed9197b1d41e00-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}] 4:HL["/vl2-forge/_next/static/media/e9ed9197b1d41e00-s.p.woff2","font",{"crossOrigin":"","type":"font/woff2"}]
5:HL["/vl2-forge/_next/static/css/bdff6c7d2b4adc3b.css","style"] 5:HL["/vl2-forge/_next/static/css/bdff6c7d2b4adc3b.css","style"]
6:HL["/vl2-forge/_next/static/css/d8287fbacbbad8f6.css","style"] 6:HL["/vl2-forge/_next/static/css/8f45e42583e71b0d.css","style"]
0:{"P":null,"b":"9Pi0et4BW69YTW10MWfB6","p":"/vl2-forge","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$7","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/vl2-forge/_next/static/css/bdff6c7d2b4adc3b.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"__variable_6dd175 __variable_60c549 __variable_b97ccf","children":["$","body",null,{"children":["$","$L8",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]]}],{"children":["__PAGE__",["$","$7","c",{"children":[["$","$La",null,{}],[["$","link","0",{"rel":"stylesheet","href":"/vl2-forge/_next/static/css/d8287fbacbbad8f6.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","$Lb",null,{"children":"$Lc"}]]}],{},null]},null],["$","$7","h",{"children":[null,["$","$7","Bbs2r012w6VYSLPXygGDr",{"children":[["$","$Ld",null,{"children":"$Le"}],["$","$Lf",null,{"children":"$L10"}],["$","meta",null,{"name":"next-size-adjust"}]]}]]}]]],"m":"$undefined","G":"$11","s":false,"S":true} 0:{"P":null,"b":"doB4mbbCjK_svkG4sfPx3","p":"/vl2-forge","c":["",""],"i":false,"f":[[["",{"children":["__PAGE__",{}]},"$undefined","$undefined",true],["",["$","$7","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/vl2-forge/_next/static/css/bdff6c7d2b4adc3b.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","html",null,{"lang":"en","className":"__variable_6dd175 __variable_60c549 __variable_b97ccf","children":["$","body",null,{"children":["$","$L8",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","errorScripts":"$undefined","template":["$","$L9",null,{}],"templateStyles":"$undefined","templateScripts":"$undefined","notFound":[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":"404"}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],"notFoundStyles":[]}]}]}]]}],{"children":["__PAGE__",["$","$7","c",{"children":[["$","$La",null,{}],[["$","link","0",{"rel":"stylesheet","href":"/vl2-forge/_next/static/css/8f45e42583e71b0d.css","precedence":"next","crossOrigin":"$undefined","nonce":"$undefined"}]],["$","$Lb",null,{"children":"$Lc"}]]}],{},null]},null],["$","$7","h",{"children":[null,["$","$7","zy8WzxHRJrJcftPOY8oU_",{"children":[["$","$Ld",null,{"children":"$Le"}],["$","$Lf",null,{"children":"$L10"}],["$","meta",null,{"name":"next-size-adjust"}]]}]]}]]],"m":"$undefined","G":"$11","s":false,"S":true}
10:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]] 10:[["$","meta","0",{"name":"viewport","content":"width=device-width, initial-scale=1"}]]
e:[["$","meta","0",{"charSet":"utf-8"}],["$","title","1",{"children":"VL2 Forge"}],["$","meta","2",{"name":"description","content":"Create .vl2 files for Tribes 2"}]] e:[["$","meta","0",{"charSet":"utf-8"}],["$","title","1",{"children":"VL2 Forge"}],["$","meta","2",{"name":"description","content":"Create .vl2 files for Tribes 2"}]]
c:null c:null

View file

@ -1,5 +1,11 @@
"use client"; "use client";
import { useCallback, useMemo, useState, useTransition } from "react"; import {
useCallback,
useEffect,
useMemo,
useState,
useTransition,
} from "react";
import { useDropzone } from "react-dropzone"; import { useDropzone } from "react-dropzone";
import orderBy from "lodash.orderby"; import orderBy from "lodash.orderby";
import { FaGithub } from "react-icons/fa"; import { FaGithub } from "react-icons/fa";
@ -8,39 +14,67 @@ import { FileItem } from "./FileItem";
import { DownloadForm } from "./DownloadForm"; import { DownloadForm } from "./DownloadForm";
import { handleInputFile } from "./utils"; import { handleInputFile } from "./utils";
import styles from "./Forge.module.css"; import styles from "./Forge.module.css";
import { Messages } from "./Messages";
const defaultFileData = {
files: new Map(),
actionLog: [],
};
export function Forge() { export function Forge() {
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const [loadingCount, setLoadingCount] = useState(0); const [loadingCount, setLoadingCount] = useState(0);
const [actionLog, setActionLog] = useState(() => []); const [fileData, setFileData] = useState(defaultFileData);
const [files, setFiles] = useState(() => new Map());
const { files, actionLog } = fileData;
const onDrop = useCallback(async (acceptedFiles) => { const onDrop = useCallback(async (acceptedFiles) => {
setLoadingCount((count) => count + 1); setLoadingCount((count) => count + 1);
const actionLog = []; const actionLog = [];
const finalMap = new Map(); const finalMap = new Map();
const orderedAcceptedFiles = orderBy(
Array.from(acceptedFiles),
[(file) => (file.name ?? "").toLowerCase()],
["asc"]
);
const allFiles: Array<Map<string, any>> = await Promise.all( const allFiles: Array<Map<string, any>> = await Promise.all(
acceptedFiles.map((file) => handleInputFile(file)) orderedAcceptedFiles.map((file) => handleInputFile(file))
); );
allFiles.forEach((map) => { allFiles.forEach((map) => {
map.forEach((file, path) => { map.forEach((file, path) => {
if (finalMap.has(path)) { if (finalMap.has(path)) {
actionLog.push({ actionLog.push({
type: "overwrite", type: "BATCH_OVERWRITE",
path, path,
oldSource: finalMap.get(path).source,
newSource: file.source,
}); });
} }
finalMap.set(path, file); finalMap.set(path, file);
}); });
}); });
startTransition(() => { setFileData((prevData) => {
setFiles((prevMap) => { const { files: prevFiles, actionLog: prevActionLog } = prevData;
return new Map([ const newActionLog = [...prevActionLog, ...actionLog];
...Array.from(prevMap.entries()), const newFiles = new Map(prevFiles);
...Array.from(finalMap.entries()), finalMap.forEach((file, path) => {
]); if (newFiles.has(path)) {
newActionLog.push({
type: "NEW_OVERWRITE",
path,
oldSource: newFiles.get(path).source,
newSource: file.source,
});
}
newFiles.set(path, file);
}); });
setActionLog((prevLog) => [...prevLog, ...actionLog]); return {
files: newFiles,
actionLog:
newActionLog.length === prevActionLog.length
? prevActionLog
: newActionLog,
};
}); });
setLoadingCount((count) => count - 1); setLoadingCount((count) => count - 1);
}, []); }, []);
@ -73,21 +107,27 @@ export function Forge() {
); );
const handleDelete = useCallback((path) => { const handleDelete = useCallback((path) => {
setFiles((files) => { setFileData((prevFileData) => {
const newFiles = new Map(files); const newFiles = new Map(prevFileData.files);
newFiles.delete(path); newFiles.delete(path);
return newFiles; return {
files: newFiles,
actionLog: prevFileData.actionLog,
};
}); });
}, []); }, []);
const handleRename = useCallback((oldPath, newPath) => { const handleRename = useCallback((oldPath, newPath) => {
setFiles((files) => { setFileData((prevFileData) => {
const file = files.get(oldPath); const file = prevFileData.files.get(oldPath);
const newFile = { ...file, path: newPath }; const newFile = { ...file, path: newPath };
const newFiles = new Map(files); const newFiles = new Map(prevFileData.files);
newFiles.delete(oldPath); newFiles.delete(oldPath);
newFiles.set(newPath, newFile); newFiles.set(newPath, newFile);
return newFiles; return {
files: newFiles,
actionLog: prevFileData.actionLog,
};
}); });
}, []); }, []);
@ -144,6 +184,7 @@ export function Forge() {
<FaGithub aria-label="GitHub" /> <FaGithub aria-label="GitHub" />
</a> </a>
<DownloadForm fileList={fileList} /> <DownloadForm fileList={fileList} />
<Messages actionLog={actionLog} />
</footer> </footer>
</section> </section>
); );

69
src/Messages.module.css Normal file
View file

@ -0,0 +1,69 @@
.MessagesButton,
.CloseButton {
background: transparent;
border: 0;
margin: 0;
padding: 0;
display: grid;
place-content: center;
width: 40px;
height: 40px;
font-size: 32px;
color: rgba(255, 255, 255, 0.3);
transition: color 200ms;
cursor: pointer;
}
.MessagesButton.NewMessages {
color: rgba(255, 162, 43, 1);
}
.MessagesButton svg {
pointer-events: none;
}
.CloseButton {
position: absolute;
top: 12px;
right: 12px;
}
.CloseButton:hover {
color: rgba(255, 255, 255, 1);
}
.ActionLog {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
font-size: 12px;
background: rgba(0, 0, 0, 0.9);
color: rgba(255, 162, 43, 1);
padding: 32px;
overflow: auto;
z-index: 2;
}
.ActionLog del {
font-style: normal;
text-decoration: none;
border-bottom: 2px solid red;
}
.ActionLog ins {
font-style: normal;
text-decoration: none;
border-bottom: 2px solid green;
}
.Path {
color: rgb(255, 237, 212);
}
@media (max-width: 767px) {
.MessagesButton {
display: none;
}
}

72
src/Messages.tsx Normal file
View file

@ -0,0 +1,72 @@
import { useEffect, useState } from "react";
import { TbAlertTriangleFilled } from "react-icons/tb";
import { RiCloseCircleFill } from "react-icons/ri";
import styles from "./Messages.module.css";
export function Messages({ actionLog }) {
const [isOpen, setIsOpen] = useState(false);
const [hasNewMessages, setHasNewMessages] = useState(false);
useEffect(() => {
if (!isOpen) {
setHasNewMessages(false);
}
}, [isOpen]);
useEffect(() => {
if (actionLog.length > 0) {
setHasNewMessages(true);
}
}, [actionLog]);
return (
<>
<button
type="button"
className={`${styles.MessagesButton} ${
hasNewMessages ? styles.NewMessages : ""
}`}
onClick={() => {
setIsOpen((isOpen) => !isOpen);
}}
>
<TbAlertTriangleFilled />
</button>
<div className={styles.ActionLog} hidden={!isOpen}>
<button
className={styles.CloseButton}
type="button"
onClick={() => {
setIsOpen(false);
}}
>
<RiCloseCircleFill />
</button>
{actionLog.map((message, i) => {
switch (message.type) {
case "BATCH_OVERWRITE":
return (
<p key={i}>
File at <span className={styles.Path}>{message.path}</span>{" "}
from <del>{message.oldSource}</del> was replaced with the one
from <ins>{message.newSource}</ins> because of the priority in
which it was handled.
</p>
);
case "NEW_OVERWRITE":
return (
<p key={i}>
File at <span className={styles.Path}>{message.path}</span>{" "}
from <del>{message.oldSource}</del> was replaced with the one
from <ins>{message.newSource}</ins> because it is a newer
upload.
</p>
);
default:
return null;
}
})}
</div>
</>
);
}

View file

@ -7,6 +7,7 @@ export type FileType = {
}; };
export type FileEntry = { export type FileEntry = {
source: string | null;
path: string; path: string;
buffer: ArrayBuffer; buffer: ArrayBuffer;
blobUri: string | null; blobUri: string | null;
@ -145,6 +146,7 @@ export async function handleZipFile(file) {
path = detectBestPath(path); path = detectBestPath(path);
const buffer = await fileObj.async("arraybuffer"); const buffer = await fileObj.async("arraybuffer");
const fileEntry = { const fileEntry = {
source: file.name || null,
path, path,
buffer, buffer,
blobUri: null, blobUri: null,
@ -192,6 +194,7 @@ export async function handleOtherFile(file) {
reader.readAsArrayBuffer(file); reader.readAsArrayBuffer(file);
}); });
const fileEntry = { const fileEntry = {
source: "direct upload",
path, path,
buffer, buffer,
blobUri: null, blobUri: null,