✨ Enhance app with multi-CCN support and improved UX
Features: ✅ Add support for multiple CCNs 🔍 Implement sidebar hiding functionality 🎨 Revamp navigation system for better flow 🖌️ Replace icons with custom-designed assets and improved naming 🚀 Streamline initial setup process 📝 Refine terminology (e.g., "forum thread" → "topic") 🛠️ Enhance forum usability and interaction
|
@ -1,5 +1,5 @@
|
|||
import { fileURLToPath, URL } from "node:url";
|
||||
import { defineConfig, externalizeDepsPlugin } from "electron-vite";
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
import { resolve } from "path";
|
||||
|
||||
export default defineConfig({
|
||||
|
@ -40,6 +40,7 @@ export default defineConfig({
|
|||
"@components": fileURLToPath(
|
||||
new URL("./src/components", import.meta.url)
|
||||
),
|
||||
"@assets": fileURLToPath(new URL("./src/assets", import.meta.url)),
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
},
|
||||
},
|
||||
|
|
|
@ -48,6 +48,7 @@
|
|||
"@nostr/tools": "npm:@jsr/nostr__tools",
|
||||
"@open-wc/lit-helpers": "^0.7.0",
|
||||
"@std/encoding": "npm:@jsr/std__encoding",
|
||||
"iconify-icon": "^2.3.0",
|
||||
"lit": "^3.2.1",
|
||||
"markdown-it": "^14.1.0"
|
||||
}
|
||||
|
|
64
src/arx-icons.ts
Normal file
|
@ -0,0 +1,64 @@
|
|||
import arborIcon from '@assets/icons/arbor.svg?raw';
|
||||
import calendarIcon from '@assets/icons/calendar.svg?raw';
|
||||
import howlIcon from '@assets/icons/howl.svg?raw';
|
||||
import lettersIcon from '@assets/icons/letters.svg?raw';
|
||||
import marketIcon from '@assets/icons/market.svg?raw';
|
||||
import oracleIcon from '@assets/icons/oracle.svg?raw';
|
||||
import poolIcon from '@assets/icons/pool.svg?raw';
|
||||
import settingsIcon from '@assets/icons/settings.svg?raw';
|
||||
import walletIcon from '@assets/icons/wallet.svg?raw';
|
||||
import { addIcon } from 'iconify-icon';
|
||||
|
||||
addIcon('arx:letters', {
|
||||
width: 52,
|
||||
height: 46,
|
||||
body: lettersIcon,
|
||||
});
|
||||
|
||||
addIcon('arx:howl', {
|
||||
width: 48,
|
||||
height: 55,
|
||||
body: howlIcon,
|
||||
});
|
||||
|
||||
addIcon('arx:calendar', {
|
||||
width: 51,
|
||||
height: 51,
|
||||
body: calendarIcon,
|
||||
});
|
||||
|
||||
addIcon('arx:arbor', {
|
||||
width: 40,
|
||||
height: 52,
|
||||
body: arborIcon,
|
||||
});
|
||||
|
||||
addIcon('arx:market', {
|
||||
width: 48,
|
||||
height: 44,
|
||||
body: marketIcon,
|
||||
});
|
||||
|
||||
addIcon('arx:wallet', {
|
||||
width: 50,
|
||||
height: 49,
|
||||
body: walletIcon,
|
||||
});
|
||||
|
||||
addIcon('arx:pool', {
|
||||
width: 49,
|
||||
height: 45,
|
||||
body: poolIcon,
|
||||
});
|
||||
|
||||
addIcon('arx:oracle', {
|
||||
width: 49,
|
||||
height: 44,
|
||||
body: oracleIcon,
|
||||
});
|
||||
|
||||
addIcon('arx:settings', {
|
||||
width: 42,
|
||||
height: 44,
|
||||
body: settingsIcon,
|
||||
});
|
15
src/assets/icons/arbor.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="40.039608"
|
||||
height="52.527237"
|
||||
viewBox="0 0 40.039608 52.527237"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-222.26565,-123.96073)"><path
|
||||
fill="currentColor"
|
||||
d="m 240.7398,165.76285 v 7.9375 c -2.12881,0 -4.50865,-0.30249 -6.60948,0.0392 -0.82036,0.13342 -1.55334,0.84386 -1.01096,1.69836 0.68907,1.08562 3.09514,0.64369 4.18086,0.64369 3.97936,0 9.38418,0.96805 13.21365,-0.0841 1.23413,-0.33908 1.23405,-1.87398 0,-2.21304 -2.02512,-0.55641 -4.76467,-0.0841 -6.86365,-0.0841 v -7.9375 c 2.82486,-0.0583 5.74458,-0.39114 8.46667,-1.17144 1.70605,-0.48904 3.31189,-1.25448 4.7625,-2.27958 9.47829,-6.69797 4.6268,-18.00061 -0.15904,-25.91773 -2.96729,-4.90873 -8.2379,-12.43543 -14.65763,-12.43338 -6.07697,0.002 -10.76388,7.13175 -13.67667,11.63963 -5.06755,7.84264 -10.47978,20.15759 -0.61083,26.80988 1.55411,1.04756 3.23299,1.75205 5.02708,2.28469 2.52513,0.74967 5.31349,1.01377 7.9375,1.06793 m 2.91042,-2.64575 c 0,-1.79717 -0.53832,-4.58229 0.61327,-6.08154 1.33511,-1.73818 3.39249,-2.96434 4.93788,-4.51221 0.51867,-0.51951 1.33859,-1.26331 1.01689,-2.08664 -0.93033,-2.38103 -5.56187,2.97579 -6.56804,3.68456 v -10.84792 c 0,-1.22749 0.35101,-3.17567 -0.6439,-4.11349 -1.02349,-0.96473 -2.01369,0.15521 -2.20609,1.20329 -0.45803,2.49515 -0.0604,5.40062 -0.0604,7.93729 -0.83903,-0.58408 -1.61249,-1.25107 -2.38125,-1.92763 -0.75509,-0.66453 -1.62186,-1.90747 -2.6446,-2.1436 -1.15689,-0.26711 -1.66045,1.06455 -1.12264,1.90965 1.42696,2.24231 5.0207,3.3653 5.98885,5.87616 1.12987,2.93031 0.15964,7.95856 0.15964,11.10208 -2.26299,-0.006 -4.6865,-0.25665 -6.87916,-0.83499 -1.48053,-0.39051 -2.92423,-0.99456 -4.23334,-1.79145 -8.95699,-5.45228 -3.2979,-17.00133 0.97769,-23.56731 2.43511,-3.73956 6.61801,-10.29751 11.72231,-10.29751 4.94348,0 9.25377,6.67706 11.64086,10.29751 4.54708,6.89642 9.76862,17.84656 0.79456,23.62734 -1.28569,0.8282 -2.75253,1.42344 -4.23333,1.80021 -2.22805,0.56689 -4.58661,0.76594 -6.87917,0.7662"
|
||||
id="path2" /></g></svg>
|
After Width: | Height: | Size: 2.1 KiB |
15
src/assets/icons/calendar.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="51.278458"
|
||||
height="51.425667"
|
||||
viewBox="0 0 51.278458 51.425667"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-444.0572,-101.62492)"><path
|
||||
id="path122"
|
||||
fill="currentColor"
|
||||
d="m 454.70902,106.8615 c -3.19761,0 -6.56741,-0.47993 -8.94601,2.12727 -2.21765,2.43078 -1.63732,5.9542 -1.63732,8.98523 v 21.16666 c 0,5.06957 -0.99214,13.56232 6.35,13.75446 10.39108,0.27191 20.82578,0.004 31.22083,0.004 3.78326,0 8.99806,1.00047 11.81927,-2.14137 2.29833,-2.55954 1.67448,-6.33069 1.67448,-9.50029 v -21.96055 c 0,-2.95962 0.71315,-6.93903 -1.01955,-9.52479 -2.10019,-3.13417 -5.44385,-2.91062 -8.77003,-2.91062 0,-1.21181 0.17592,-2.61389 -0.53163,-3.67558 -2.72743,-4.092633 -5.81805,0.83525 -5.81837,3.67558 h -18.25625 c -0.0278,-1.33976 -0.16425,-2.88647 -1.13204,-3.9122 -2.81339,-2.981842 -5.83114,0.91466 -4.95338,3.9122 m 3.17502,-2.38122 0.26458,2.38125 h -1.05833 l 0.26458,-2.38125 h 0.52917 m 24.60625,0 0.26458,2.38125 h -1.05833 l 0.26458,-2.38125 h 0.52917 m -27.78125,5.02708 c 0,1.00729 -0.26764,2.50246 0.27193,3.39937 1.71689,2.85393 2.30559,-2.34217 2.10932,-3.39937 h 25.66458 c 0,4.31724 2.64584,4.35972 2.64584,0 1.44201,0 3.10649,-0.22424 4.49791,0.21273 3.45176,1.08403 2.64584,5.45298 2.64584,8.25394 h -45.77294 c 0,-2.65657 -0.80331,-6.63763 2.11768,-8.05061 1.66857,-0.80714 4.01509,-0.41606 5.81982,-0.41606 m 22.75417,11.11247 v 6.61458 l -2.64584,1.16327 -2.98677,-0.0168 0.59163,2.55765 -0.51527,2.64583 c -0.4492,0.0358 -0.88789,0.0677 -1.32925,0.16782 -4.10143,0.93035 1.4149,3.07357 1.87474,4.5957 0.35894,1.18815 -1.41492,2.96805 -0.44608,3.79644 0.85994,0.73527 1.90867,-0.38477 2.811,-0.4085 0.88052,-0.0232 1.80753,0.60298 2.64584,0.84437 -0.11557,1.46421 0.42271,6.7452 -0.68882,7.62555 -1.2819,1.01528 -5.13339,0.31195 -6.71952,0.31195 h -15.61041 c -2.11941,0 -4.86634,0.28968 -6.47372,-1.41294 -1.87881,-1.99013 -1.1992,-5.60953 -1.1992,-8.11206 v -20.37286 h 30.69167 m 4.7625,6.61458 -2.11667,-0.26458 c 0.003,-3.41657 1.72788,-2.62019 2.11667,0.26458 M 461.3236,124.4469 c -6.03811,1.08442 -10.08007,6.48864 -9.1808,12.57709 0.66984,4.53514 6.43336,9.82676 11.29645,8.84436 1.46845,-0.29664 2.16747,-1.62781 1.62691,-3.02352 -1.40184,-3.61944 -4.00982,-5.76914 -3.07661,-10.05417 0.47148,-2.16489 3.50194,-4.6106 3.3616,-6.61336 -0.13229,-1.88806 -2.66114,-1.9758 -4.02755,-1.7304 m 0.52917,2.52293 c -0.58096,1.38147 -1.53574,2.52775 -2.02255,3.96875 -1.53579,4.54613 0.0978,8.22269 2.28713,12.17083 -5.248,-0.24038 -8.91156,-5.92399 -7.01982,-10.84792 1.1151,-2.9024 3.84279,-4.61753 6.75524,-5.29166 m 26.72292,0.79375 c -0.55031,4.94109 -4.94361,0.27062 0,0 m -9.78959,1.67365 c 7.72196,-1.2965 9.3983,10.09438 1.5875,11.1576 -7.07471,0.96304 -8.75576,-9.95407 -1.5875,-11.1576 m 9.26042,6.52843 v -1.85208 c 3.23527,3.7e-4 3.17789,1.78438 0,1.85208 m 0.79375,6.35 c -3.82709,0.93226 -2.89125,-4.41063 -0.46078,-1.06894 0.24535,0.33733 0.32929,0.68221 0.46078,1.06894 m -6.61458,0.26458 c -0.0608,0.45117 -0.12538,0.88688 -0.23806,1.32925 -0.85928,3.37347 -3.44313,-0.55408 0.23806,-1.32925" /></g></svg>
|
After Width: | Height: | Size: 3.2 KiB |
15
src/assets/icons/howl.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="48.373081"
|
||||
height="55.578983"
|
||||
viewBox="0 0 48.373081 55.578983"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-373.7764,-99.170626)"><path
|
||||
id="path12"
|
||||
fill="currentColor"
|
||||
d="m 391.20902,126.44066 c -1.65061,5.81649 -6.57957,9.99022 -7.40833,16.13958 h -0.26458 c -0.70233,-2.574 -1.05834,-4.72503 -1.05834,-7.40833 0,-0.91748 0.30246,-2.56821 -0.42423,-3.27892 -1.98111,-1.93753 -6.02782,3.49441 -6.77444,4.86642 -2.6164,4.80797 -1.82557,11.02536 2.44659,14.69376 5.38053,4.62011 13.77321,3.03332 20.3625,3.03332 1.99816,0 5.59497,0.68633 7.3479,-0.38544 3.2062,-1.96033 -1.00132,-5.98592 -3.11457,-6.49372 1.37462,-3.10568 0.61187,-6.23649 -2.11687,-8.36254 -1.18526,-0.92348 -4.20673,-2.22995 -5.33474,-0.5598 -0.3831,0.56723 -0.24599,1.31264 0.38258,1.61996 2.17754,1.06461 5.34675,1.12394 5.24145,4.54426 -0.0404,1.31071 -1.99403,3.02525 -1.12611,4.2319 0.95602,1.32913 3.15171,1.20121 4.27661,2.75955 h -8.20208 c -2.22213,0 -6.1103,0.75614 -8.14166,-0.15965 -1.68246,-0.7585 -1.15186,-5.21443 -1.11488,-6.71952 0.1643,-6.68824 5.64564,-11.79048 7.48468,-17.97716 0.34158,-1.14911 2.3585,-1.5222 2.04419,-2.89716 -0.22297,-0.97539 -1.33211,-1.05518 -2.12442,-0.88092 -3.64794,0.80236 -6.27697,0.74986 -9.26041,-1.79267 2.5149,-0.6029 5.67696,-2.47893 7.30503,-4.50179 1.11193,-1.38155 1.60271,-2.96203 3.01473,-4.14248 1.92884,-1.61252 4.81542,-1.83106 6.56478,-3.56188 1.23981,-1.22669 2.30228,-2.94073 3.14131,-4.4626 0.34074,-0.61805 0.70504,-1.81659 1.45113,-2.06195 1.74285,-0.57317 2.84227,4.66962 3.0131,5.76612 0.30589,1.9634 -1.3697,7.26806 2.49742,5.72773 0.80851,-0.32204 1.54116,-0.72323 2.38125,-0.96523 -0.30214,3.01128 -2.85342,5.20777 -3.35343,8.20208 -0.59063,3.53693 1.57536,6.1743 1.98172,9.525 0.6255,5.15776 -1.94628,9.08762 0.39585,14.2875 0.60995,1.35414 1.32317,3.2015 2.58806,4.07368 1.57915,1.08889 2.69637,0.73071 3.94405,2.5409 -1.7015,0 -3.94675,0.38139 -5.55605,-0.2364 -2.68845,-1.03206 -4.06019,-5.47157 -4.89091,-7.96568 -0.5215,-1.56574 -0.79029,-6.42303 -2.53171,-6.96859 -2.77029,-0.86787 -0.8155,5.15342 -0.5457,6.17484 1.5426,5.83998 3.90584,11.64166 10.61395,11.64166 1.30735,0 4.73467,0.61552 5.3966,-0.85418 1.84801,-4.10311 -3.05996,-5.32934 -5.04505,-7.1664 -1.15578,-1.06959 -1.74698,-2.94667 -2.19322,-4.41483 -1.17665,-3.87125 0.89951,-7.67037 0.47282,-11.64167 -0.29835,-2.77677 -2.45166,-5.37784 -1.98458,-8.20208 0.60756,-3.67366 5.17031,-7.58113 2.7769,-11.60247 -0.98085,-1.648 -3.00281,-0.0197 -4.18597,0.48997 0,-3.34987 -0.55609,-7.48707 -2.93819,-10.05008 -0.78501,-0.84462 -2.126,-2.105701 -3.41079,-1.837387 -2.15564,0.450182 -3.75492,4.948827 -4.99462,6.595587 -1.81798,2.41493 -4.54008,2.71975 -6.91265,4.44138 -1.8192,1.32009 -2.4261,3.16301 -3.80747,4.79537 -1.40352,1.65855 -3.7454,2.91133 -5.71753,3.76173 -1.02516,0.44206 -2.60665,0.75598 -2.79752,2.08707 -0.16634,1.16002 0.98399,2.17763 1.7402,2.88162 2.46269,2.29261 5.25211,2.67054 8.46565,2.67054 m -11.37708,8.99583 c 0,6.07723 3.14634,10.37028 4.23333,16.13959 -7.70219,-0.67671 -10.2839,-11.67289 -4.23333,-16.13959 m 20.90229,-22.21639 c -2.12454,1.03159 -2.17034,4.79076 0.51753,3.38344 1.75412,-0.91844 1.98561,-4.59887 -0.51753,-3.38344" /></g></svg>
|
After Width: | Height: | Size: 3.3 KiB |
15
src/assets/icons/letters.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="52.115841"
|
||||
height="46.76223"
|
||||
viewBox="0 0 52.115841 46.76223"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-358.17974,-252.02887)"><path
|
||||
fill="currentColor"
|
||||
d="m 397.28739,264.99344 c -1.40599,-10.88693 -10.92028,-12.96458 -20.10834,-12.96458 -4.77787,0 -9.23968,0.12302 -13.22916,3.14847 -5.98978,4.54239 -7.06816,14.53774 -4.37808,21.19319 0.87644,2.16838 3.01313,3.76017 3.79277,5.82083 0.76549,2.02328 -1.67974,4.4397 -0.38891,6.55803 1.98627,3.25961 7.47102,-1.61671 9.44088,-2.72993 1.00571,-0.56836 2.59306,-1.07839 3.48777,-0.0398 1.8852,2.18832 2.50599,4.7496 4.9789,6.65502 5.0549,3.89489 10.2043,1.33597 15.61042,2.95757 2.09565,0.62862 5.95608,4.40808 8.16288,2.80671 1.94012,-1.40786 0.0334,-4.3627 0.8403,-6.1534 1.1982,-2.65904 3.37225,-4.44576 4.18413,-7.40833 2.39052,-8.72298 -2.28592,-19.63397 -12.39356,-19.84375 m -32.01457,21.96041 c 0.21828,-1.62096 1.3393,-3.67673 0.93972,-5.29166 -0.47287,-1.91116 -2.7399,-3.24108 -3.63577,-5.02709 -2.33028,-4.64562 -2.1349,-11.31022 0.25785,-15.87499 3.45112,-6.5839 11.61756,-6.5495 18.04862,-6.10909 3.85358,0.2639 7.3102,0.74311 10.30813,3.51206 5.70409,5.26841 4.98496,17.72041 -1.3123,22.23213 -5.28123,3.78378 -10.67902,1.36889 -16.40416,2.44903 -3.06714,0.57866 -5.22896,3.25149 -8.20209,4.10961 m 37.57082,8.73125 c -2.04656,-0.85206 -4.14391,-2.51759 -6.35,-2.85407 -7.08037,-1.07991 -14.21872,1.18216 -17.72709,-7.20009 3.35658,0 6.64649,-0.11779 9.78959,-1.44581 6.67288,-2.81944 8.99583,-9.86083 8.99583,-16.54585 1.75384,0.362 3.49724,0.80936 5.02708,1.77552 5.25432,3.3183 6.33047,11.09689 3.88607,16.48072 -0.86529,1.90581 -3.1352,3.40125 -3.70274,5.29167 -0.37206,1.23929 0.0813,3.17596 0.0813,4.49791"
|
||||
id="path115" /></g></svg>
|
After Width: | Height: | Size: 1.9 KiB |
15
src/assets/icons/market.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="48.970749"
|
||||
height="44.663296"
|
||||
viewBox="0 0 48.970749 44.663296"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(25.898366,-196.75692)"><path
|
||||
fill="currentColor"
|
||||
d="m -21.117011,197.13272 c -3.45885,0.67086 -3.807992,6.52957 -4.525681,9.40107 -0.902247,3.60992 0.844618,5.38243 1.213694,8.73125 0.58923,5.34638 0.07305,11.3194 -0.106159,16.71387 -0.07531,2.26682 -0.608849,5.71499 0.886026,7.61617 2.102019,2.67332 7.457615,1.59913 10.469619,1.59913 H 7.7225714 c 3.2456176,0 7.1526626,0.59189 10.3187486,-0.14005 4.375573,-1.01153 3.175,-6.53031 3.175,-9.91412 0,-5.16609 -0.609507,-10.75231 0.03512,-15.875 0.201086,-1.59798 1.391367,-2.89721 1.69325,-4.49792 0.558615,-2.96199 -0.854219,-6.16306 -1.562594,-8.99583 -0.299759,-1.19873 -0.470473,-2.69748 -1.334959,-3.64393 -1.975126,-2.16242 -7.549519,-1.11857 -10.2078956,-1.11857 H -10.798262 c -3.301582,0 -7.074319,-0.50534 -10.318749,0.12393 m 11.112499,2.52191 c -2.91e-4,2.63414 -0.290882,5.32196 -0.604703,7.9375 -0.247568,2.06334 -0.542584,3.97835 -2.068896,5.53154 -2.767129,2.81588 -8.186093,2.62792 -10.180536,-1.03363 -1.311467,-2.4077 -0.255178,-5.22629 0.388096,-7.67291 0.295443,-1.12368 0.533001,-3.77403 1.502777,-4.49057 0.728285,-0.53813 2.173364,-0.27193 3.025763,-0.27193 h 7.937499 m 14.2874994,0 c 0.165571,3.85616 1.912711,8.5829 -0.140862,12.17083 -2.656019,4.64049 -9.80335,4.18364 -11.827653,-0.79375 -1.39569,-3.43174 0.316966,-7.79636 0.326849,-11.37708 h 11.641666 m 2.645834,0 H 14.86632 c 0.87204,0 2.285471,-0.25816 3.040056,0.27193 1.044247,0.73359 1.203358,3.07405 1.491341,4.22598 0.605793,2.42317 1.707026,5.26187 0.495686,7.67272 -2.274384,4.52654 -8.827816,4.00979 -11.2503056,2e-4 -0.865515,-1.43257 -0.862148,-2.89417 -1.072827,-4.49792 -0.327119,-2.49012 -0.620658,-5.16136 -0.641449,-7.67291 m -16.404169,13.49372 c 4.260269,5.23092 11.640007,6.34338 15.610416,0 h 0.529166 c 2.837,4.90474 7.4612446,4.7251 12.1708326,3.43959 v 15.875 c 0,1.46606 0.585478,4.59285 -0.6439,5.66118 -1.120709,0.97391 -4.041638,0.42423 -5.441516,0.42423 H -2.0670116 c 0.565368,-3.14441 0.529441,-7.4078 0.112283,-10.58333 -0.138693,-1.05578 0.02283,-2.23507 -0.573263,-3.17091 -1.848815,-2.90259 -11.4023664,-2.98201 -13.4259684,-0.25724 -0.92976,1.25193 -0.665134,3.01952 -0.665134,4.48648 v 9.525 c -1.247905,0 -3.874985,0.49472 -4.830688,-0.46098 -1.048153,-1.04815 -0.460979,-4.24842 -0.460979,-5.62443 v -15.875 c 5.667189,1.54629 8.233166,0.67686 12.4354164,-3.43959 m 14.034551,10.25053 c -1.985647,0.80916 -1.959909,7.20925 -0.873574,8.77066 1.154754,1.65973 6.5150086,1.58136 8.2661896,0.8907 2.147063,-0.84675 2.401343,-7.32205 1.130604,-9.00358 -1.105214,-1.46251 -6.9241566,-1.30942 -8.5232196,-0.65778 m -9.007468,15.14949 h -9.7895824 v -8.99583 l 0.311945,-3.54452 4.4505544,-0.42423 4.602852,0.42423 0.424231,3.27994 v 9.26041 m 15.3458316,-12.96458 0.264584,5.02708 H 5.6059044 v -5.02708 H 10.89757"
|
||||
id="path10" /></g></svg>
|
After Width: | Height: | Size: 3 KiB |
18
src/assets/icons/oracle.svg
Normal file
|
@ -0,0 +1,18 @@
|
|||
<svg
|
||||
width="49.721642"
|
||||
height="44.373489"
|
||||
viewBox="0 0 49.721642 44.373489"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-444.72745,-194.40505)"><path
|
||||
fill="currentColor"
|
||||
d="m 491.75067,225.13027 -3.96875,2.38125 c -0.22685,-2.9761 -5.58574,-3.14716 -7.9375,-3.47408 -6.07972,-0.8452 -12.68821,-0.87866 -18.78542,-0.15454 -1.5143,0.17983 -4.89981,0.0374 -5.92168,1.33045 -0.78418,0.99227 0.10538,1.97416 1.16327,1.99438 2.74686,0.0525 5.99388,-0.50795 8.72716,-0.86683 2.60533,-0.34208 5.31206,-0.15246 7.9375,-0.1523 4.07722,2.6e-4 7.93593,0.76801 11.90625,1.5875 v 0.26458 c -11.98028,2.47148 -26.22182,3.49475 -37.30625,-2.64583 3.44524,-2.92695 8.40903,-3.12235 12.7,-3.62862 1.00808,-0.11895 4.0722,0.25753 3.51573,-1.65161 -0.53347,-1.83022 -4.03519,-0.97906 -5.36781,-0.85011 -3.66057,0.35424 -7.59129,0.78401 -10.84792,2.65891 -1.2606,0.72575 -2.81276,1.8729 -2.83671,3.47143 -0.0943,6.29245 6.15471,10.34595 11.56796,11.73966 8.49806,2.18791 18.26142,2.2538 26.72292,-0.17496 3.97842,-1.14195 7.83997,-2.90127 10.13072,-6.53762 4.91681,-7.80494 -5.29097,-10.50363 -11.18906,-11.06757 -1.47074,-0.14062 -5.8316,-1.16661 -6.43941,0.76312 -0.57251,1.81765 2.24448,1.59158 3.26441,1.72551 4.816,0.63234 8.47059,1.42518 12.96459,3.28728 m -43.12709,3.70416 c 3.73629,2.17255 8.46641,2.76565 12.7,3.2189 10.32641,1.10555 19.73081,-0.12344 29.63334,-2.95431 -2.12089,3.00253 -5.80719,4.50058 -9.26042,5.38966 -7.30844,1.88163 -15.09278,1.92904 -22.48958,0.53407 -2.97728,-0.56149 -6.0578,-1.51909 -8.46667,-3.41489 -0.85772,-0.67502 -2.141,-1.62332 -2.11667,-2.77343"
|
||||
id="path129" /><path
|
||||
fill="currentColor"
|
||||
d="m 467.67358,194.55187 c -5.6648,0.81116 -10.88544,3.57401 -15.34583,7.0862 -1.35571,1.0675 -4.01038,2.80903 -4.14413,4.70675 -0.14477,2.05424 3.00037,4.12525 4.40871,5.23592 4.921,3.8809 11.94882,8.02768 18.52084,7.0862 5.88618,-0.84325 11.30586,-3.66932 15.875,-7.40732 1.37344,-1.1236 3.90056,-2.69592 4.07899,-4.65021 0.19818,-2.17061 -3.12808,-4.19348 -4.60816,-5.32229 -5.07524,-3.87072 -12.15861,-7.68414 -18.78542,-6.73525 m 6.61459,3.32632 c 5.27311,1.71872 10.01662,4.63232 14.02291,8.46666 v 0.26459 c -4.92688,4.53757 -11.53209,9.29021 -18.52083,9.51356 -6.96364,0.22257 -14.11713,-4.85812 -18.78542,-9.51356 v -0.26459 c 3.48114,-3.40932 8.55121,-7.351 13.49375,-8.20208 -2.0146,2.87121 -4.06992,4.94266 -3.65824,8.73125 1.05633,9.72119 15.90908,10.10578 17.10319,0.26458 0.2598,-2.14121 -0.0999,-4.27577 -1.30187,-6.08541 -0.70629,-1.06334 -2.05345,-1.93721 -2.35349,-3.175 m -6.08542,1.93906 c 7.71615,-1.35602 9.98382,10.50121 2.38125,11.95993 -7.57672,1.45375 -9.97408,-10.62561 -2.38125,-11.95993"
|
||||
id="path128" /></g></svg>
|
After Width: | Height: | Size: 2.8 KiB |
15
src/assets/icons/pool.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="49.911552"
|
||||
height="45.77253"
|
||||
viewBox="0 0 49.911552 45.77253"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-485.34034,-171.20574)"><path
|
||||
id="path159"
|
||||
fill="currentColor"
|
||||
d="m 510.05511,178.81945 c -1.57359,-3.32099 -3.43658,-6.0272 -7.14375,-7.13251 -1.11516,-0.33251 -4.92197,-1.29014 -4.60856,1.01954 0.1702,1.25432 2.09877,1.18853 3.02106,1.41376 1.96804,0.48062 3.66152,1.67255 4.77148,3.37629 3.0845,4.73458 2.63686,12.30605 2.63686,17.72708 0,1.4274 -0.73794,6.02034 0.61327,6.86774 2.51315,1.5761 2.03256,-3.70262 2.03256,-4.75107 0,-5.95317 -0.8977,-14.27484 2.31796,-19.57917 1.72918,-2.85234 3.96526,-2.76914 6.6491,-3.94036 0.71881,-0.3137 1.17005,-1.06666 0.76352,-1.82023 -0.71508,-1.32557 -2.79882,-0.65876 -3.90975,-0.34504 -3.71421,1.04893 -5.66234,3.83846 -7.14375,7.16397 m 17.21507,-3.92221 c -0.99661,0.13061 -1.18065,1.65229 -1.71837,2.30693 -0.83745,1.01958 -4.12299,2.14557 -1.96375,3.71337 0.66871,0.48554 1.41572,0.70732 1.96375,1.37007 0.50444,0.61004 0.71417,2.44829 1.70776,2.41841 1.04042,-0.0313 1.24734,-1.76087 1.75346,-2.41921 0.8246,-1.07264 4.69432,-1.98951 2.17976,-3.59597 -0.62197,-0.39736 -1.24973,-0.6658 -1.76287,-1.22289 -0.61726,-0.67012 -0.99006,-2.724 -2.15974,-2.57071 m -33.2487,6.61458 -1.42744,2.57152 -2.63155,1.7247 -0.83027,1.16551 2.39063,1.55667 1.93599,2.94595 1.1447,0.48589 1.38049,-2.29061 2.4137,-1.86659 0.83028,-1.04872 -2.61009,-1.99868 -1.4593,-2.72259 -1.13714,-0.52305 m 8.09613,13.29488 c -6.3301,0.83154 -18.56886,1.09803 -16.55646,10.20647 0.26528,1.20067 0.59588,2.33664 1.14734,3.43958 0.72856,1.45711 1.66268,2.77971 2.97473,3.76745 2.77796,2.09133 6.16528,3.04505 9.52398,3.69295 8.31578,1.6041 18.6872,1.56689 26.72291,-1.29353 4.81129,-1.71265 11.53362,-8.26771 8.61039,-14.10437 -2.3416,-4.67538 -11.23232,-5.15563 -15.75414,-5.57605 -1.28286,-0.11929 -4.09721,-0.87491 -5.131,0.17945 -1.84779,1.88454 2.08062,2.30005 3.01434,2.41064 4.05841,0.48069 8.3526,0.63299 12.17083,2.26837 0.96704,0.41418 3.4983,1.39203 2.93125,2.83038 -0.98846,2.50724 -6.19528,3.40554 -8.4875,3.86688 -8.51983,1.71469 -17.67578,1.70394 -26.19375,0 -2.49523,-0.49916 -5.26496,-1.13312 -7.40812,-2.56786 -0.85323,-0.57119 -2.04453,-1.48615 -1.22351,-2.56906 2.26966,-2.99359 9.42041,-3.47696 12.86496,-3.72969 1.24799,-0.0916 3.62291,0.14476 4.6784,-0.55999 0.70002,-0.4674 0.82631,-1.80742 0,-2.21426 -0.98988,-0.48737 -2.82848,-0.1861 -3.88465,-0.0474 m -12.7,12.05853 c 4.04223,2.15276 8.98457,2.83523 13.49375,3.19767 9.99933,0.80373 18.7626,-0.4682 28.31042,-3.19767 -1.59402,3.62953 -5.12554,5.02355 -8.73125,5.91963 -7.23833,1.79889 -15.43116,1.70377 -22.75417,0.4955 -2.77592,-0.45802 -10.89255,-2.45631 -10.31875,-6.41513 m 29.63334,-4.49788 -9.26042,0.786 -8.73125,-0.786 c -0.002,-0.60492 0.16226,-1.68559 -0.42423,-2.08563 -1.15689,-0.78913 -4.90382,0.46519 -4.74412,2.08563 0.21736,2.20568 4.57866,2.52966 6.22668,2.79447 5.28009,0.84841 10.87014,0.90351 16.13959,-0.0466 1.6202,-0.29211 4.2504,-0.44755 5.40374,-1.76307 1.8744,-2.13796 -4.92551,-5.6829 -4.60999,-0.98484" /></g></svg>
|
After Width: | Height: | Size: 3.2 KiB |
15
src/assets/icons/settings.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="42.581379"
|
||||
height="44.50452"
|
||||
viewBox="0 0 42.581379 44.50452"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-513.56562,-191.36311)"><path
|
||||
fill="currentColor"
|
||||
d="m 532.49652,191.64146 c -4.39731,0.63872 -2.56173,4.78288 -5.56034,6.77403 -2.95736,1.96376 -6.07138,-1.80106 -8.96704,0.62532 -2.11951,1.77602 -5.29879,6.30373 -3.8726,9.1561 0.92264,1.84528 3.27214,2.7776 3.30975,5.02708 0.0453,2.71082 -2.46408,3.38655 -3.49184,5.55625 -1.33779,2.82418 1.47505,6.68912 3.32321,8.70347 2.92261,3.18543 6.27113,-0.65416 9.43394,1.28067 2.11104,1.29141 1.45151,3.5743 2.78568,5.35006 1.6224,2.1594 5.66911,1.84637 8.06632,1.52236 4.52395,-0.61143 2.75201,-5.10792 5.82492,-6.97348 2.81455,-1.70871 5.88816,1.56386 8.71655,-0.72699 2.33704,-1.89287 5.23956,-6.44838 3.60434,-9.41966 -1.04248,-1.89421 -3.32097,-2.59314 -3.32097,-5.0281 0,-2.42603 2.32857,-3.3277 3.31055,-5.29166 1.41947,-2.83893 -1.32225,-7.02785 -3.36753,-8.90581 -2.89674,-2.65977 -5.74302,1.04072 -8.42807,-0.5608 -2.4985,-1.49027 -1.75431,-3.83447 -3.33649,-5.85045 -1.56372,-1.99246 -5.79257,-1.56344 -8.03038,-1.23839 m 0.53323,2.62358 c 1.26588,-0.26796 4.10261,-0.47757 5.15264,0.40055 1.01087,0.84538 0.88937,2.61199 1.406,3.74072 0.64467,1.40842 2.18735,2.31554 3.49144,3.05209 2.55735,1.44442 5.74141,-1.99739 7.73641,0.18435 1.00156,1.0953 2.8298,3.382 2.49333,4.96666 -0.3292,1.55039 -2.09313,2.41535 -2.88959,3.70416 -0.79737,1.29027 -0.6337,3.82596 -0.42281,5.29167 0.31074,2.15969 3.53793,2.95804 3.51553,5.03096 -0.0161,1.49228 -1.81055,3.95999 -2.84611,4.96174 -1.57495,1.52354 -3.45152,-0.16397 -5.20551,-0.37094 -1.48638,-0.17537 -3.04285,0.75743 -4.22925,1.55525 -2.05375,1.3811 -1.64232,5.78247 -3.97773,6.22506 -1.3731,0.26022 -4.37865,0.46774 -5.43683,-0.58348 -0.90976,-0.90377 -0.77784,-2.47932 -1.32332,-3.58923 -0.66636,-1.35586 -2.00913,-2.19111 -3.28912,-2.89999 -2.63062,-1.45688 -6.41482,1.51188 -8.26558,-0.57898 -0.9788,-1.1058 -3.26811,-3.92646 -2.52354,-5.49338 0.98865,-2.08056 3.48183,-2.68094 3.63394,-5.31534 0.087,-1.50666 0.15038,-3.19558 -0.74374,-4.4875 -0.81381,-1.17589 -2.58529,-1.9438 -2.85039,-3.45 -0.17283,-0.98192 0.54289,-1.88054 1.04813,-2.64584 0.65538,-0.99273 1.36908,-2.54787 2.56804,-2.96798 1.77899,-0.62334 3.70635,1.24635 5.54564,1.04874 1.23405,-0.1326 2.45479,-1.05871 3.43857,-1.75879 2.01717,-1.4355 1.62963,-5.52428 3.97385,-6.0205 m 1.58341,11.17888 c -11.03424,0.44841 -9.60319,16.76802 1.32292,16.23614 10.25302,-0.4991 8.86751,-16.65024 -1.32292,-16.23614 m -0.79375,2.84019 c 6.86754,-1.22632 9.37097,9.42319 2.38125,10.67357 -6.97499,1.24773 -9.38056,-9.42375 -2.38125,-10.67357"
|
||||
id="path126" /></g></svg>
|
After Width: | Height: | Size: 2.8 KiB |
15
src/assets/icons/wallet.svg
Normal file
|
@ -0,0 +1,15 @@
|
|||
<svg
|
||||
width="50.380646"
|
||||
height="49.466183"
|
||||
viewBox="0 0 50.380646 49.466183"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
||||
id="defs1" /><g
|
||||
id="layer1"
|
||||
transform="translate(-376.19943,-188.11239)"><path
|
||||
fill="currentColor"
|
||||
d="m 422.95902,228.57028 c 4.14241,-0.83637 4.12926,-10.03343 2.91185,-13.22916 -0.52062,-1.36665 -1.50643,-2.31078 -2.91185,-2.64584 0,-3.09737 0.0819,-6.54769 -3.43958,-7.67291 4.46137,-10.3694 -7.14017,-20.11134 -16.93333,-15.90869 -2.23451,0.95892 -4.08497,2.48283 -5.4113,4.5316 -0.72822,1.12489 -1.11596,3.49858 -2.5407,3.88465 -5.05834,1.37065 -15.21975,-2.97233 -17.72442,3.52369 -1.14751,2.97611 -0.51733,6.9166 -0.51733,10.05416 0,6.47222 -0.44959,13.12572 0.0239,19.57917 0.18713,2.55057 1.61147,4.91294 3.94487,6.04866 2.57935,1.25545 6.45127,0.56592 9.26041,0.56592 h 20.90209 c 2.71375,0 7.40537,0.89471 9.78938,-0.53162 2.99788,-1.79356 2.64603,-5.18964 2.64603,-8.19963 M 411.58196,204.4932 c 0.45959,-1.73781 0.54573,-2.70426 -0.52917,-4.23333 v -0.26459 c 1.0056,-1.8109 0.19175,-3.05081 -1.32291,-4.23333 -0.0447,-1.55397 -1.20998,-1.28905 -1.5875,0 h -0.79375 c -0.24581,-1.53679 -1.34169,-1.53679 -1.5875,0 -2.55964,0.52269 -1.11284,6.36621 -2.11667,8.73125 -1.27255,0 -3.81969,0.48852 -4.89132,-0.27193 -1.19179,-0.84572 -0.99866,-3.49263 -0.90563,-4.75515 0.37831,-5.13388 5.07913,-8.76536 10.03028,-8.73003 4.99877,0.0357 9.54681,3.86879 9.77917,8.99461 0.0553,1.22021 0.22947,3.67552 -0.9191,4.49057 -1.11502,0.79125 -3.82869,0.27193 -5.1559,0.27193 m -5.29171,-4.76293 v -1.85208 c 3.82488,0 3.78187,1.85166 0,1.85208 m -11.1125,0.52917 0.52917,4.23333 c -4.23316,0 -8.46689,0.0205 -12.7,-1.9e-4 -1.39253,-0.007 -3.58331,-0.22971 -3.58331,-2.11648 0,-1.93423 2.16781,-2.11431 3.58331,-2.11666 h 12.17083 m 11.1125,3.175 v -1.85209 c 4.29192,0 4.29192,1.85209 0,1.85209 m 14.02292,10.84791 c -3.5483,0 -7.75573,-0.72841 -10.8469,1.35069 -4.60095,3.09455 -4.4004,10.37749 0.79273,12.79023 2.90295,1.3487 6.93221,0.67575 10.05417,0.67575 0,1.914 0.46958,4.38936 -1.59159,5.40477 -2.31284,1.13937 -6.19823,0.41606 -8.72716,0.41606 h -19.84375 c -2.76357,0 -6.89675,0.77783 -9.24898,-0.96544 -2.41027,-1.7863 -1.86352,-5.14562 -1.86352,-7.76581 v -19.84375 c 3.92504,1.04929 7.85467,0.79375 11.90625,0.79375 h 17.19792 7.67291 c 1.07294,0 2.46288,-0.21416 3.38303,0.46099 1.78508,1.30974 1.11489,4.76059 1.11489,6.68276 m 3.175,2.64584 c 0,2.24401 0.75836,6.18376 -0.31195,8.19045 -0.75367,1.41303 -2.2735,1.33401 -3.6568,1.33455 -2.99197,0.001 -7.22064,0.82449 -9.46845,-1.61241 -2.3274,-2.5232 -0.8511,-6.74842 2.3247,-7.66414 1.19515,-0.34462 2.47492,-0.24845 3.70417,-0.24845 h 7.40833 m -10.57925,2.80793 c -2.62779,0.64098 -1.68539,4.89676 1.05302,4.25048 2.77349,-0.65454 1.67838,-4.91675 -1.05302,-4.25048"
|
||||
id="path125" /></g></svg>
|
After Width: | Height: | Size: 2.8 KiB |
|
@ -60,12 +60,12 @@ export class ArborForumCategory extends LitElement {
|
|||
<div class="category-header">
|
||||
<span>${this.title}</span>
|
||||
<arx-button
|
||||
label="New Topic"
|
||||
href="/arbor/new-topic/${this.id}"
|
||||
label="New Thread"
|
||||
href="/arbor/new-thread/${this.id}"
|
||||
></arx-button>
|
||||
</div>
|
||||
<slot>
|
||||
<div class="empty-state">No topics yet...</div>
|
||||
<div class="empty-state">No threads yet...</div>
|
||||
</slot>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -12,7 +12,7 @@ import '@components/MarkdownContent';
|
|||
@customElement('arx-forum-post')
|
||||
export class ForumPost extends LitElement {
|
||||
@property({ type: String }) override id = '';
|
||||
@property({ type: String }) topicId = '';
|
||||
@property({ type: String }) threadId = '';
|
||||
@property({ type: String }) npub = '';
|
||||
@property({ type: Date }) date = new Date();
|
||||
@property({ type: String }) content = '';
|
||||
|
@ -217,12 +217,12 @@ export class ForumPost extends LitElement {
|
|||
}
|
||||
|
||||
private _copyPermalink() {
|
||||
const permalink = `eve://phora/topics/${this.topicId}#post-${this.id}`;
|
||||
const permalink = `eve://phora/threads/${this.threadId}#post-${this.id}`;
|
||||
navigator.clipboard.writeText(permalink);
|
||||
}
|
||||
|
||||
override render() {
|
||||
const permalink = `eve://phora/topics/${this.topicId}#post-${this.id}`;
|
||||
const permalink = `eve://phora/threads/${this.threadId}#post-${this.id}`;
|
||||
|
||||
const postClasses = {
|
||||
post: true,
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import { LitElement, css, html } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
import '@components/EveLink';
|
||||
import formatDateTime from '@/utils/formatDateTime';
|
||||
import '@components/EveLink';
|
||||
|
||||
@customElement('arx-arbor-forum-topic')
|
||||
export class ArborForumTopic extends LitElement {
|
||||
@customElement('arx-arbor-forum-thread')
|
||||
export class ArborForumThread extends LitElement {
|
||||
static override styles = [
|
||||
css`
|
||||
.topic {
|
||||
.thread {
|
||||
display: grid;
|
||||
grid-template-columns: 3fr 1fr;
|
||||
padding: 1.75rem;
|
||||
|
@ -25,7 +25,7 @@ export class ArborForumTopic extends LitElement {
|
|||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
}
|
||||
|
||||
.topic:hover {
|
||||
.thread:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: calc(var(--depth) * 4px) calc(var(--depth) * 4px)
|
||||
calc(var(--depth) * 8px)
|
||||
|
@ -35,7 +35,7 @@ export class ArborForumTopic extends LitElement {
|
|||
oklch(from var(--color-base-100) l c h / 0.5);
|
||||
}
|
||||
|
||||
.topic-icon {
|
||||
.thread-icon {
|
||||
inline-size: 40px;
|
||||
block-size: 40px;
|
||||
background: var(--color-accent);
|
||||
|
@ -48,7 +48,7 @@ export class ArborForumTopic extends LitElement {
|
|||
oklch(from var(--color-base-content) l c h / 0.15);
|
||||
}
|
||||
|
||||
.topic-icon::after {
|
||||
.thread-icon::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
@ -59,13 +59,13 @@ export class ArborForumTopic extends LitElement {
|
|||
);
|
||||
}
|
||||
|
||||
.topic-info {
|
||||
.thread-info {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.topic-details {
|
||||
.thread-details {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
|
@ -85,7 +85,7 @@ export class ArborForumTopic extends LitElement {
|
|||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
.topic-details p {
|
||||
.thread-details p {
|
||||
margin: 0;
|
||||
font-size: 0.975rem;
|
||||
line-height: 1.6;
|
||||
|
@ -156,7 +156,7 @@ export class ArborForumTopic extends LitElement {
|
|||
}
|
||||
|
||||
@media (max-width: 968px) {
|
||||
.topic {
|
||||
.thread {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 1.5rem;
|
||||
padding: 1.25rem;
|
||||
|
@ -188,13 +188,13 @@ export class ArborForumTopic extends LitElement {
|
|||
|
||||
override render() {
|
||||
return html`
|
||||
<div class="topic">
|
||||
<div class="topic-info">
|
||||
<div class="topic-icon"></div>
|
||||
<div class="topic-details">
|
||||
<div class="thread">
|
||||
<div class="thread-info">
|
||||
<div class="thread-icon"></div>
|
||||
<div class="thread-details">
|
||||
<arx-eve-link
|
||||
class="${this.status}"
|
||||
href="/arbor/topics/${this.id}"
|
||||
href="/arbor/threads/${this.id}"
|
||||
>
|
||||
${this.title}
|
||||
</arx-eve-link>
|
|
@ -116,9 +116,12 @@ export class EveDialog extends LitElement {
|
|||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
document.addEventListener('keydown', this._handleKeyDown);
|
||||
this.rootWindow = document.body
|
||||
.querySelector('arx-eve-router')!
|
||||
.shadowRoot!.querySelector('.window') as HTMLDivElement;
|
||||
const router = document.querySelector('arx-eve-router');
|
||||
if (router) {
|
||||
this.rootWindow = router.shadowRoot!.querySelector('.window') as HTMLDivElement;
|
||||
} else {
|
||||
this.rootWindow = document.body as HTMLDivElement;
|
||||
}
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
|
@ -168,7 +171,7 @@ export class EveDialog extends LitElement {
|
|||
override render() {
|
||||
return html`
|
||||
<div
|
||||
class="${classMap({ active: this.open })}"
|
||||
class=${classMap({ active: this.open })}
|
||||
@click=${this._handleOverlayClick}
|
||||
style="--dialog-width: ${this.width}; --dialog-max-width: ${this.maxWidth};"
|
||||
>
|
||||
|
|
|
@ -11,7 +11,7 @@ export class StyledSelect<T> extends LitElement {
|
|||
@property() name = '';
|
||||
@property({ type: Boolean }) required = false;
|
||||
@property() label = '';
|
||||
@property({ type: Array<T> }) options = [];
|
||||
@property({ type: Array }) options: T[] = [];
|
||||
@property() valueMapper?: (option: T) => string;
|
||||
@property() textMapper?: (option: T) => string;
|
||||
|
||||
|
@ -106,15 +106,17 @@ export class StyledSelect<T> extends LitElement {
|
|||
private _getOptionValue(option: T) {
|
||||
if (option === undefined || option === null) return '';
|
||||
if (this.valueMapper) return this.valueMapper(option);
|
||||
if (typeof option === 'object' && 'value' in option) return option.value;
|
||||
return option;
|
||||
if (typeof option === 'object' && 'value' in (option as object))
|
||||
return (option as unknown as { value: string }).value;
|
||||
return String(option);
|
||||
}
|
||||
|
||||
private _getOptionText(option: T) {
|
||||
if (option === undefined || option === null) return '';
|
||||
if (this.textMapper) return this.textMapper(option);
|
||||
if (typeof option === 'object' && 'label' in option) return option.label;
|
||||
return option;
|
||||
if (typeof option === 'object' && 'label' in (option as object))
|
||||
return (option as unknown as { label: string }).label;
|
||||
return String(option);
|
||||
}
|
||||
|
||||
override render() {
|
||||
|
@ -144,7 +146,7 @@ export class StyledSelect<T> extends LitElement {
|
|||
const optionText = this._getOptionText(option);
|
||||
return html`
|
||||
<option
|
||||
value=${optionValue}
|
||||
.value=${optionValue}
|
||||
?selected=${optionValue === this.value}
|
||||
>
|
||||
${optionText}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { LitElement, css, html } from 'lit';
|
||||
import { customElement, property } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
import { ArxInputChangeEvent } from './Input';
|
||||
|
||||
|
@ -134,7 +135,7 @@ export class StyledTextarea extends LitElement {
|
|||
|
||||
return html`
|
||||
${when(this.label, () => html`<label for="textarea-${this.name}">${this.label}</label>`)}
|
||||
<div class="${hasMaxlength ? 'has-maxlength' : ''}">
|
||||
<div class=${classMap({ 'has-maxlength': hasMaxlength })}>
|
||||
<textarea
|
||||
id="textarea-${this.name}"
|
||||
.value=${this.value}
|
||||
|
@ -142,7 +143,7 @@ export class StyledTextarea extends LitElement {
|
|||
?required=${this.required}
|
||||
placeholder=${this.placeholder}
|
||||
rows=${this.rows}
|
||||
maxlength=${this.maxlength}
|
||||
.maxlength=${Number.parseInt(this.maxlength) || -1}
|
||||
name=${this.name}
|
||||
@input=${this._handleInput}
|
||||
@focus=${this._handleFocus}
|
||||
|
|
|
@ -172,7 +172,7 @@ export class Header extends LitElement {
|
|||
</div>
|
||||
<div class="nav-buttons">
|
||||
<button @click=${this._goToWallet}>
|
||||
<iconify-icon icon="material-symbols:wallet"></iconify-icon>
|
||||
<iconify-icon icon="arx:wallet"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
@ -290,11 +290,11 @@ export class Header extends LitElement {
|
|||
window.location.hash = 'arbor';
|
||||
break;
|
||||
case 892:
|
||||
window.location.hash = `arbor/topics/${suggestion.id}`;
|
||||
window.location.hash = `arbor/threads/${suggestion.id}`;
|
||||
break;
|
||||
case 893: {
|
||||
const threadId = suggestion.tags.find((tag) => tag[0] === 'E')?.[1]!;
|
||||
window.location.hash = `arbor/topics/${threadId}`;
|
||||
window.location.hash = `arbor/threads/${threadId}`;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
import { ndk, setSigner } from '@/ndk';
|
||||
import type { ArxInputChangeEvent } from '@components/General/Input';
|
||||
import { animate } from '@lit-labs/motion';
|
||||
import { randomBytes } from '@noble/ciphers/webcrypto';
|
||||
import { NDKEvent, NDKKind, NDKPrivateKeySigner } from '@nostr-dev-kit/ndk';
|
||||
import * as nip06 from '@nostr/tools/nip06';
|
||||
import * as nip19 from '@nostr/tools/nip19';
|
||||
import * as nip49 from '@nostr/tools/nip49';
|
||||
import * as nostrTools from '@nostr/tools/pure';
|
||||
import { encodeBase64 } from '@std/encoding/base64';
|
||||
|
@ -12,18 +9,23 @@ import { LitElement, css, html } from 'lit';
|
|||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
||||
import { ndk, setSigner } from '@/ndk';
|
||||
import '@components/General/Button';
|
||||
import '@components/General/Fieldset';
|
||||
import '@components/General/Input';
|
||||
import '@components/LoadingView';
|
||||
import '@components/ProgressSteps';
|
||||
import '@components/RelayLogs';
|
||||
import '@components/Setup/CCNList';
|
||||
import { NDKEvent, NDKKind, NDKPrivateKeySigner, NDKRelay, NDKRelaySet } from '@nostr-dev-kit/ndk';
|
||||
import { nip19 } from '@nostr/tools';
|
||||
|
||||
@customElement('arx-initial-setup')
|
||||
export class InitialSetup extends LitElement {
|
||||
@state() private currentPage = 1;
|
||||
@state() private isAnimating = false;
|
||||
@state() private seedPhrase = '';
|
||||
@state() private communityName = '';
|
||||
@state() private userName = '';
|
||||
@state() private profileImage = '';
|
||||
@state() private lightningAddress = '';
|
||||
|
@ -32,8 +34,10 @@ export class InitialSetup extends LitElement {
|
|||
pid: null,
|
||||
logs: [],
|
||||
};
|
||||
@state() private selectedCCN: string | undefined;
|
||||
@state() private ccnList: { name: string; pubkey: string }[] = [];
|
||||
|
||||
private readonly pageLabels = ['Welcome', 'Seed Phrase', 'Relay Setup', 'Profile', 'Complete'];
|
||||
private readonly pageLabels = ['Welcome', 'Relay Setup', 'Seed Phrase', 'Profile', 'Complete'];
|
||||
|
||||
get encryptionPassphrase() {
|
||||
let encryptionPassphrase = localStorage.getItem('encryption_key');
|
||||
|
@ -212,10 +216,30 @@ export class InitialSetup extends LitElement {
|
|||
}, 300);
|
||||
}
|
||||
|
||||
private nextStep() {
|
||||
if (this.currentPage === 1) return this.handleNavigation(2);
|
||||
if (this.currentPage === 2) {
|
||||
if (this.selectedCCN) return this.handleNavigation(4);
|
||||
return this.handleNavigation(3);
|
||||
}
|
||||
if (this.currentPage === 3) return this.handleNavigation(4);
|
||||
if (this.currentPage === 4) return this.goToFinalStep();
|
||||
}
|
||||
|
||||
private previousStep() {
|
||||
if (this.currentPage === 2) return this.handleNavigation(1);
|
||||
if (this.currentPage === 3) return this.handleNavigation(2);
|
||||
if (this.currentPage === 4) return this.handleNavigation(3);
|
||||
}
|
||||
|
||||
private onSeedPhraseInput(event: ArxInputChangeEvent) {
|
||||
this.seedPhrase = event.detail.value;
|
||||
}
|
||||
|
||||
private onCommunityNameInput(event: ArxInputChangeEvent) {
|
||||
this.communityName = event.detail.value;
|
||||
}
|
||||
|
||||
private generateSeedPhrase() {
|
||||
this.seedPhrase = nip06.generateSeedWords();
|
||||
}
|
||||
|
@ -227,7 +251,12 @@ export class InitialSetup extends LitElement {
|
|||
return true;
|
||||
}
|
||||
|
||||
private renderPageOne() {
|
||||
private isValidCommunityName() {
|
||||
const trimmedName = this.communityName.trim();
|
||||
return trimmedName.length > 0;
|
||||
}
|
||||
|
||||
private renderWelcomePage() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<arx-progress-steps
|
||||
|
@ -253,35 +282,139 @@ export class InitialSetup extends LitElement {
|
|||
</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<arx-button
|
||||
label="Share Feedback"
|
||||
@click=${() => window.open('https://arx-ccn.com/eve-feedback')}
|
||||
>
|
||||
<iconify-icon slot="prefix" icon="mdi:feedback"></iconify-icon>
|
||||
</arx-button>
|
||||
<arx-button
|
||||
variant="secondary"
|
||||
label="Report a Bug"
|
||||
@click=${() => window.open('https://arx-ccn.com/report-eve-bug')}
|
||||
>
|
||||
<iconify-icon slot="prefix" icon="mdi:bug"></iconify-icon>
|
||||
</arx-button>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<span></span>
|
||||
<arx-button
|
||||
variant="primary"
|
||||
label="Next"
|
||||
@click=${() => this.handleNavigation(2)}
|
||||
@click=${() => this.nextStep()}
|
||||
></arx-button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
private renderPageTwo() {
|
||||
private renderRelaySetupPage() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<arx-progress-steps
|
||||
.currentPage=${this.currentPage}
|
||||
.pageLabels=${this.pageLabels}
|
||||
></arx-progress-steps>
|
||||
<section>
|
||||
<h2>Configure Eve Relay</h2>
|
||||
<p>
|
||||
During this alpha phase, manual relay configuration is required.
|
||||
This process will be automated in future releases.
|
||||
</p>
|
||||
<p>Please press the button below to start the relay.</p>
|
||||
<arx-button
|
||||
variant="primary"
|
||||
label=${this.relayStatus.running ? 'Relay Running' : 'Start Relay'}
|
||||
?disabled=${this.relayStatus.running}
|
||||
@click=${() => this.startRelay()}
|
||||
>
|
||||
${when(
|
||||
this.relayStatus.running,
|
||||
() => html`<iconify-icon slot="prefix" icon="mdi:check-circle"></iconify-icon>`,
|
||||
)}
|
||||
</arx-button>
|
||||
${when(
|
||||
this.relayStatus.running,
|
||||
() => html`
|
||||
<p class="note">
|
||||
<iconify-icon icon="mdi:information"></iconify-icon>
|
||||
Relay is running with PID: ${this.relayStatus.pid}
|
||||
</p>
|
||||
<arx-relay-logs .logs=${this.relayStatus.logs}></arx-relay-logs>
|
||||
<arx-setup-ccn-list @ccn-selected=${this.onCCNSelected} .ccns=${this.ccnList}></arx-setup-ccn-list>`,
|
||||
)}
|
||||
<p>
|
||||
Having trouble? Our team is here to help if you encounter any
|
||||
issues.
|
||||
</p>
|
||||
<p>Click Continue once the relay is running.</p>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<arx-button
|
||||
@click=${() => this.previousStep()}
|
||||
variant="secondary"
|
||||
label="Back"
|
||||
>
|
||||
</arx-button>
|
||||
<arx-button
|
||||
@click=${() => this.nextStep()}
|
||||
variant="primary"
|
||||
?disabled=${!this.relayStatus.running}
|
||||
label=${this.selectedCCN ? 'Continue' : 'Create CCN'}
|
||||
>
|
||||
</arx-button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
private onCCNSelected(e: CustomEvent<{ ccn: string }>) {
|
||||
this.selectedCCN = e.detail.ccn;
|
||||
}
|
||||
|
||||
private async startRelay() {
|
||||
await window.relay.start(this.encryptionPassphrase);
|
||||
await this.updateRelayStatus();
|
||||
await new Promise((resolve) => {
|
||||
const ws = new WebSocket('ws://localhost:6942');
|
||||
ws.onopen = () => {
|
||||
ws.send(JSON.stringify(['CCN', 'LIST']));
|
||||
};
|
||||
ws.onmessage = ({ data }) => {
|
||||
const responseData = JSON.parse(data);
|
||||
if (responseData[0] !== 'OK' || responseData[1] !== 'CCN LIST' || responseData[2] !== true) return;
|
||||
this.ccnList = JSON.parse(responseData[3]);
|
||||
resolve(true);
|
||||
ws.close();
|
||||
};
|
||||
ws.onerror = () => {
|
||||
resolve(false);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private async updateRelayStatus() {
|
||||
this.relayStatus = await window.relay.getStatus();
|
||||
if (this.relayStatus.running) setTimeout(() => this.updateRelayStatus(), 2000);
|
||||
}
|
||||
|
||||
private async createCCN() {
|
||||
await new Promise((resolve) => {
|
||||
const ws = new WebSocket('ws://localhost:6942');
|
||||
ws.onopen = () => {
|
||||
ws.send(
|
||||
JSON.stringify([
|
||||
'CCN',
|
||||
'CREATE',
|
||||
{
|
||||
name: this.communityName,
|
||||
seed: this.seedPhrase,
|
||||
},
|
||||
]),
|
||||
);
|
||||
};
|
||||
ws.onmessage = ({ data }) => {
|
||||
const responseData = JSON.parse(data);
|
||||
if (responseData[0] !== 'OK' || responseData[1] !== 'CCN CREATED' || responseData[2] !== true) return;
|
||||
resolve(true);
|
||||
ws.close();
|
||||
this.selectedCCN = JSON.parse(responseData[3]).pubkey;
|
||||
};
|
||||
ws.onerror = () => {
|
||||
resolve(false);
|
||||
};
|
||||
});
|
||||
this.handleNavigation(4);
|
||||
}
|
||||
|
||||
private renderSeedPhrasePage() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<arx-progress-steps
|
||||
|
@ -334,89 +467,34 @@ export class InitialSetup extends LitElement {
|
|||
</div>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<arx-button
|
||||
@click=${() => this.handleNavigation(1)}
|
||||
label="Back"
|
||||
variant="secondary"
|
||||
>
|
||||
</arx-button>
|
||||
<arx-button
|
||||
@click=${() => this.handleNavigation(3)}
|
||||
?disabled=${!this.isValidSeedPhrase()}
|
||||
label="Continue"
|
||||
variant="primary"
|
||||
>
|
||||
</arx-button>
|
||||
</div>
|
||||
</main>
|
||||
`;
|
||||
}
|
||||
|
||||
private async startRelay() {
|
||||
await window.relay.writeSeed(this.seedPhrase);
|
||||
await window.relay.start(this.encryptionPassphrase);
|
||||
this.updateRelayStatus();
|
||||
}
|
||||
|
||||
private async updateRelayStatus() {
|
||||
this.relayStatus = await window.relay.getStatus();
|
||||
if (this.relayStatus.running) setTimeout(() => this.updateRelayStatus(), 2000);
|
||||
}
|
||||
|
||||
private renderPageThree() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<arx-progress-steps
|
||||
.currentPage=${this.currentPage}
|
||||
.pageLabels=${this.pageLabels}
|
||||
></arx-progress-steps>
|
||||
<section>
|
||||
<h2>Configure Eve Relay</h2>
|
||||
<h3>Community Name</h3>
|
||||
|
||||
<p>
|
||||
During this alpha phase, manual relay configuration is required.
|
||||
This process will be automated in future releases.
|
||||
Enter a name for your community. This will be used to identify your
|
||||
community in Eve
|
||||
</p>
|
||||
<p>Please press the button below to start the relay.</p>
|
||||
<arx-button
|
||||
variant="primary"
|
||||
label=${this.relayStatus.running ? 'Relay Running' : 'Start Relay'}
|
||||
?disabled=${this.relayStatus.running}
|
||||
@click=${() => this.startRelay()}
|
||||
>
|
||||
${when(
|
||||
this.relayStatus.running,
|
||||
() => html`<iconify-icon slot="prefix" icon="mdi:check-circle"></iconify-icon>`,
|
||||
)}
|
||||
</arx-button>
|
||||
${when(
|
||||
this.relayStatus.running,
|
||||
() => html`
|
||||
<p class="note">
|
||||
<iconify-icon icon="mdi:information"></iconify-icon>
|
||||
Relay is running with PID: ${this.relayStatus.pid}
|
||||
</p>
|
||||
<arx-relay-logs .logs=${this.relayStatus.logs}></arx-relay-logs>`,
|
||||
)}
|
||||
<p>
|
||||
Having trouble? Our team is here to help if you encounter any
|
||||
issues.
|
||||
</p>
|
||||
<p>Click Continue once the relay is running.</p>
|
||||
|
||||
<arx-input
|
||||
type="text"
|
||||
placeholder="Enter Community Name..."
|
||||
.value=${this.communityName}
|
||||
@change=${this.onCommunityNameInput}
|
||||
></arx-input>
|
||||
</section>
|
||||
|
||||
<div class="navigation">
|
||||
<arx-button
|
||||
@click=${() => this.handleNavigation(2)}
|
||||
variant="secondary"
|
||||
@click=${() => this.previousStep()}
|
||||
label="Back"
|
||||
variant="secondary"
|
||||
>
|
||||
</arx-button>
|
||||
<arx-button
|
||||
@click=${() => this.handleNavigation(4)}
|
||||
variant="primary"
|
||||
?disabled=${!this.relayStatus.running}
|
||||
@click=${() => this.createCCN()}
|
||||
?disabled=${!this.isValidSeedPhrase() || !this.isValidCommunityName()}
|
||||
label="Continue"
|
||||
variant="primary"
|
||||
>
|
||||
</arx-button>
|
||||
</div>
|
||||
|
@ -432,7 +510,7 @@ export class InitialSetup extends LitElement {
|
|||
this.profileImage = e.detail.value;
|
||||
}
|
||||
|
||||
private renderPageFour() {
|
||||
private renderProfileSetupPage() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<arx-progress-steps
|
||||
|
@ -473,13 +551,13 @@ export class InitialSetup extends LitElement {
|
|||
</section>
|
||||
<div class="navigation">
|
||||
<arx-button
|
||||
@click=${() => this.handleNavigation(3)}
|
||||
@click=${() => this.previousStep()}
|
||||
variant="secondary"
|
||||
label="Back"
|
||||
>
|
||||
</arx-button>
|
||||
<arx-button
|
||||
@click=${() => this.goToFinalStep()}
|
||||
@click=${() => this.nextStep()}
|
||||
variant="primary"
|
||||
label="Next"
|
||||
>
|
||||
|
@ -490,17 +568,34 @@ export class InitialSetup extends LitElement {
|
|||
}
|
||||
|
||||
private async goToFinalStep() {
|
||||
await new Promise((resolve) => {
|
||||
const ws = new WebSocket('ws://localhost:6942');
|
||||
ws.onopen = () => {
|
||||
ws.send(
|
||||
JSON.stringify([
|
||||
'CCN',
|
||||
'ACTIVATE',
|
||||
{
|
||||
pubkey: this.selectedCCN,
|
||||
},
|
||||
]),
|
||||
);
|
||||
};
|
||||
ws.onmessage = ({ data }) => {
|
||||
const responseData = JSON.parse(data);
|
||||
if (responseData[0] !== 'OK' || responseData[1] !== 'CCN ACTIVATED' || responseData[2] !== true) return;
|
||||
resolve(true);
|
||||
};
|
||||
ws.onerror = () => {
|
||||
resolve(false);
|
||||
};
|
||||
});
|
||||
const randomPrivateKey = nostrTools.generateSecretKey();
|
||||
const encryptedNsec = nip49.encrypt(randomPrivateKey, this.encryptionPassphrase);
|
||||
const npub = nip19.npubEncode(nostrTools.getPublicKey(randomPrivateKey));
|
||||
|
||||
this.lightningAddress = `${npub}@npub.cash`;
|
||||
|
||||
localStorage.setItem('ncryptsec', encryptedNsec);
|
||||
|
||||
setSigner(new NDKPrivateKeySigner(randomPrivateKey));
|
||||
await ndk.connect(5000);
|
||||
|
||||
await ndk.connect(10000);
|
||||
const event = new NDKEvent(ndk);
|
||||
event.kind = NDKKind.Metadata;
|
||||
event.content = JSON.stringify({
|
||||
|
@ -509,12 +604,13 @@ export class InitialSetup extends LitElement {
|
|||
lud16: this.lightningAddress,
|
||||
});
|
||||
await event.sign();
|
||||
await event.publish();
|
||||
|
||||
const relaySet = new NDKRelaySet(new Set([new NDKRelay('ws://localhost:6942', undefined, ndk)]), ndk);
|
||||
await event.publish(relaySet, 10000);
|
||||
localStorage.setItem('ncryptsec', encryptedNsec);
|
||||
this.handleNavigation(5);
|
||||
}
|
||||
|
||||
private renderPageFive() {
|
||||
private renderFinalPage() {
|
||||
return html`
|
||||
<main class="welcome-container" ${animate()}>
|
||||
<arx-progress-steps
|
||||
|
@ -539,7 +635,7 @@ export class InitialSetup extends LitElement {
|
|||
|
||||
<div class="navigation">
|
||||
<arx-button
|
||||
@click=${() => this.handleNavigation(4)}
|
||||
@click=${() => this.previousStep()}
|
||||
variant="secondary"
|
||||
label="Back"
|
||||
>
|
||||
|
@ -571,15 +667,15 @@ export class InitialSetup extends LitElement {
|
|||
override render() {
|
||||
switch (this.currentPage) {
|
||||
case 1:
|
||||
return this.renderPageOne();
|
||||
return this.renderWelcomePage();
|
||||
case 2:
|
||||
return this.renderPageTwo();
|
||||
return this.renderRelaySetupPage();
|
||||
case 3:
|
||||
return this.renderPageThree();
|
||||
return this.renderSeedPhrasePage();
|
||||
case 4:
|
||||
return this.renderPageFour();
|
||||
return this.renderProfileSetupPage();
|
||||
case 5:
|
||||
return this.renderPageFive();
|
||||
return this.renderFinalPage();
|
||||
default:
|
||||
return html`<div class="welcome-container">
|
||||
<arx-loading-view></arx-loading-view>
|
||||
|
|
56
src/components/Setup/CCNList.ts
Normal file
|
@ -0,0 +1,56 @@
|
|||
import '@components/General/Button';
|
||||
import '@components/General/Select';
|
||||
import { LitElement, css, html } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
|
||||
@customElement('arx-setup-ccn-list')
|
||||
export class CCNList extends LitElement {
|
||||
@property({ type: Array }) ccns: { name: string; pubkey: string }[] = [];
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
`;
|
||||
|
||||
@state() private selectedCCN: string | undefined;
|
||||
|
||||
private _valueMapper = (ccn: { name: string; pubkey: string }) => ccn.pubkey;
|
||||
private _textMapper = (ccn: { name: string; pubkey: string }) => ccn.name;
|
||||
|
||||
private _handleCCNSelected(pubkey: string | undefined) {
|
||||
this.selectedCCN = pubkey;
|
||||
this.dispatchEvent(new CustomEvent('ccn-selected', { detail: { ccn: this.selectedCCN } }));
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (this.ccns.length === 0)
|
||||
return html`
|
||||
<p>No CCNs found in the relay. You will be asked to setup a CCN in the next step.</p>
|
||||
`;
|
||||
|
||||
if (this.ccns.length === 1)
|
||||
return html`
|
||||
<div>
|
||||
<h2>CCN Found</h2>
|
||||
<p>We found one CCN in your relay, which likely means you've used Eve before or there was an update introducing new setup steps. You can use this CCN to proceed with the setup, or create a new one.</p>
|
||||
<p>CCN Name: ${this.ccns[0].name}</p>
|
||||
<arx-button variant=${this.selectedCCN === this.ccns[0].pubkey ? 'accent' : 'secondary'} @click="${() => this._handleCCNSelected(this.ccns[0].pubkey)}" label="Use This CCN"></arx-button>
|
||||
<arx-button variant=${this.selectedCCN === undefined ? 'accent' : 'secondary'} @click="${() => this._handleCCNSelected(undefined)}" label="Create New CCN"></arx-button>
|
||||
</div>
|
||||
`;
|
||||
return html`
|
||||
<div>
|
||||
<h2>CCNs</h2>
|
||||
<p>We found the following CCNs in your relay, which likely means you've used Eve before or there was an update introducing new setup steps. You can quickly choose one of these CCNs to proceed with the setup, or create a new one.</p>
|
||||
<arx-select
|
||||
.options="${this.ccns}"
|
||||
.valueMapper="${this._valueMapper}"
|
||||
.textMapper="${this._textMapper}"
|
||||
@change="${(e: CustomEvent<{ value: string }>) => this._handleCCNSelected(e.detail.value)}"
|
||||
></arx-select>
|
||||
<arx-button variant=${this.selectedCCN === undefined ? 'accent' : 'secondary'} @click="${() => this._handleCCNSelected(undefined)}" label="Create New CCN"></arx-button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
|
@ -20,31 +20,31 @@ export default class Sidebar extends LitElement {
|
|||
apps = [
|
||||
{
|
||||
id: 1,
|
||||
href: 'beacon',
|
||||
name: 'Beacon',
|
||||
href: 'calendar',
|
||||
name: 'Calendar',
|
||||
color: '#FF8C00',
|
||||
icon: 'fa-solid:sun',
|
||||
icon: 'arx:calendar',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
href: 'arbor',
|
||||
name: 'Arbor',
|
||||
color: '#FF4040',
|
||||
icon: 'fa-solid:tree',
|
||||
icon: 'arx:arbor',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
href: 'wallet',
|
||||
name: 'Wallet',
|
||||
color: '#1E90FF',
|
||||
icon: 'fa-solid:spa',
|
||||
icon: 'arx:wallet',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
href: 'settings',
|
||||
name: 'Settings',
|
||||
color: '#7B68EE',
|
||||
icon: 'fa-solid:tools',
|
||||
icon: 'arx:settings',
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -1,19 +1,10 @@
|
|||
import path from 'node:path';
|
||||
import { is, optimizer } from '@electron-toolkit/utils';
|
||||
import { BrowserWindow, app, ipcMain, shell } from 'electron';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { RelayManager } from './relayManager';
|
||||
|
||||
const relay = new RelayManager();
|
||||
|
||||
ipcMain.handle('relay:writeSeed', async (_, ...args: string[]) => {
|
||||
if (!args[0]) throw new Error('No seed provided');
|
||||
const seed = args[0] as string;
|
||||
const configPath = relay.getRelayConfigPath();
|
||||
fs.mkdirSync(configPath, { recursive: true });
|
||||
fs.writeFileSync(path.join(configPath, 'ccn.seed'), seed);
|
||||
});
|
||||
|
||||
ipcMain.handle('relay:start', (_, ...args: string[]) => {
|
||||
if (!args[0]) throw new Error('No encryption key provided');
|
||||
const encryptionKey = args[0];
|
||||
|
|
|
@ -5,7 +5,6 @@ if (process.contextIsolated) {
|
|||
try {
|
||||
contextBridge.exposeInMainWorld('electron', electronAPI);
|
||||
contextBridge.exposeInMainWorld('relay', {
|
||||
writeSeed: (seed: string) => ipcRenderer.invoke('relay:writeSeed', seed),
|
||||
start: (encryptionKey: string) => ipcRenderer.invoke('relay:start', encryptionKey),
|
||||
stop: () => ipcRenderer.invoke('relay:stop'),
|
||||
getStatus: () => ipcRenderer.invoke('relay:status'),
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import { is } from '@electron-toolkit/utils';
|
||||
import { app } from 'electron';
|
||||
import { type ChildProcess, spawn } from 'node:child_process';
|
||||
import { existsSync, rmdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import { is } from '@electron-toolkit/utils';
|
||||
import { app } from 'electron';
|
||||
|
||||
type PackageEnvironment = 'flatpak' | 'appimage' | 'system' | 'mac' | 'dev';
|
||||
|
||||
|
@ -144,20 +144,6 @@ export class RelayManager {
|
|||
this.process = spawn('nc', ['localhost', '6942']);
|
||||
this.devRunning = true;
|
||||
|
||||
if (this.process.stdout) {
|
||||
this.process.stdout.on('data', (data: Buffer) => {
|
||||
const logLine = data.toString().trim();
|
||||
this.addLog(`[DEV] ${logLine}`);
|
||||
});
|
||||
}
|
||||
|
||||
if (this.process.stderr) {
|
||||
this.process.stderr.on('data', (data: Buffer) => {
|
||||
const logLine = data.toString().trim();
|
||||
this.addLog(`[DEV] ERROR: ${logLine}`);
|
||||
});
|
||||
}
|
||||
|
||||
this.process.on('error', (err: Error) => {
|
||||
this.addLog(`[DEV] Failed to start netcat: ${err.message}`);
|
||||
this.process = null;
|
||||
|
@ -210,9 +196,7 @@ export class RelayManager {
|
|||
|
||||
this.process.on('exit', this.handleProcessExit.bind(this));
|
||||
|
||||
if (this.process.pid) {
|
||||
this.restartAttempts = 0;
|
||||
}
|
||||
if (this.process.pid) this.restartAttempts = 0;
|
||||
} catch (error) {
|
||||
console.error(`Error starting Relay: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
this.restartProcess();
|
||||
|
|
64
src/main.ts
|
@ -1,3 +1,4 @@
|
|||
import '@/arx-icons';
|
||||
import '@components/Breadcrumbs';
|
||||
import '@components/ErrorView';
|
||||
import '@components/Header';
|
||||
|
@ -8,6 +9,8 @@ import '@routes/router';
|
|||
import type EveRouter from '@routes/router';
|
||||
import './style.css';
|
||||
|
||||
let availableCCNs: { name: string; pubkey: string }[] = [];
|
||||
|
||||
function checkRelayUp() {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeoutId = setTimeout(() => {
|
||||
|
@ -19,7 +22,66 @@ function checkRelayUp() {
|
|||
|
||||
ws.onopen = () => {
|
||||
clearTimeout(timeoutId);
|
||||
ws.close();
|
||||
ws.send(JSON.stringify(['CCN', 'LIST']));
|
||||
};
|
||||
|
||||
ws.onmessage = async (message) => {
|
||||
const data = JSON.parse(message.data);
|
||||
console.log(data);
|
||||
if (data[0] !== 'OK' || data[1] !== 'CCN LIST' || data[2] !== true) return;
|
||||
availableCCNs = JSON.parse(data[3]);
|
||||
if (availableCCNs.length === 0) throw new Error('No CCNs found');
|
||||
if (availableCCNs.length === 1) {
|
||||
ws.send(
|
||||
JSON.stringify([
|
||||
'CCN',
|
||||
'ACTIVATE',
|
||||
{
|
||||
pubkey: availableCCNs[0].pubkey,
|
||||
},
|
||||
]),
|
||||
);
|
||||
return resolve(true);
|
||||
}
|
||||
if (localStorage.getItem('selectedCCN')) {
|
||||
const selected = JSON.parse(localStorage.getItem('selectedCCN')!) as { date: number; pubkey: string };
|
||||
if (Date.now() - selected.date < 1000 * 60 * 60 * 24) {
|
||||
ws.send(JSON.stringify(['CCN', 'ACTIVATE', { pubkey: selected.pubkey }]));
|
||||
return resolve(true);
|
||||
}
|
||||
}
|
||||
await new Promise((resolve) => {
|
||||
let selectedCCN: string | null = availableCCNs[0].pubkey;
|
||||
const selectBox = document.createElement('arx-select');
|
||||
selectBox.options = availableCCNs.map((ccn) => ({ label: ccn.name, value: ccn.pubkey }));
|
||||
selectBox.addEventListener('change', (e) => {
|
||||
selectedCCN = e.target.value;
|
||||
});
|
||||
const okButton = document.createElement('arx-button');
|
||||
okButton.label = 'OK';
|
||||
okButton.addEventListener('click', () => {
|
||||
if (selectedCCN) {
|
||||
ws.send(
|
||||
JSON.stringify([
|
||||
'CCN',
|
||||
'ACTIVATE',
|
||||
{
|
||||
pubkey: selectedCCN,
|
||||
},
|
||||
]),
|
||||
);
|
||||
localStorage.setItem('selectedCCN', JSON.stringify({ date: Date.now(), pubkey: selectedCCN }));
|
||||
resolve(true);
|
||||
document.body.removeChild(dialog);
|
||||
}
|
||||
});
|
||||
const dialog = document.createElement('arx-dialog');
|
||||
dialog.title = 'Select CCN';
|
||||
dialog.open = true;
|
||||
dialog.appendChild(selectBox);
|
||||
dialog.appendChild(okButton);
|
||||
document.body.appendChild(dialog);
|
||||
});
|
||||
resolve(true);
|
||||
};
|
||||
|
||||
|
|
1
src/relayManager.d.ts
vendored
|
@ -5,7 +5,6 @@ interface RelayStatus {
|
|||
}
|
||||
|
||||
interface RelayBridge {
|
||||
writeSeed: (seed: string) => Promise<void>;
|
||||
start: (encryptionKey: string) => Promise<void>;
|
||||
stop: () => Promise<void>;
|
||||
getStatus: () => Promise<RelayStatus>;
|
||||
|
|
|
@ -5,14 +5,14 @@ import { customElement, state } from 'lit/decorators.js';
|
|||
import { map } from 'lit/directives/map.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
||||
import '@components/Breadcrumbs';
|
||||
import '@components/Arbor/ForumTopic';
|
||||
import '@components/Arbor/ForumCategory';
|
||||
import '@components/Arbor/ForumThread';
|
||||
import '@components/Breadcrumbs';
|
||||
import '@components/General/Prompt';
|
||||
|
||||
import type { EvePrompt } from '@components/General/Prompt';
|
||||
|
||||
interface ForumTopic {
|
||||
interface ForumThread {
|
||||
id: string;
|
||||
title: string;
|
||||
author: string;
|
||||
|
@ -23,7 +23,7 @@ interface ForumTopic {
|
|||
interface ForumCategory {
|
||||
id: string;
|
||||
name: string;
|
||||
topics: ForumTopic[];
|
||||
threads: ForumThread[];
|
||||
}
|
||||
|
||||
@customElement('arx-arbor-home')
|
||||
|
@ -33,7 +33,7 @@ export class ArborForum extends LitElement {
|
|||
|
||||
private isSaving = false;
|
||||
|
||||
private topicsQuery: NDKSubscription | undefined;
|
||||
private threadsQuery: NDKSubscription | undefined;
|
||||
|
||||
@state()
|
||||
private forum: NDKEvent | null = null;
|
||||
|
@ -42,6 +42,10 @@ export class ArborForum extends LitElement {
|
|||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
arx-button {
|
||||
margin-bottom: calc(var(--spacing-sm) / 2);
|
||||
}
|
||||
`;
|
||||
|
||||
override async connectedCallback() {
|
||||
|
@ -92,11 +96,11 @@ export class ArborForum extends LitElement {
|
|||
{
|
||||
id: dtag,
|
||||
name: newCategory,
|
||||
topics: [],
|
||||
threads: [],
|
||||
},
|
||||
];
|
||||
|
||||
this.loadTopics();
|
||||
this.loadThreads();
|
||||
} catch (error) {
|
||||
console.error('Failed to create category:', error);
|
||||
alert('Failed to create category');
|
||||
|
@ -128,17 +132,17 @@ export class ArborForum extends LitElement {
|
|||
{
|
||||
id: categoryId,
|
||||
name: tag[2],
|
||||
topics: [],
|
||||
threads: [],
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
this.loadTopics();
|
||||
this.loadThreads();
|
||||
}
|
||||
|
||||
private async loadTopics() {
|
||||
if (this.topicsQuery) this.topicsQuery.stop();
|
||||
this.topicsQuery = ndk
|
||||
private async loadThreads() {
|
||||
if (this.threadsQuery) this.threadsQuery.stop();
|
||||
this.threadsQuery = ndk
|
||||
.subscribe({
|
||||
kinds: [892 as NDKKind],
|
||||
'#d': this.categories.map((category) => `60891:${category.id}`),
|
||||
|
@ -148,9 +152,9 @@ export class ArborForum extends LitElement {
|
|||
(category) => category.id === event.tags.find((tag) => tag[0] === 'd')?.[1].split(':')[1],
|
||||
);
|
||||
if (categoryId === -1) return;
|
||||
if (this.categories[categoryId].topics.find((topic) => topic.id === event.id)) return;
|
||||
this.categories[categoryId].topics = [
|
||||
...this.categories[categoryId].topics,
|
||||
if (this.categories[categoryId].threads.find((thread) => thread.id === event.id)) return;
|
||||
this.categories[categoryId].threads = [
|
||||
...this.categories[categoryId].threads,
|
||||
{
|
||||
id: event.id,
|
||||
title: event.tags.find((tag) => tag[0] === 'name')?.[1] || '',
|
||||
|
@ -187,18 +191,18 @@ export class ArborForum extends LitElement {
|
|||
this.categories,
|
||||
(category) => html`
|
||||
<arx-arbor-forum-category id=${category.id} title=${category.name}>
|
||||
${when(category.topics.length === 0, () => html` <div style="padding: 1rem 1.5rem">No topics...</div> `)}
|
||||
${when(category.threads.length === 0, () => html` <div style="padding: 1rem 1.5rem">No threads...</div> `)}
|
||||
${map(
|
||||
category.topics,
|
||||
(topic) => html`
|
||||
<arx-arbor-forum-topic
|
||||
id=${topic.id}
|
||||
title=${topic.title}
|
||||
description=${topic.description}
|
||||
lastPostBy=${topic.author}
|
||||
lastPostTime=${topic.created_at}
|
||||
category.threads,
|
||||
(thread) => html`
|
||||
<arx-arbor-forum-thread
|
||||
id=${thread.id}
|
||||
title=${thread.title}
|
||||
description=${thread.description}
|
||||
lastPostBy=${thread.author}
|
||||
lastPostTime=${thread.created_at}
|
||||
>
|
||||
</arx-arbor-forum-topic>
|
||||
</arx-arbor-forum-thread>
|
||||
`,
|
||||
)}
|
||||
</arx-arbor-forum-category>
|
||||
|
|
|
@ -9,13 +9,13 @@ import '@components/General/Textarea';
|
|||
@customElement('arx-arbor-post-creator')
|
||||
export class ArborPostCreator extends LitElement {
|
||||
@property({ type: String })
|
||||
topicId = '';
|
||||
threadId = '';
|
||||
|
||||
@state()
|
||||
private postContent = '';
|
||||
|
||||
@state()
|
||||
private topic: NDKEvent | null = null;
|
||||
private thread: NDKEvent | null = null;
|
||||
|
||||
@state()
|
||||
private isCreating = false;
|
||||
|
@ -34,7 +34,7 @@ export class ArborPostCreator extends LitElement {
|
|||
gap: 1rem;
|
||||
}
|
||||
|
||||
.topic-id {
|
||||
.thread-id {
|
||||
color: #666;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
@ -58,24 +58,24 @@ export class ArborPostCreator extends LitElement {
|
|||
|
||||
override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this.loadTopic();
|
||||
this.loadThread();
|
||||
}
|
||||
|
||||
private async loadTopic() {
|
||||
private async loadThread() {
|
||||
try {
|
||||
await getSigner();
|
||||
this.topic = await ndk.fetchEvent(this.topicId);
|
||||
if (!this.topic) {
|
||||
throw new Error('Could not load topic');
|
||||
this.thread = await ndk.fetchEvent(this.threadId);
|
||||
if (!this.thread) {
|
||||
throw new Error('Could not load thread');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to load topic:', error);
|
||||
console.error('Failed to load thread:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private async doCreatePost() {
|
||||
if (this.isCreating) return;
|
||||
if (!this.topic) return;
|
||||
if (!this.thread) return;
|
||||
|
||||
if (this.postContent.length < 10) {
|
||||
this.error = 'Post content must be at least 10 characters long';
|
||||
|
@ -88,9 +88,9 @@ export class ArborPostCreator extends LitElement {
|
|||
const event = new NDKEvent(ndk);
|
||||
event.kind = 893;
|
||||
event.tags = [
|
||||
['A', this.topic.tags.find((tag) => tag[0] === 'd')?.[1] || ''],
|
||||
['E', this.topicId],
|
||||
['p', this.topic.pubkey],
|
||||
['A', this.thread.tags.find((tag) => tag[0] === 'd')?.[1] || ''],
|
||||
['E', this.threadId],
|
||||
['p', this.thread.pubkey],
|
||||
];
|
||||
event.content = this.postContent;
|
||||
|
||||
|
@ -117,7 +117,7 @@ export class ArborPostCreator extends LitElement {
|
|||
override render() {
|
||||
return html`
|
||||
<div class="container">
|
||||
<div class="topic-id">Topic ID: ${this.topicId}</div>
|
||||
<div class="thread-id">Thread ID: ${this.threadId}</div>
|
||||
|
||||
<arx-textarea
|
||||
placeholder="Post. You can use Markdown here."
|
||||
|
|
|
@ -8,16 +8,16 @@ import '@components/General/Button';
|
|||
import '@components/General/Input';
|
||||
import '@components/General/Textarea';
|
||||
|
||||
@customElement('arx-arbor-topic-creator')
|
||||
export class ArborTopicCreator extends LitElement {
|
||||
@customElement('arx-arbor-thread-creator')
|
||||
export class ArborThreadCreator extends LitElement {
|
||||
@property({ type: String })
|
||||
categoryId = '';
|
||||
|
||||
@state()
|
||||
private newTopic = '';
|
||||
private newThread = '';
|
||||
|
||||
@state()
|
||||
private topicContent = '';
|
||||
private threadContent = '';
|
||||
|
||||
@state()
|
||||
private isCreating = false;
|
||||
|
@ -39,16 +39,16 @@ export class ArborTopicCreator extends LitElement {
|
|||
}
|
||||
`;
|
||||
|
||||
private async doCreateTopic() {
|
||||
private async doCreateThread() {
|
||||
if (this.isCreating) return;
|
||||
|
||||
if (this.newTopic.length < 3) {
|
||||
alert('Topic title must be at least 3 characters long');
|
||||
if (this.newThread.length < 3) {
|
||||
alert('Thread title must be at least 3 characters long');
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.topicContent.length < 10) {
|
||||
alert('Topic content must be at least 10 characters long');
|
||||
if (this.threadContent.length < 10) {
|
||||
alert('Thread content must be at least 10 characters long');
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -59,40 +59,27 @@ export class ArborTopicCreator extends LitElement {
|
|||
const event = new NDKEvent(ndk);
|
||||
event.kind = 892;
|
||||
event.tags = [
|
||||
['name', this.newTopic],
|
||||
['name', this.newThread],
|
||||
['d', `60891:${this.categoryId}`],
|
||||
];
|
||||
event.content = this.topicContent;
|
||||
event.content = this.threadContent;
|
||||
await event.sign();
|
||||
await event.publish();
|
||||
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('topic-created', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {
|
||||
topicId: event.id,
|
||||
categoryId: this.categoryId,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
this.newTopic = '';
|
||||
this.topicContent = '';
|
||||
location.hash = `/arbor/${event.id}`;
|
||||
} catch (error) {
|
||||
console.error('Failed to create topic:', error);
|
||||
alert('Failed to create topic');
|
||||
console.error('Failed to create thread:', error);
|
||||
alert('Failed to create thread');
|
||||
} finally {
|
||||
this.isCreating = false;
|
||||
}
|
||||
}
|
||||
|
||||
private handleTopicInput(e: ArxInputChangeEvent) {
|
||||
this.newTopic = e.detail.value;
|
||||
private handleThreadInput(e: ArxInputChangeEvent) {
|
||||
this.newThread = e.detail.value;
|
||||
}
|
||||
|
||||
private handleContentInput(e: ArxInputChangeEvent) {
|
||||
this.topicContent = e.detail.value;
|
||||
this.threadContent = e.detail.value;
|
||||
}
|
||||
|
||||
override render() {
|
||||
|
@ -101,15 +88,15 @@ export class ArborTopicCreator extends LitElement {
|
|||
|
||||
<arx-input
|
||||
type="text"
|
||||
placeholder="New Topic"
|
||||
.value=${this.newTopic}
|
||||
@change=${this.handleTopicInput}
|
||||
placeholder="New Thread"
|
||||
.value=${this.newThread}
|
||||
@change=${this.handleThreadInput}
|
||||
?disabled=${this.isCreating}
|
||||
></arx-input>
|
||||
|
||||
<arx-textarea
|
||||
placeholder="Topic. You can use Markdown here."
|
||||
.value=${this.topicContent}
|
||||
placeholder="Thread. You can use Markdown here."
|
||||
.value=${this.threadContent}
|
||||
@change=${this.handleContentInput}
|
||||
?disabled=${this.isCreating}
|
||||
></arx-textarea>
|
||||
|
@ -123,7 +110,7 @@ export class ArborTopicCreator extends LitElement {
|
|||
|
||||
<arx-button
|
||||
label=${this.isCreating ? 'Creating...' : 'Create'}
|
||||
@click=${this.doCreateTopic}
|
||||
@click=${this.doCreateThread}
|
||||
?disabled=${this.isCreating}
|
||||
>
|
||||
</arx-button>
|
|
@ -3,8 +3,8 @@ import type { NDKKind, NDKSubscription } from '@nostr-dev-kit/ndk';
|
|||
import { LitElement, css, html } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
|
||||
import '@components/Breadcrumbs';
|
||||
import '@components/Arbor/ForumPost';
|
||||
import '@components/Breadcrumbs';
|
||||
import '@components/General/Button';
|
||||
|
||||
import { map } from 'lit/directives/map.js';
|
||||
|
@ -17,10 +17,10 @@ interface ForumPost {
|
|||
content: string;
|
||||
}
|
||||
|
||||
@customElement('arx-arbor-topic-view')
|
||||
export class ArborTopicView extends LitElement {
|
||||
@customElement('arx-arbor-thread-view')
|
||||
export class ArborThreadView extends LitElement {
|
||||
@property({ type: String })
|
||||
topicId = '';
|
||||
threadId = '';
|
||||
|
||||
@state()
|
||||
override title = '';
|
||||
|
@ -36,7 +36,7 @@ export class ArborTopicView extends LitElement {
|
|||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.topic-container {
|
||||
.thread-container {
|
||||
background: var(--color-base-100);
|
||||
border-radius: var(--radius-box);
|
||||
border: var(--border) solid var(--color-base-300);
|
||||
|
@ -144,7 +144,7 @@ export class ArborTopicView extends LitElement {
|
|||
|
||||
override async connectedCallback() {
|
||||
super.connectedCallback();
|
||||
await this.loadTopic();
|
||||
await this.loadThread();
|
||||
}
|
||||
|
||||
override disconnectedCallback() {
|
||||
|
@ -154,13 +154,13 @@ export class ArborTopicView extends LitElement {
|
|||
}
|
||||
}
|
||||
|
||||
private async loadTopic() {
|
||||
private async loadThread() {
|
||||
try {
|
||||
await getSigner();
|
||||
const event = await ndk.fetchEvent(this.topicId);
|
||||
const event = await ndk.fetchEvent(this.threadId);
|
||||
|
||||
if (!event) {
|
||||
throw new Error('Could not load topic');
|
||||
throw new Error('Could not load thread');
|
||||
}
|
||||
|
||||
this.title = event.tags.find((tag) => tag[0] === 'name')?.[1] || '';
|
||||
|
@ -178,7 +178,7 @@ export class ArborTopicView extends LitElement {
|
|||
this.subscription = ndk
|
||||
.subscribe({
|
||||
kinds: [893 as NDKKind],
|
||||
'#E': [this.topicId],
|
||||
'#E': [this.threadId],
|
||||
})
|
||||
.on('event', (event) => {
|
||||
this.posts = [
|
||||
|
@ -192,8 +192,8 @@ export class ArborTopicView extends LitElement {
|
|||
];
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Failed to load topic:', error);
|
||||
alert('Could not load topic');
|
||||
console.error('Failed to load thread:', error);
|
||||
alert('Could not load thread');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,9 +203,9 @@ export class ArborTopicView extends LitElement {
|
|||
return html`
|
||||
<arx-breadcrumbs .items=${breadcrumbItems}></arx-breadcrumbs>
|
||||
|
||||
<div class="topic-container">
|
||||
<div class="thread-container">
|
||||
<div class="header">
|
||||
<span>${this.title || 'Loading topic...'}</span>
|
||||
<span>${this.title || 'Loading thread...'}</span>
|
||||
</div>
|
||||
|
||||
<div class="posts-container">
|
||||
|
@ -218,7 +218,7 @@ export class ArborTopicView extends LitElement {
|
|||
(post) => html`
|
||||
<arx-forum-post
|
||||
id=${post.id}
|
||||
.topicId=${this.topicId}
|
||||
.threadId=${this.threadId}
|
||||
.npub=${post.npub}
|
||||
.date=${post.date}
|
||||
.content=${post.content}
|
||||
|
@ -229,7 +229,7 @@ export class ArborTopicView extends LitElement {
|
|||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<arx-button label="New Post" href="/arbor/new-post/${this.topicId}">
|
||||
<arx-button label="New Post" href="/arbor/new-post/${this.threadId}">
|
||||
New Post
|
||||
</arx-button>
|
||||
</div>
|
|
@ -26,56 +26,57 @@ export class Home extends LitElement {
|
|||
href: 'letters',
|
||||
name: 'Letters',
|
||||
color: '#FF3E96',
|
||||
icon: 'fa-solid:leaf',
|
||||
icon: 'arx:letters',
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
href: 'messages',
|
||||
name: 'Murmur',
|
||||
href: 'howl',
|
||||
name: 'Howl',
|
||||
color: '#00CD66',
|
||||
icon: 'fa-solid:seedling',
|
||||
icon: 'arx:howl',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
href: 'beacon',
|
||||
name: 'Beacon',
|
||||
href: 'calendar',
|
||||
name: 'Calendar',
|
||||
color: '#FF8C00',
|
||||
icon: 'fa-solid:sun',
|
||||
icon: 'arx:calendar',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
href: 'arbor',
|
||||
name: 'Arbor',
|
||||
color: '#FF4040',
|
||||
icon: 'fa-solid:tree',
|
||||
icon: 'arx:arbor',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
href: 'grove',
|
||||
name: 'Grove',
|
||||
href: 'market',
|
||||
name: 'Market',
|
||||
color: '#9370DB',
|
||||
icon: 'fa-solid:store-alt',
|
||||
icon: 'arx:market',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
href: 'wallet',
|
||||
name: 'Wallet',
|
||||
color: '#1E90FF',
|
||||
icon: 'fa-solid:spa',
|
||||
icon: 'arx:wallet',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
href: 'oracle',
|
||||
name: 'Oracle',
|
||||
color: '#FFD700',
|
||||
icon: 'bxs:landscape',
|
||||
href: 'pool',
|
||||
name: 'Pool',
|
||||
color: '#7aD700',
|
||||
icon: 'arx:pool',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
href: 'settings',
|
||||
name: 'Settings',
|
||||
color: '#7B68EE',
|
||||
icon: 'fa-solid:tools',
|
||||
icon: 'arx:settings',
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
|
|
@ -3,8 +3,8 @@ import '@components/Sidebar';
|
|||
import '@routes/404Page';
|
||||
import '@routes/Arbor/Home';
|
||||
import '@routes/Arbor/NewPost';
|
||||
import '@routes/Arbor/NewTopic';
|
||||
import '@routes/Arbor/TopicView';
|
||||
import '@routes/Arbor/NewThread';
|
||||
import '@routes/Arbor/ThreadView';
|
||||
import '@routes/Calendar';
|
||||
import '@routes/Home';
|
||||
import '@routes/Profile';
|
||||
|
@ -16,6 +16,7 @@ import type { NDKUserProfile } from '@nostr-dev-kit/ndk';
|
|||
import { spread } from '@open-wc/lit-helpers';
|
||||
import { LitElement, css } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { classMap } from 'lit/directives/class-map.js';
|
||||
import { keyed } from 'lit/directives/keyed.js';
|
||||
import { type Ref, createRef, ref } from 'lit/directives/ref.js';
|
||||
import { when } from 'lit/directives/when.js';
|
||||
|
@ -42,7 +43,7 @@ export default class EveRouter extends LitElement {
|
|||
component: literal`arx-eve-home`,
|
||||
},
|
||||
{
|
||||
pattern: 'beacon',
|
||||
pattern: 'calendar',
|
||||
params: {},
|
||||
component: literal`arx-calendar-route`,
|
||||
},
|
||||
|
@ -57,20 +58,25 @@ export default class EveRouter extends LitElement {
|
|||
component: literal`arx-arbor-home`,
|
||||
},
|
||||
{
|
||||
pattern: 'arbor/new-topic/:categoryId',
|
||||
pattern: 'arbor/new-thread/:categoryId',
|
||||
params: {},
|
||||
component: literal`arx-arbor-topic-creator`,
|
||||
component: literal`arx-arbor-thread-creator`,
|
||||
},
|
||||
{
|
||||
pattern: 'arbor/topics/:topicId',
|
||||
pattern: 'arbor/threads/:threadId',
|
||||
params: {},
|
||||
component: literal`arx-arbor-topic-view`,
|
||||
component: literal`arx-arbor-thread-view`,
|
||||
},
|
||||
{
|
||||
pattern: 'arbor/new-post/:topicId',
|
||||
pattern: 'arbor/new-post/:threadId',
|
||||
params: {},
|
||||
component: literal`arx-arbor-post-creator`,
|
||||
},
|
||||
{
|
||||
pattern: 'howl',
|
||||
params: {},
|
||||
component: literal`arx-howl-route`,
|
||||
},
|
||||
{
|
||||
pattern: 'settings',
|
||||
params: {},
|
||||
|
@ -112,6 +118,9 @@ export default class EveRouter extends LitElement {
|
|||
@state()
|
||||
private userNpub = '';
|
||||
|
||||
@state()
|
||||
private sidebarVisible = true;
|
||||
|
||||
static override styles = css`
|
||||
:host {
|
||||
position: fixed;
|
||||
|
@ -121,8 +130,13 @@ export default class EveRouter extends LitElement {
|
|||
height: 100%;
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-columns: 100px 1fr;
|
||||
grid-template-columns: auto 1fr;
|
||||
overflow: hidden;
|
||||
transition: grid-template-columns 0.3s ease;
|
||||
}
|
||||
|
||||
:host([sidebar-hidden]) {
|
||||
grid-template-columns: 0 1fr;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
|
@ -173,6 +187,21 @@ export default class EveRouter extends LitElement {
|
|||
arx-sidebar {
|
||||
grid-column: 1;
|
||||
grid-row: 1 / span 2;
|
||||
transition: width 0.3s ease, opacity 0.3s ease, padding 0.3s ease, transform 0.3s ease;
|
||||
width: 100px;
|
||||
opacity: 1;
|
||||
overflow: hidden;
|
||||
background: var(--color-base-100);
|
||||
border-right: 1px solid var(--color-base-300);
|
||||
}
|
||||
|
||||
arx-sidebar.hidden {
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
padding: 0;
|
||||
pointer-events: none;
|
||||
transform: translateX(-20px);
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.window-content {
|
||||
|
@ -187,6 +216,7 @@ export default class EveRouter extends LitElement {
|
|||
transition: var(--transition);
|
||||
backface-visibility: hidden;
|
||||
filter: blur(0px);
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.window-content::after {
|
||||
|
@ -218,6 +248,44 @@ export default class EveRouter extends LitElement {
|
|||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
.sidebar-toggle {
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 100px;
|
||||
transform: translateY(-50%);
|
||||
z-index: 1000;
|
||||
background: color-mix(in srgb, var(--color-primary) 70%, transparent);
|
||||
backdrop-filter: blur(5px);
|
||||
color: var(--color-base);
|
||||
border: none;
|
||||
width: 20px;
|
||||
height: 50px;
|
||||
border-top-right-radius: var(--radius-btn);
|
||||
border-bottom-right-radius: var(--radius-btn);
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
transition: all var(--transition);
|
||||
box-shadow: 2px 0 5px rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
:host([sidebar-hidden]) .sidebar-toggle {
|
||||
left: 0;
|
||||
border-top-left-radius: var(--radius-btn);
|
||||
border-bottom-left-radius: var(--radius-btn);
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.sidebar-toggle:hover {
|
||||
/* background: var(--color-primary-focus); */
|
||||
background: color-mix(in srgb, var(--color-primary) 90%, transparent);
|
||||
}
|
||||
`;
|
||||
|
||||
constructor() {
|
||||
|
@ -233,6 +301,7 @@ export default class EveRouter extends LitElement {
|
|||
if (this.ccnSetup) {
|
||||
this.loadUserProfile();
|
||||
}
|
||||
this.updateSidebarAttribute();
|
||||
}
|
||||
|
||||
override disconnectedCallback(): void {
|
||||
|
@ -403,11 +472,36 @@ export default class EveRouter extends LitElement {
|
|||
}
|
||||
}
|
||||
|
||||
private toggleSidebar() {
|
||||
this.sidebarVisible = !this.sidebarVisible;
|
||||
this.updateSidebarAttribute();
|
||||
}
|
||||
|
||||
private updateSidebarAttribute() {
|
||||
if (this.sidebarVisible) {
|
||||
this.removeAttribute('sidebar-hidden');
|
||||
} else {
|
||||
this.setAttribute('sidebar-hidden', '');
|
||||
}
|
||||
}
|
||||
|
||||
override render() {
|
||||
if (!this.ccnSetup) return this.renderSetup();
|
||||
|
||||
return html`
|
||||
<button class="sidebar-toggle" @click=${this.toggleSidebar} title=${when(
|
||||
this.sidebarVisible,
|
||||
() => 'Hide Sidebar',
|
||||
() => 'Show Sidebar',
|
||||
)}>
|
||||
${when(
|
||||
this.sidebarVisible,
|
||||
() => html`<iconify-icon icon="mdi:chevron-left"></iconify-icon>`,
|
||||
() => html`<iconify-icon icon="mdi:chevron-right"></iconify-icon>`,
|
||||
)}
|
||||
</button>
|
||||
<arx-sidebar
|
||||
class=${classMap({ hidden: !this.sidebarVisible })}
|
||||
.currentPath=${this.currentPath}
|
||||
.userNpub=${this.userNpub}
|
||||
.userProfile=${this.userProfile}
|
||||
|
@ -422,8 +516,8 @@ export default class EveRouter extends LitElement {
|
|||
@go-forward=${this.goForward}
|
||||
title="Eve"
|
||||
></arx-header>
|
||||
<div class="window ${this.currentRoute.pattern === 'home' ? 'hide-overflow' : ''}">
|
||||
<div ${ref(this.windowContentRef)} class="window-content ${this.isTransitioning ? 'transitioning' : ''}">
|
||||
<div class=${classMap({ window: true, 'hide-overflow': this.currentRoute.pattern === 'home' })}>
|
||||
<div ${ref(this.windowContentRef)} class=${classMap({ 'window-content': true, transitioning: this.isTransitioning })}>
|
||||
${when(
|
||||
this.isTransitioning,
|
||||
() => html`<arx-loading-view></arx-loading-view>`,
|
||||
|
|
|
@ -29,7 +29,8 @@
|
|||
"@routes/*": ["./src/routes/*"],
|
||||
"@styles/*": ["./src/styles/*"],
|
||||
"@components/*": ["./src/components/*"],
|
||||
"@widgets/*": ["./src/components/Widgets/*"]
|
||||
"@widgets/*": ["./src/components/Widgets/*"],
|
||||
"@assets/*": ["./src/assets/*"],
|
||||
}
|
||||
},
|
||||
"include": ["./src/**/*"]
|
||||
|
|