Compare commits

...

730 Commits

Author SHA1 Message Date
e3b4be26ce chore(git): merge pull request #211 from issues/203-nsfw-redirect into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m8s
Reviewed-on: #211
2025-01-30 18:17:05 +00:00
en
0019713bf9 fix(nsfw): show popup if visiting nsfw post, redirect on cancel to home
Closes #203
2025-01-30 19:13:07 +01:00
en
44d7f57f0a fix(nsfw): close will trigger as if clicking no 2025-01-30 19:08:54 +01:00
7331866479 chore(git): merge pull request#210 from issues/140-zap-split-ux into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m7s
Reviewed-on: #210
2025-01-30 17:27:26 +00:00
en
c1b4dac5a3 feat(zaps): show profile image on qr if available 2025-01-30 18:21:55 +01:00
en
9287822c64 fix(zaps): zap split UX
Closes #140
2025-01-30 18:01:37 +01:00
1ef86470fc Update src/assets/games/Games_Steam4.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m10s
2025-01-30 15:47:52 +00:00
903cf30377 Update src/assets/games/Games_SteamManual.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m6s
2025-01-30 15:44:00 +00:00
d99f2941cb Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m3s
2025-01-30 15:36:38 +00:00
88a1bdfdd3 Update src/assets/games/Games_Steam2.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m0s
2025-01-30 14:49:28 +00:00
f51dde697a Update src/assets/games/Games_Steam2.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m8s
2025-01-30 14:44:55 +00:00
8747633104 Update src/assets/games/Games_Steam3.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m6s
2025-01-30 14:32:26 +00:00
c279e9ee87 Update src/assets/games/Games_Steam4.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m8s
2025-01-30 14:18:32 +00:00
7f3d54f10c Update src/assets/games/Games_Steam5.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m7s
2025-01-30 14:15:06 +00:00
c217ed15b8 chore(git): merge pull request #209 from fixes/adv-comments-30-1-25 into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m9s
Reviewed-on: #209
2025-01-30 13:43:13 +00:00
en
1226c60917 refactor(comments): move style from react to css 2025-01-30 14:42:12 +01:00
en
94eb88bdd3 fix(comments): clear input on publish 2025-01-30 14:28:42 +01:00
en
bf18d61f1f fix(comments): publish and discovery interaction, add discovery to popup 2025-01-30 14:20:44 +01:00
en
a92d1da7ad fix(comments): move depth calc, parent and root fetch to hook, main post leads to root, add small spinners 2025-01-30 11:12:03 +01:00
3804644635 Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m8s
2025-01-30 09:26:15 +00:00
en
9b9e97b40e fix(comments): comment out repost button 2025-01-30 09:44:17 +01:00
en
b03fa6e55d fix(comments): show new line in content p 2025-01-30 09:40:12 +01:00
en
8430a55beb fix(comments): cache first comments fetch 2025-01-30 09:34:34 +01:00
99c80855d2 Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m8s
2025-01-29 21:43:34 +00:00
b918c875a4 chore(git): merge pull request #207 from feat/130-adv-comments into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m5s
Reviewed-on: #207
2025-01-29 20:53:32 +00:00
cb7c10384a Update src/assets/games/Games_SteamManual.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m10s
2025-01-29 20:34:52 +00:00
en
905b3ee5e4 feat(comments): add popup, types, and utils, split components 2025-01-29 21:24:44 +01:00
en
11f4281067 feat(ndk): use ndk nip07 signer 2025-01-29 21:24:44 +01:00
en
97b44a55f2 feat(reply): publish new reply with ndkevent, fetch kind 1 and 1111 2025-01-29 21:24:44 +01:00
612524741b Upload files to "src/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m15s
2025-01-29 17:04:19 +00:00
8189149288 Upload files to "src/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m8s
2025-01-29 15:41:47 +00:00
f51018befb Update src/assets/games/Games_Steam2.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m0s
2025-01-28 14:01:20 +00:00
e213a61f56 Update src/assets/games/Games_Steam3.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m7s
2025-01-28 13:53:22 +00:00
0612c24dee Update src/assets/games/Games_Steam4.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m4s
2025-01-28 13:46:40 +00:00
df21fa0346 Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m7s
2025-01-28 13:38:51 +00:00
4d240554ce Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m7s
2025-01-28 13:30:17 +00:00
1d5550190b Update src/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m13s
2025-01-28 09:55:15 +00:00
6dafda7071 Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m10s
2025-01-28 01:09:00 +00:00
8dd73919f5 chore(git): merge pull request #206 from issues/205-zap-tipping-broken into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m8s
Reviewed-on: #206
2025-01-27 13:42:28 +00:00
en
670b981b05 fix(zap): add timeout and hide loading when done 2025-01-27 14:24:48 +01:00
en
b41676e4a9 fix(pubkey): handle error thrown by canceling pubkey fetch 2025-01-27 13:20:44 +01:00
87244dda32 Update src/assets/games/Games_Steam3.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m6s
2025-01-27 12:03:48 +00:00
79020fbcde Update src/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m3s
2025-01-27 12:01:16 +00:00
en
02897ea72a fix(wot): site and mine filter condition updated
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m6s
2025-01-27 12:28:33 +01:00
en
1bac85f6f0 fix(wot): site and mine filter condition updated
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m11s
Fixes #204
2025-01-27 12:23:45 +01:00
en
144c24b254 chore(deps): update ndk version
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m0s
2025-01-24 11:09:22 +01:00
dbbbba075b Update src/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m7s
2025-01-23 22:47:03 +00:00
2dbad13c5d Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m5s
2025-01-22 19:47:12 +00:00
4f27b4aafa Update src/assets/games/Games_Steam2.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m0s
2025-01-22 19:45:47 +00:00
15be873136 Update src/assets/games/Games_Steam3.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m7s
2025-01-22 19:44:25 +00:00
73e17c09d5 Update src/assets/games/Games_SteamManual.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m11s
2025-01-22 19:30:28 +00:00
ed348533ad chore(git): merge pull request #202 from extra/112-mods-fields into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m2s
Reviewed-on: #202
2025-01-22 16:51:04 +00:00
en
5a6327fb73 feat(mod): add permissions and details
Close #112
2025-01-22 17:47:38 +01:00
99d7dbe89d Update src/components/ModForm.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
2025-01-21 20:40:40 +00:00
e11e6f1fb2 Update src/styles/downloads.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2025-01-21 17:48:06 +00:00
83f15b4b69 Update src/pages/mod/index.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
2025-01-21 17:45:09 +00:00
enes
787231ce0d fix(download): reset on new fields
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2025-01-21 17:36:50 +01:00
1e9c24e013 chore(git): merge pull request #201 from extra/196-mods-refactor into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 51s
Reviewed-on: #201
2025-01-21 16:26:50 +00:00
enes
d75e3508ea feat(download): add download details popup 2025-01-21 17:19:35 +01:00
enes
4bec281ea0 feat(download): add media url 2025-01-21 16:27:03 +01:00
enes
3f141ed58b feat(download): add title and remove show more links 2025-01-21 15:47:05 +01:00
beed4dabe0 chore(git): merge pull request #200 from extra/188-only-moderated-filter into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
Reviewed-on: #200
2025-01-21 10:55:57 +00:00
enes
e5dd28c23c feat(filter): add only moderation filter to mods
Closes #188
2025-01-21 11:54:23 +01:00
09dda039da Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 59s
2025-01-20 11:50:10 +00:00
f53eeeece1 Update src/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 58s
2025-01-19 21:08:29 +00:00
0a2e6a327a Upload files to "src/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 59s
2025-01-17 21:31:21 +00:00
9d7c57224b Upload files to "src/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m2s
2025-01-17 21:03:05 +00:00
096c16f0f6 Upload files to "src/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m3s
2025-01-17 20:50:05 +00:00
85116e0e9f Upload files to "src/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m4s
2025-01-17 20:38:15 +00:00
enes
d00b142231 fix(download): add checkUrlForFile timeout
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m5s
Fix #164
2025-01-17 12:51:52 +01:00
enes
32f35ebcca revert: scan link early return
Refs: fbde15e075d92bf4070b75f5decc554ca18db02e.
2025-01-17 12:49:34 +01:00
enes
fbde15e075 fix(download): show notice and return earily if missing malware scan linky
All checks were successful
Release to Staging / build_and_release (push) Successful in 57s
Fix #164
2025-01-17 12:29:01 +01:00
enes
599c29b4c4 fix(download): warn on same scan and url link
All checks were successful
Release to Staging / build_and_release (push) Successful in 59s
Fix #164
2025-01-17 10:24:19 +01:00
enes
a9c5c3d18a build(download): remove unused variable
All checks were successful
Release to Staging / build_and_release (push) Successful in 57s
2025-01-16 19:45:02 +01:00
enes
dc96783231 fix(download): remove reachable and fix typo
Some checks failed
Release to Staging / build_and_release (push) Failing after 26s
Fix #164
2025-01-16 19:42:52 +01:00
enes
18a9744f96 fix(download): scan link detection
All checks were successful
Release to Staging / build_and_release (push) Successful in 58s
Fix #164
2025-01-16 19:19:34 +01:00
02c4cb52b1 chore(git): merge pull request #199 from extra/164-scan-notice into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 58s
Reviewed-on: #199
2025-01-16 17:18:04 +00:00
enes
f335640ec5 feat(download): add malware scan notice
Closes #164
2025-01-16 18:16:36 +01:00
595360c88c chore(git): merge pull request #198 from extra/161-download-link-notice into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 59s
Reviewed-on: #198
2025-01-16 14:03:32 +00:00
enes
3d7671c303 feat(download): show notice download url leads to another website
Closes #161
2025-01-16 15:01:44 +01:00
e85b33d95d chore(git): merge pull request #197 from extra/154-blocked-warning into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m4s
Reviewed-on: #197
2025-01-16 12:07:46 +00:00
enes
a835996db7 refactor(mutelist): no need to check admin specific list for isBlocked 2025-01-16 13:06:16 +01:00
enes
8c10a467be fix(router): revalited loaders on auth 2025-01-16 12:57:13 +01:00
enes
cdf23c7fac feat(mutelist): add post warnings to blog/mod
+ fix: block/unblock string
2025-01-16 12:56:46 +01:00
enes
177d2fb2ac fix(zap): hide split button if author has no ln address
All checks were successful
Release to Staging / build_and_release (push) Successful in 59s
Fixes #183
2025-01-16 11:15:17 +01:00
425f4f9cd4 chore(git): merge pull request #195 from issues/190-missing-repost-tags into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 58s
Reviewed-on: #195
2025-01-15 19:22:06 +00:00
enes
d079c1a564 fix(home): add repost tag to latest mods 2025-01-15 20:20:07 +01:00
enes
e0394ab0fd feat: add repostList hook 2025-01-15 20:19:02 +01:00
df5ebdb37f chore(git): merge pull request #194 from issues/179-only-logged-in-report into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m0s
Reviewed-on: #194
2025-01-15 18:33:25 +00:00
enes
4832c46548 fix(report): available only to logged in users
Closes #179
2025-01-15 19:32:06 +01:00
a97159119d chore(git): merge pull request #193 from issues/183-ln-button into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 59s
Reviewed-on: #193
2025-01-15 18:12:08 +00:00
enes
0f2af47087 fix(profile): check for empty strings for ln
Closes #183
2025-01-15 19:10:48 +01:00
138de5a2ae chore(git): merge pull request #192 from 182-old-mods-edit into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m4s
Reviewed-on: #192
2025-01-15 16:07:43 +00:00
enes
9931f4ec0d feat(mods): editor error handling 2025-01-15 17:05:35 +01:00
enes
094b7349b3 feat(editor): add diffsourcePlugin 2025-01-15 17:05:12 +01:00
enes
3f80f9e0ce refactor: remove unused package 2025-01-15 17:04:09 +01:00
2949444c8a chore(git): merge pull request #191 from 186-try-again-mod-publish into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 56s
Reviewed-on: #191
2025-01-15 12:24:36 +00:00
enes
ce27515bfe feat: spinner with timer 2025-01-14 17:48:15 +01:00
enes
5d479102d4 feat: mod submission try again 2025-01-14 17:23:47 +01:00
enes
a247f05f6e refactor: publish then catch to try catch 2025-01-14 17:16:22 +01:00
enes
dddabbc1d1 feat(errors): timeout error and set prototype 2025-01-09 17:20:59 +01:00
df27451c46 chore(git): merge pull request #187 from 166-caching-fields into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 57s
Reviewed-on: #187
2025-01-09 13:45:45 +00:00
enes
17e9cad9e3 refactor(mods): use lodash clone 2025-01-09 14:30:54 +01:00
enes
5fad718356 feat(cache): clear cache on succesful publish 2025-01-09 14:17:12 +01:00
enes
c95af90b28 fix(mod): set original author field to optional 2025-01-09 14:04:45 +01:00
enes
60773ec446 feat(cache): add blog cache, blog to controlled inputs 2025-01-09 13:21:49 +01:00
enes
30a87cc347 feat(cache): add mod cache 2025-01-09 13:20:44 +01:00
enes
b3ade8e1d2 style(categories): prettier formatting 2025-01-09 13:20:11 +01:00
enes
b60659eebf feat(cache): add simple localcache hook 2025-01-09 13:19:17 +01:00
enes
f214d66799 refactor(storage): util func moved 2025-01-09 13:18:20 +01:00
enes
ed3585f9c8 fix(mod): reset form 2025-01-08 13:59:35 +01:00
a278800025 Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
2025-01-08 00:37:04 +00:00
2224403742 Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m1s
2025-01-08 00:32:29 +00:00
649adc609b Update src/assets/games/Games_Steam2.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 56s
2025-01-07 20:18:50 +00:00
397ab457e4 Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m0s
2025-01-07 20:15:09 +00:00
7854b5480c Update src/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 57s
2025-01-07 20:06:28 +00:00
cda8e1c210 chore(git): merge pull request #185 from fixes-1-7 into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m2s
Reviewed-on: #185
2025-01-07 20:02:56 +00:00
enes
33b8565051 feat(image): add spinner while uploading 2025-01-07 21:01:11 +01:00
enes
d4d7dde1ab fix(mod): redirect on edit if user is not original author 2025-01-07 20:40:59 +01:00
6170050070 chore(git): merge pull request #184 from 106-direct-image-upload into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m4s
Reviewed-on: #184
2025-01-07 18:33:55 +00:00
enes
8205d4ac3e refactor: remove comment 2025-01-07 19:29:16 +01:00
enes
080b231f5c chore(git): merge staging into 106-direct-image-upload 2025-01-07 19:25:40 +01:00
e
8f97161eb2 chore: code cleanup 2025-01-07 16:39:23 +01:00
e
7244591d34 fix(image): bad image url input field name 2025-01-07 14:31:19 +01:00
enes
0026f4d751 feat(image): multiple files upload 2025-01-07 12:40:27 +01:00
enes
9fd1aca99c feat(image): use image upload field in blog 2025-01-07 10:04:37 +01:00
enes
4c410be9ba feat(image): use image upload field 2025-01-07 09:49:02 +01:00
enes
b33015cbaf feat(image): add direct image upload components 2025-01-07 09:48:30 +01:00
enes
0b2d488bbe feat(image): add controller, media services and error handling 2025-01-07 09:46:28 +01:00
enes
e3aab5a5dc build: bump to es2022 2025-01-07 09:43:46 +01:00
enes
31cd625886 feat(image): add dropzone package 2025-01-07 09:41:20 +01:00
efa16433e8 Update src/pages/about.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m1s
2025-01-05 20:32:44 +00:00
2dccadd670 Update src/pages/about.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 58s
2025-01-05 20:26:19 +00:00
331ac285ce Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m3s
2025-01-05 14:35:16 +00:00
cff9bbd8d0 Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
2025-01-05 13:16:39 +00:00
4527c1c154 Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 56s
2025-01-03 16:11:25 +00:00
35176302e5 Update src/assets/games/Games_Steam4.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m1s
2025-01-03 11:26:38 +00:00
08884cc066 Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m3s
2025-01-02 23:25:40 +00:00
50d982a3a8 Update src/components/ModForm.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 57s
2025-01-02 21:43:09 +00:00
enes
e15307be3b fix(mod): loading mod edge case
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m2s
Close #175 - Add timeout to requests and try again button
2025-01-01 16:16:39 +01:00
c6c2013f1e Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
2025-01-01 12:19:03 +00:00
aa9f1015e1 Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 58s
2025-01-01 12:17:25 +00:00
a46aaa9e55 Update src/assets/categories/categories.json
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m7s
2024-12-31 15:50:32 +00:00
50f9800935 chore(git): merge pull request #178 from fixes-12-26 into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 56s
Reviewed-on: #178
2024-12-27 10:22:58 +00:00
enes
5449591b6e fix: blogs loading for reported nprofile 2024-12-26 17:21:19 +01:00
enes
1c4a9c8586 fix: failing links in footer, backup and supporters pages prep
Partially #142
2024-12-26 17:20:21 +01:00
enes
ad68ba8e84 fix: add fallback for usersPubkey in loaders 2024-12-26 16:46:40 +01:00
enes
ad3d069ad5 refactor: deps cleanup 2024-12-26 16:42:11 +01:00
enes
4bf9787660 refactor: remove user metadata console errors 2024-12-26 16:40:40 +01:00
enes
037b81c49e fix(games): add no games found in search
Closes #141
2024-12-26 16:39:09 +01:00
7a5639b8cf Upload files to "src/assets/img"
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
2024-12-24 19:36:11 +00:00
enes
2440620328 fix(viewer): remove double sanitize, fix yt directive
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m1s
2024-12-24 20:26:38 +01:00
73cec02ee5 chore(git): merge pull request #176 from feedback-11-24 into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
Reviewed-on: #176
2024-12-24 18:38:20 +00:00
enes
130be2567d fix(editor): feedback updates 2024-12-24 19:36:58 +01:00
fdbb64d360 chore(git): merge pull request #174 from 170-editor-update into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m1s
Reviewed-on: #174
2024-12-24 16:13:48 +00:00
enes
cbd53852a5 fix(viewer): image bug 2024-12-24 17:09:01 +01:00
enes
137cd95c4e fix(viewer): allow iframe, only from custom yt directive 2024-12-24 17:03:18 +01:00
enes
a6ed390fad fix(viewer): yt directive 2024-12-24 16:36:29 +01:00
enes
0760a3e81e refactor(viewer): table styling 2024-12-24 16:33:43 +01:00
enes
026dae65bb refactor(viewer): add yt directive renderer with marked-directive pkg 2024-12-24 16:07:00 +01:00
enes
e40ec6c5aa refactor(editor): update yt delete button 2024-12-24 14:15:36 +01:00
enes
e384bae945 refactor(editor): override image and link dialog 2024-12-24 14:00:04 +01:00
enes
3080b376aa refactor: remove tiptap 2024-12-23 20:24:11 +01:00
enes
1b1aa4289a refactor: add viewer, swap editor, and refactor submitMod page 2024-12-23 20:21:06 +01:00
0b7d88a18c Update src/assets/categories/categories.json
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-12-22 23:42:08 +00:00
dd081ed1a2 Update src/assets/categories/categories.json
Some checks failed
Release to Staging / build_and_release (push) Failing after 24s
2024-12-22 23:35:30 +00:00
a93c113701 Update src/assets/categories/categories.json
Some checks failed
Release to Staging / build_and_release (push) Failing after 28s
2024-12-22 23:25:09 +00:00
3ee868e613 Update src/assets/categories/categories.json
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
2024-12-22 23:12:46 +00:00
c448e3be73 Update src/components/Filters/CategoryFilterPopup.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 53s
2024-12-22 22:46:04 +00:00
d823d3f007 Update src/components/Filters/CategoryFilterPopup.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-12-22 22:42:58 +00:00
c973bdd436 adjusted placeholder text
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
2024-12-22 22:35:46 +00:00
777cd2a7c7 adjusted placeholder text
Some checks failed
Release to Staging / build_and_release (push) Failing after 20s
2024-12-22 22:33:36 +00:00
enes
70c15dceb0 fix(category): show indeterminate on linked category parents 2024-12-18 12:48:47 +01:00
enes
52f1735d40 fix(ndk): disable debug
debug mode should be only enabled locally or on proper env
2024-12-18 12:48:10 +01:00
enes
b5ba87443c refactor(blog): replace editor 2024-12-18 12:47:09 +01:00
fd9cd80bc1 chore(git): merge pull request #172 from fixes-12-16 into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
Reviewed-on: #172
2024-12-16 12:20:51 +00:00
enes
615b39a8d8 fix(mod): add keys to categories list 2024-12-16 13:14:05 +01:00
enes
3f237ab2af refactor(category): linking category update 2024-12-16 13:10:34 +01:00
b53c759251 Update src/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-12-16 12:00:36 +00:00
579063e073 Update src/pages/about.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
2024-12-13 15:35:32 +00:00
c93850d63c Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 53s
2024-12-13 10:42:09 +00:00
3c026bc0a8 Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 52s
2024-12-13 10:34:47 +00:00
fbae220e7d Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-12-13 10:33:12 +00:00
d1cc49bae2 Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-12-13 10:28:06 +00:00
enes
ba60b7f1d4 fix(mod): submit and edit reset bug
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-12-13 10:44:00 +01:00
1076124356 Update src/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 53s
2024-12-13 00:20:14 +00:00
cc788a433b Update src/assets/games/Games_Steam5.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-12-13 00:15:58 +00:00
23e05b30ab Update src/assets/games/Games_Steam4.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 53s
2024-12-13 00:12:15 +00:00
e67c8ae440 Update src/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-12-13 00:05:32 +00:00
b27711ed6b chore(git): merge pull request #171 from 116-categories into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
Reviewed-on: #171
2024-12-12 16:37:38 +00:00
enes
ff0eb2cddf refactor: remove unused variable 2024-12-12 17:29:48 +01:00
enes
49b3ce4205 fix(category): top level category bug 2024-12-12 17:19:58 +01:00
enes
b71e503c8f feat(blog): clear form changes with confirm alert popup 2024-12-12 16:48:18 +01:00
enes
5877912cea feat(mod): clear form changes with confirm alert popup 2024-12-12 16:19:08 +01:00
enes
96fe99669b feat(categories): autocomplete user and existing categories, new LTags option to add to user's list 2024-12-12 14:20:57 +01:00
enes
bac48a4486 feat(category): indeterminate state, parent marking 2024-12-11 16:26:16 +01:00
enes
b9d6820405 feat(category): check if user defined already exists, remove duplicates 2024-12-11 14:36:07 +01:00
enes
f7f8778707 feat(category): user hierarchy, fix filter 2024-12-11 14:03:33 +01:00
enes
535aabe4a3 build(audit): update packages 2024-12-11 14:02:10 +01:00
enes
127c1fd8a6 fix(category): use hierarchy links, visual indicator for link 2024-12-05 21:03:00 +01:00
enes
cbcb82e779 fix(storage): memoize hook values after JSON parsing 2024-12-05 20:51:02 +01:00
enes
41cfc57cf9 fix(category): open in new tab, require game select 2024-12-05 13:37:30 +01:00
enes
8d9bbbc7a5 feat(category): category filter popup 2024-12-05 13:02:04 +01:00
enes
ecbe839b30 chore(git): merge branch '137-168-alert-popups' into 116-categories 2024-12-04 14:20:03 +01:00
enes
1454929710 feat(category): initial filter prep 2024-12-04 14:17:00 +01:00
enes
836d5b76e1 feat(category): dynamic dropdown item height 2024-12-04 14:17:00 +01:00
enes
4bf84cd9a6 fix(mod): remove debug code 2024-12-04 14:17:00 +01:00
enes
cd5e6dcd8f feat(categories): link c to games and split input on > 2024-12-04 14:17:00 +01:00
enes
cb94f0ced6 fix(search): remove test categoriesh 2024-12-04 14:17:00 +01:00
enes
3b2dce54c5 feat: initial categories 2024-12-04 14:17:00 +01:00
enes
71f934129c refactor: use local storage instead of session for nsfw preference 2024-12-03 17:32:55 +01:00
enes
1ee56ba91a feat: generic alert popup and nsfw popup confirmation 2024-12-03 15:09:40 +01:00
8c6046ac6d Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-12-02 23:27:35 +00:00
6680acee85 Update src/styles/tags.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 56s
2024-12-02 14:49:41 +00:00
f5d03263e7 chore(git): merge pull request #162 from 107-143-refactoring-mod-and-repost into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
Reviewed-on: #162
2024-11-28 16:33:14 +00:00
enes
6246dece84 refactor(mods): use loader to preload lists 2024-11-28 17:21:35 +01:00
enes
f61c32c16a fix: comments 2024-11-28 17:13:53 +01:00
enes
b1d578c329 feat: add filtering, split mods and blog filters 2024-11-28 16:47:10 +01:00
enes
376164cbf4 refactor: add repost tag if missing 2024-11-27 19:56:19 +01:00
enes
35cedba3db feat: add repost and original author fields 2024-11-27 17:17:54 +01:00
enes
6c0ac7d59d fix: mod edit route 2024-11-27 14:42:48 +01:00
enes
c55dc03382 refactor: mod page, add generic report popup, repost option 2024-11-27 12:33:09 +01:00
3d5d59a64d Update src/assets/games/Games_SteamManual.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
2024-11-26 16:01:02 +00:00
c38d14a633 Update src/pages/mod/index.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 53s
2024-11-22 12:08:34 +00:00
38bd029687 Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-11-21 21:03:28 +00:00
f29a2634fd Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-11-21 20:58:39 +00:00
61a94e5358 Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 53s
2024-11-21 20:51:21 +00:00
f05f0dc1ea new css and adjusted current ones
All checks were successful
Release to Staging / build_and_release (push) Successful in 52s
2024-11-21 20:47:13 +00:00
c9ceed6c0f adjustments to modify the look of 'view' button on mod post body (now 'read full')
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
2024-11-21 20:45:27 +00:00
enes
a241f90269 fix(settings): load relays for new npubs
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
Closes #159
2024-11-20 16:14:39 +01:00
enes
a486e5a383 refactor: remove a few console logs
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-11-20 13:54:35 +01:00
enes
8d20678c75 fix(ndk): dont create NDKRelaySet from empty arrays
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-11-20 13:50:49 +01:00
enes
994382f39c fix(wot): add exclude and fix wot dropdown
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-11-20 12:27:41 +01:00
54ab35e78c chore(git): merge pull request #158 from wot-updates-11-19 into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
Reviewed-on: #158
Reviewed-by: s <s@noreply.git.nostrdev.com>
2024-11-19 16:27:39 +00:00
enes
02a81213a2 refactor(wot): update redux wot level if admin wot npub changes level 2024-11-19 16:23:42 +01:00
enes
8b5b9a6e30 refactor(wot): ignore filter selection based on ruleset 2024-11-19 13:11:50 +01:00
enes
2936d6d53b refactor(wot): add Trust label to wot filter 2024-11-19 13:09:12 +01:00
enes
4b6926b0b9 refactor(wot): single loop only 2024-11-19 13:08:27 +01:00
990f91c0a6 chore(git): merge pull request 'fix(wot): profile pref user wot' (#157) from 156-wot-level into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
Reviewed-on: #157
2024-11-19 09:07:42 +00:00
enes
81d012b0cb refactor(settings): add readOnly to remove warnings for wip checkboxes 2024-11-19 10:01:02 +01:00
1b960e5f02 Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 52s
2024-11-19 00:17:39 +00:00
daniyal
4b6db36646 fix: include authors in wotLevel filter
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
2024-11-19 00:57:32 +05:00
s
0b2de940d0 Merge pull request 'fix: improve wot logic' (#155) from wot-fixes into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m4s
Reviewed-on: #155
2024-11-18 18:25:52 +00:00
s
cea403d212 Merge branch 'staging' into wot-fixes 2024-11-18 18:23:50 +00:00
daniyal
4f8cac6eee fix: improve wot logic
Improve data structure for storing WoT
Also improve WoT calculation logic
2024-11-18 23:21:05 +05:00
432d182d15 Merge pull request 'fix: update wot filter to remove mine_only for non admin users' (#152) from wot-fixes into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 53s
Reviewed-on: #152
2024-11-18 11:02:09 +00:00
enes
870262fcdc fix(filters): merge defaults and stored value
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
2024-11-18 11:52:42 +01:00
daniyal
6e4e580402 fix: update wot filter to remove mine_only for non admin users 2024-11-18 15:33:55 +05:00
f3ab7f6d6a Merge pull request 'fix: change default value for wotLevel' (#151) from wot-fixes into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
Reviewed-on: #151
Reviewed-by: enes <enes@noreply.git.nostrdev.com>
2024-11-18 10:20:44 +00:00
9a30eae749 Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
2024-11-18 09:18:04 +00:00
79ef25cb3b Update src/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 51s
2024-11-18 09:15:36 +00:00
daniyal
77c2e880f3 fix: change default value for wotLevel 2024-11-18 12:27:43 +05:00
59c1171260 Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-11-17 21:55:52 +00:00
7a5128c802 Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-11-17 21:51:57 +00:00
ebf0b5aa13 Update src/layout/footer.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-11-17 21:47:27 +00:00
e0a3b3b286 Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 52s
2024-11-17 21:46:20 +00:00
8f7a85cf0a Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-11-17 21:44:47 +00:00
56696129d6 Update src/assets/games/Games_Steam3.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
2024-11-17 21:32:33 +00:00
2de5cd52b6 Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
2024-11-17 20:55:04 +00:00
enes
33635194fc fix(workflow): remove extra quote mark and duplicate env var
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-11-15 10:25:50 +01:00
48368e469e chore(git): merge pull request #121 from wot into staging
Some checks failed
Release to Staging / build_and_release (push) Failing after 21s
Reviewed-on: #121
Reviewed-by: enes <enes@noreply.git.nostrdev.com>
2024-11-15 09:20:20 +00:00
daniyal
940f400300 Merge branch 'staging' into wot 2024-11-15 14:12:29 +05:00
11de23b7d2 Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-11-14 19:27:53 +00:00
a912e3e43c Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-11-14 19:26:11 +00:00
df0c64e2c9 chore(git): merge pull request #123 from fixes-120-117-84-104 into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 51s
Reviewed-on: #123
2024-11-14 16:02:04 +00:00
enes
2ed81c857c refactor(comments): reduce initial load wait and add discovered 2024-11-14 16:50:37 +01:00
enes
18bbc12776 fix(comments): add initial loading indicator 15sec 2024-11-14 14:50:28 +01:00
enes
f7d21807a4 refactor(fetch): add 1min timeout on reactions, 10sec timeout on user relay list 2024-11-14 13:56:42 +01:00
enes
cd3c7ace01 refactor(comments): add dots to comment reactions 2024-11-14 13:55:48 +01:00
enes
aaffc56424 refactor(reactions): use dots loader and block interaction while loading 2024-11-14 11:31:13 +01:00
enes
7b1a70446d feat: spinner and new dots loader 2024-11-14 11:29:01 +01:00
enes
4140438044 fix(comments): link to profile from name and npub
Closes #117
2024-11-13 16:41:15 +01:00
enes
d6bc3b8684 fix(profile): accept npub as valid profile param
Closes #120
2024-11-13 16:24:19 +01:00
296b0ad61d chore(git): merge pull request #122 from fixes-11-13 into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 52s
Reviewed-on: #122
2024-11-13 14:13:26 +00:00
enes
0f874c6bbb chore: comment typo 2024-11-13 15:12:45 +01:00
enes
297de3999c fix(comments): hide if missing aTag, force render on blog id change 2024-11-13 15:08:43 +01:00
enes
49435c2b50 fix(home): add missing spinners 2024-11-13 14:30:58 +01:00
enes
718350d2bc fix(blogs): moderation and missing aTag
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-11-13 14:09:13 +01:00
enes
3ee9e313de fix: remove unused var
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
2024-11-13 10:58:30 +01:00
enes
91830a539a fix(home): latest blogs published_at sort
Some checks failed
Release to Staging / build_and_release (push) Failing after 23s
2024-11-13 10:55:51 +01:00
enes
834701aa2c fix(home): latest mods published_at sort
All checks were successful
Release to Staging / build_and_release (push) Successful in 51s
2024-11-13 10:43:49 +01:00
0c7e61cadd Upload files to "src/assets/img"
All checks were successful
Release to Staging / build_and_release (push) Successful in 56s
2024-11-13 00:21:43 +00:00
enes
b49ae9537b fix(blog): nsfw filtering, use L tag instead nsfw
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
2024-11-12 20:15:27 +01:00
enes
352179f1d9 fix(blog): event fetch filter, editing as non-author, add errors
All checks were successful
Release to Staging / build_and_release (push) Successful in 55s
2024-11-12 14:25:34 +01:00
enes
77b6aa0d75 refactor(blog): missing blog data will not trigger loading screen 2024-11-12 14:23:58 +01:00
enes
2c31c279a1 refactor(404): more generic error page 2024-11-12 14:22:54 +01:00
enes
7be41272a0 ci(env): add new env vars
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
2024-11-12 09:58:50 +01:00
cea814676e Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-11-11 23:23:48 +00:00
bd6515ca53 Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 54s
2024-11-11 23:21:04 +00:00
fedd7dd463 Update src/layout/header.tsx
Some checks failed
Release to Staging / build_and_release (push) Failing after 27s
2024-11-11 23:19:32 +00:00
7ceb109bab Update src/layout/header.tsx
Some checks failed
Release to Staging / build_and_release (push) Failing after 20s
2024-11-11 23:13:01 +00:00
b3747e9c22 Update src/layout/header.tsx
Some checks failed
Release to Staging / build_and_release (push) Failing after 20s
2024-11-11 23:10:21 +00:00
d10b10a4fb Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-11-11 20:33:03 +00:00
daniyal
ad197fdd62 chore: necessary fixes after merging stagging branch 2024-11-11 23:10:00 +05:00
daniyal
d854622d25 Merge branch 'staging' into wot 2024-11-11 22:54:24 +05:00
daniyal
0aac63d968 feat: implemented WOT 2024-11-11 22:37:49 +05:00
2fe0a79009 chore(git): merge pull request #118 from feature/blogs into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 52s
Reviewed-on: #118
Reviewed-by: s <s@noreply.git.nostrdev.com>
2024-11-11 12:00:59 +00:00
8af73df889 Merge branch 'staging' into feature/blogs 2024-11-11 12:00:02 +00:00
0d10890ca3 Update src/assets/games/Games_Steam3.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-11-11 09:25:15 +00:00
4176b06bee Update src/assets/games/Games_Steam2.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 51s
2024-11-11 09:23:28 +00:00
enes
178876ab99 feat(blog): editing 2024-11-08 11:14:07 +01:00
enes
1c1430ba5c fix(mod): use existing uuid for edit 2024-11-08 11:02:07 +01:00
enes
2f563e1bfb feat(blog): initial editing 2024-11-07 18:05:19 +01:00
enes
f7f3764686 feat(blog): moderation and more filtering 2024-11-07 17:33:59 +01:00
f734b1447f Update src/styles/post.css 2024-11-06 17:57:16 +01:00
enes
6d6ff8ce43 feat(profile): blogs tab 2024-11-06 17:33:15 +01:00
31fd4ddfb5 Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-11-06 15:11:29 +00:00
enes
f30ac01ea6 refactor(blog): re-render body, latest and filtering 2024-11-06 13:17:13 +01:00
enes
31ee0221b7 refactor(blogs): curated filter type 2024-11-06 13:15:30 +01:00
enes
c81b2c0a1d refactor(home): fetch latest blogs in parallel w/ nsfw filter 2024-11-06 13:12:19 +01:00
enes
dae94733fa feat: add react router scroll restoration 2024-11-06 11:13:42 +01:00
enes
2f32f400dd refactor(mod): remove unused import 2024-11-05 16:44:07 +01:00
enes
1d0f27d255 feat(mod): show latest mod author blog posts 2024-11-05 16:43:08 +01:00
enes
a3bec707b0 feat(landing): show latest blog posts 2024-11-05 16:22:08 +01:00
enes
169ab37304 fix: get multiple tag values 2024-11-05 14:42:22 +01:00
enes
73a7b1c1ee feat: admin blog page pagination 2024-11-05 14:11:11 +01:00
enes
847aab29d7 feat: add admin blog page, content parse and markdown 2024-11-05 13:57:39 +01:00
enes
3717c3bfb9 feat: fetching blog data 2024-11-05 13:40:28 +01:00
enes
c2413e1bd8 fix: blog kind 2024-11-05 13:40:28 +01:00
enes
d4148ed01d feat: publishing blog, ndx in router, introduce actions 2024-11-05 13:40:28 +01:00
7f0431f8f8 Update src/styles/cardBlogs.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-11-05 11:53:16 +00:00
3a440d5479 Update src/styles/cardBlogs.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 38s
2024-11-05 11:51:57 +00:00
40dd903e97 Update src/styles/author.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 39s
2024-10-30 17:01:30 +00:00
68ebaf38bd Update src/styles/author.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-10-30 16:50:03 +00:00
36aeb53a8c Update src/styles/author.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 38s
2024-10-30 16:37:33 +00:00
enes
c25d3da64b ci(gitea): trigger release
All checks were successful
Release to Staging / build_and_release (push) Successful in 38s
2024-10-30 17:23:10 +01:00
enes
4eb8c7d653 fix: use subscription for user search
All checks were successful
Release to Staging / build_and_release (push) Successful in 38s
2024-10-30 14:59:44 +01:00
43c8ae4066 Update src/styles/cardMod.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 37s
2024-10-30 13:20:24 +00:00
49c1168bb7 Update src/styles/tags.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 39s
2024-10-30 13:17:46 +00:00
69768388e4 Upload files to "src/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-10-30 10:00:46 +00:00
80172aee07 Update package.json
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-10-29 15:45:05 +00:00
enes
0f6cd4a9bd chore: trigger release
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
2024-10-29 16:28:25 +01:00
enes
35fdf2c8b7 chore: trigger release
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-10-29 16:23:54 +01:00
e41ce32ef2 chore(git): merge pull request #105 from 52-game-page-search into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
Reviewed-on: #105
2024-10-29 14:48:29 +00:00
enes
0ee3dba906 fix: user search
Closes #78
2024-10-29 15:44:41 +01:00
enes
efad0f44f5 refactor: use filter storage state, separate profile page filter 2024-10-29 13:38:13 +01:00
enes
6e07f4b8be feat(filter): remember filters, add localstorage hook and utils 2024-10-29 13:21:12 +01:00
enes
7640bdd53b refactor: use SearchInput, search params to q, kind 2024-10-29 09:39:30 +01:00
enes
72252d416b feat: mod search on game page 2024-10-29 09:35:39 +01:00
enes
6e4fa104c0 fix(search): add mods source filter fn
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
Closes #77
2024-10-28 14:49:36 +01:00
f2f80a36c6 chore(git): merge pull request #103 from 96-nsfw-list-tag into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
Reviewed-on: #103
2024-10-28 13:02:54 +00:00
enes
4f4e3a7c85 fix(mod-card): add nsfw tag if mod is in nsfwList, while filtering 2024-10-28 14:00:44 +01:00
enes
0b1d43eac4 fix(mod-page): mark mod as nsfw if found in nsfwList 2024-10-28 13:46:46 +01:00
enes
2dc0ab6cf4 fix(profile): hide block on own profile
All checks were successful
Release to Staging / build_and_release (push) Successful in 39s
2024-10-28 13:10:40 +01:00
enes
15af98359d fix(profile): unblock tag filter
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-10-28 13:08:47 +01:00
enes
3906c70bc9 fix(profile): rerender after profile link changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
Add useProfile hook, closes #99
2024-10-28 12:43:26 +01:00
enes
9341cd6544 fix(socialNav): active user state icon
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
Closes #100
2024-10-28 10:21:12 +01:00
bc782c775a Merge pull request 'comment-fix' (#101) from comment-fix into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
Reviewed-on: #101
Reviewed-by: enes <enes@noreply.git.nostrdev.com>
2024-10-25 08:45:16 +00:00
daniyal
d3c2d5fe7a chore: quick fix 2024-10-24 21:08:40 +05:00
daniyal
d96e5088b8 fix: add timeout in getting user's relay and also pass ndk pool's relays in relayset 2024-10-24 21:07:18 +05:00
daniyal
9aa57c1adf fix: no need to pass relay set to subscribe function, just include p tag with authors pubkey 2024-10-24 20:29:57 +05:00
enes
8ee6f98654 fix: hash router backwards compatibility
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-10-24 13:14:42 +02:00
enes
84cb5b6912 fix: add missing InnerBodyMain div feed layout
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-10-24 11:12:31 +02:00
9b8bf01d33 chore(git): merge pull request #97 from feature/profile-page into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
Reviewed-on: #97
2024-10-24 09:00:46 +00:00
enes
53861a36d3 fix: props and placeholder wip text 2024-10-24 10:48:49 +02:00
enes
99ce338502 feat: browser router and SPA 404.html 2024-10-23 18:00:53 +02:00
enes
76478ad572 fix: quick nav buttons and active state 2024-10-23 17:58:41 +02:00
enes
7393940027 feat: update routes 2024-10-23 17:54:33 +02:00
enes
bb653fa356 fix: add more pages 2024-10-23 17:52:53 +02:00
enes
2e367ecde8 feat: profile page, tabs, mods 2024-10-23 17:51:20 +02:00
enes
a95cd8b6ec refactor: mod report popup 2024-10-23 17:51:20 +02:00
enes
8810673492 refactor: extend checkbox field input 2024-10-23 17:51:20 +02:00
enes
a97a034178 feat: add Tabs component 2024-10-23 17:51:20 +02:00
enes
0102f41403 chore(prettier): css format 2024-10-23 17:51:19 +02:00
63333b38c3 Update src/assets/games/Games_SteamManual.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-10-22 06:02:17 +00:00
enes
38280d151a fix: rename the workflow
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-10-21 17:24:53 +02:00
16d39c407d Update src/styles/cardMod.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-10-21 14:09:24 +00:00
b10920597f chore(git): merge pull request #91 from 85-mod-card-nsfw-tag into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
Reviewed-on: #91
2024-10-21 14:05:05 +00:00
enes
2dd261161e fix: add mod nsfw tag 2024-10-21 16:04:01 +02:00
0e8eeb13d1 chore(git): merge pull request #90 from 73-scroll-up into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 39s
Reviewed-on: #90
2024-10-21 13:44:05 +00:00
enes
4bf18f1584 fix: pagination scroll into view
Fixes #73
2024-10-21 15:40:23 +02:00
988cc03f37 chore(git): merge pull request #89 from ndk-refactor into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
Reviewed-on: #89
Reviewed-by: enes <enes@noreply.git.nostrdev.com>
2024-10-21 13:39:56 +00:00
daniyal
ffc7b60363 Merge branch 'staging' into ndk-refactor 2024-10-21 18:15:53 +05:00
daniyal
545e6e6ec0 chore: quick fix 2024-10-21 17:52:11 +05:00
daniyal
b0ebe7154a fix: removed metadata controller and used ndkContext 2024-10-21 17:51:00 +05:00
daniyal
b69be4d755 fix: removed relay controller and used ndk 2024-10-21 16:39:56 +05:00
04f32546f2 chore(git): merge pull request 'fix: disable body scroll on popup open' (#88) from 61-popup-scroll into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
Reviewed-on: #88
2024-10-21 09:49:13 +00:00
enes
9aeee018b3 chore(git): merge branch 'staging' into 61-popup-scroll 2024-10-21 11:47:40 +02:00
enes
e3b6aecfe8 fix: disable scroll on nostr-login popup open 2024-10-21 11:44:26 +02:00
enes
4214fe127f fix: disable scroll on zap popup open 2024-10-21 11:44:15 +02:00
enes
4bd7c77c05 fix: disable scroll on zap popup open 2024-10-21 11:43:49 +02:00
daniyal
82b87b3e32 fix: move getTotalZapAmount from relay controller to ndkContext 2024-10-21 14:33:40 +05:00
enes
9d50cdfd88 fix: disable scroll on popup open
Closes #61
2024-10-21 11:11:35 +02:00
7f66c17f90 created a new class for NSFW tag for mod cards
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-10-21 09:00:57 +00:00
daniyal
59f4fd6b29 fix: add admin relays to ndk explicit relays asyncronously 2024-10-21 13:22:52 +05:00
610801a674 Update src/components/ModForm.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-10-19 07:47:42 +00:00
2bdee74ac8 adjusted and added text
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-10-19 07:43:20 +00:00
7f8716ab59 Upload files to "src/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 56s
2024-10-19 07:24:58 +00:00
37e9618cf9 chore(git): merge pull request #82 from 76-screenshot-validation into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
Reviewed-on: #82
2024-10-18 09:07:13 +00:00
enes
4d9c68d741 fix(image-validation): add more image types to regex 2024-10-18 11:05:57 +02:00
90efd672cd chore(git): merge pull request #81 from 64-submit-edit-mod-page into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
Reviewed-on: #81
2024-10-18 09:04:38 +00:00
enes
9c580c5ef5 fix(mod): field duplication and focus issue 2024-10-18 11:03:45 +02:00
enes
82b4774174 chore(git): merge branch 'github-pages-10-14' into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-10-18 10:45:46 +02:00
enes
4dd8473f70 build: remove vite base_url used for testing 2024-10-17 13:47:55 +02:00
enes
94d976f127 ci: update workflow 2024-10-16 17:21:44 +02:00
enes
128a2fd82a ci: update workflow 2024-10-16 17:11:22 +02:00
enes
83b93f08db ci: update workflow
Unset extra header
2024-10-16 16:41:05 +02:00
enes
701925b91e ci: update workflow 2024-10-16 15:00:32 +02:00
nostrdev-com
755014103c ci: update workflow 2024-10-16 13:57:11 +02:00
nostrdev-com
69331ba19e ci: remove feature branch from staging workflow 2024-10-15 15:57:45 +02:00
nostrdev-com
e610148e01 refactor: remove hashRouter import 2024-10-15 15:44:05 +02:00
nostrdev-com
2c49dd0c32 refactor: remove GAME_FILES const, no longer needed 2024-10-15 15:39:14 +02:00
nostrdev-com
3d064113c5 ci: update base url 2024-10-15 15:37:29 +02:00
nostrdev-com
43bddd98fc ci: update base url 2024-10-15 15:34:48 +02:00
nostrdev-com
7ee414caa5 ci: update base url 2024-10-15 15:32:42 +02:00
nostrdev-com
98d35f5b7f fix: use src/assets for images and csv 2024-10-15 15:26:18 +02:00
nostrdev-com
78ca820ad4 ci: update staging env context 2024-10-15 11:08:35 +02:00
nostrdev-com
67f06a0717 ci: update branches, steps and env vars 2024-10-15 11:03:35 +02:00
nostrdev-com
729956c1fc ci: update branch for workflow test run 2024-10-14 17:57:34 +02:00
nostrdev-com
b96ff6ab4c ci: initial github pages workflow 2024-10-14 17:53:32 +02:00
daniyal
8529a95718 feat: refactor code to use ndk
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-10-14 19:20:43 +05:00
daniyal
44acba8d26 feat: fetch mods using ndk
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-10-14 13:25:49 +05:00
freakoverse
73e2868f52 added a guide link in the registration pop-up
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-10-12 18:35:32 +00:00
freakoverse
601eda18d3 Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-10-11 11:44:56 +00:00
freakoverse
c65b49720f Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-10-11 11:38:16 +00:00
freakoverse
c4ef3060be Update src/layout/footer.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-10-11 02:42:20 +00:00
freakoverse
c022644bcd Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 52s
2024-10-07 17:29:13 +00:00
freakoverse
b48b4478af Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-10-07 12:02:20 +00:00
daniyal
b6a8fc435d chore: refactor code for mod filter
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-10-07 15:45:21 +05:00
freakoverse
2abd940c00 Update src/components/ModForm.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m10s
2024-10-06 22:57:25 +00:00
freakoverse
d05fd5f9e6 Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 56s
2024-10-06 22:53:14 +00:00
freakoverse
e6a465d54f revert 486f3b79a57646c8164baddff97488f84feae1c4
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m20s
revert Upload files to "public/assets/games"
2024-10-06 22:51:12 +00:00
freakoverse
486f3b79a5 Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m37s
2024-10-06 22:39:16 +00:00
freakoverse
6140218e80 Update public/assets/games/Games_Steam1.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m15s
2024-10-06 22:32:58 +00:00
freakoverse
6c49fd7a0a Delete public/assets/games/Games_Steam.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-10-04 11:46:57 +00:00
freakoverse
0f18e2ce8d Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-10-04 11:43:34 +00:00
freakoverse
56f5951a36 Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-10-04 11:37:55 +00:00
freakoverse
1d4d302335 Update public/assets/games/Games_Other.csv
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-10-04 11:03:57 +00:00
daniyal
440a2c6be1 feat: appyly nsfw filter on latest mods section in home page
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-10-04 00:40:36 +05:00
freakoverse
98ef58884e Update src/components/Zap.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-10-01 06:31:05 +00:00
daniyal
bad1404e1a feat: implemented zap split UI
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-09-30 21:20:30 +05:00
daniyal
3ed7eada83 fix: display download url in authentication details
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-30 15:17:32 +05:00
daniyal
3ff4437d44 fix: hide mods from latest mods section in homepage that admin/user adds to mute list
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-09-30 15:13:57 +05:00
daniyal
3b2e899c14 fix: update form state on getting existing mod data
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-30 14:52:19 +05:00
freakoverse
35c1d704e0 Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-29 05:58:39 +00:00
freakoverse
27933afdea added another button/link for nostr connect firefox extension
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-29 05:45:14 +00:00
freakoverse
c8a9c3c736 Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-09-29 05:39:21 +00:00
freakoverse
185df7ba6d Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-27 10:50:47 +00:00
daniyal
f7bf65c845 fix: in mod detail page fixed navigation for game route and edit mod
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
2024-09-25 21:12:42 +05:00
daniyal
f4f9a8bd17 fix: in game page add source filter while fetching latest 100 mods
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-25 21:00:48 +05:00
daniyal
8608765e79 fix: display profile box even if user does not have metadata
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-25 20:55:48 +05:00
daniyal
37457128c5 feat: implement relay management
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-25 20:21:53 +05:00
freakoverse
26accf4eca Update src/pages/settings/index.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-24 17:48:54 +00:00
freakoverse
7002e3e18d Update src/pages/settings/index.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-09-24 17:44:41 +00:00
freakoverse
2b23294ae5 Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-24 17:40:58 +00:00
freakoverse
a359ee7a8f Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-24 17:33:14 +00:00
daniyal
1a889213fa feat: implement profile edit
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-24 21:53:34 +05:00
freakoverse
e490b35a5f temporarily commented out the WoT rating for download links within a mod post
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-24 09:17:25 +00:00
freakoverse
a6e248d908 temporarily commented out the WoT rating for download links within a mod post
Some checks failed
Release to Staging / build_and_release (push) Failing after 19s
2024-09-24 09:08:39 +00:00
freakoverse
70a03d8b63 Update public/.well-known/nostr.json
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-23 23:46:37 +00:00
freakoverse
da702898f8 Update src/components/Banner.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-23 23:40:39 +00:00
freakoverse
09f3307635 test home nav icon fix
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-09-23 20:58:10 +00:00
freakoverse
56c45f5c57 test home nav icon fix
Some checks failed
Release to Staging / build_and_release (push) Failing after 19s
2024-09-23 20:53:36 +00:00
freakoverse
ceeb6d22d0 test home nav icon fix
Some checks failed
Release to Staging / build_and_release (push) Failing after 22s
2024-09-23 20:43:08 +00:00
freakoverse
a0d2c811db Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-23 20:33:42 +00:00
freakoverse
c0adc8f09b Update src/styles/popup.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-23 20:32:34 +00:00
daniyal
b21c79b992 feat: display reactions, comments, and zap data in mod cards
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-09-23 20:59:33 +05:00
daniyal
a10e9aafd1 chore(refactor): implement a custom hook for comments 2024-09-23 20:59:33 +05:00
freakoverse
a84608a8f9 Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-23 12:30:46 +00:00
freakoverse
8659934f6c Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-23 12:27:21 +00:00
freakoverse
5db234ffda Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-23 12:24:49 +00:00
freakoverse
e2f0f49374 Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-23 12:19:50 +00:00
freakoverse
7c7f756fb4 added a new games list
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-23 12:18:55 +00:00
freakoverse
b10fe68a2d updated games list
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-23 12:01:10 +00:00
daniyal
c59f48611c feat: sort games list based on mods associated with them
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-23 16:15:52 +05:00
daniyal
5ef9cf83d2 fix: depending on what page the user is on, add active class to social navs
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-23 15:39:56 +05:00
freakoverse
6dcb7e93c6 Upload files to "public/assets/games"
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-23 10:23:18 +00:00
daniyal
6a087e659c fix: show appropriate filters based on searching item
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-23 14:56:41 +05:00
freakoverse
d11ad60ede updated landing page games
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-23 09:45:19 +00:00
freakoverse
d171f03f29 updated landing page games
Some checks failed
Release to Staging / build_and_release (push) Failing after 20s
2024-09-23 09:40:40 +00:00
daniyal
e22c731ec6 fix: only specify the game name for featured games and image will be fetched at run time
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-23 13:56:51 +05:00
daniyal
64b5c7194f fix: redirect from mods page to search page on searching 2024-09-23 13:55:18 +05:00
freakoverse
ec4c434b66 added a new classes for comments
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-20 22:56:06 +00:00
freakoverse
59efa91677 pagination optimization
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-18 18:14:59 +00:00
freakoverse
1960589fc3 pagination optimization
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-18 18:12:50 +00:00
freakoverse
0250f6dc11 pagination optimization
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-18 18:09:26 +00:00
freakoverse
dc91a6a186 pagination optimization
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-18 18:04:49 +00:00
daniyal
1d02bf0d6f feat: navigate to search page on submitting search term in games page
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-09-18 22:40:41 +05:00
daniyal
a1dd002d28 feat: enabled routing on game card
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-18 21:49:05 +05:00
daniyal
72cbd325b0 feat: add a game page route that will display all mods associated with that game 2024-09-18 21:49:05 +05:00
daniyal
05414013ce feat: implemented logic for games page 2024-09-18 21:49:05 +05:00
daniyal
c62c1a29b9 chore(refactor): use custom hooks 2024-09-18 21:49:05 +05:00
daniyal
381028614a feat: added a custom hook for games list 2024-09-18 21:49:05 +05:00
freakoverse
876f986ea5 added an extra class to the games search results
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-18 13:53:55 +00:00
freakoverse
b9d5bb8211 Update src/styles/cardMod.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-18 08:03:52 +00:00
daniyal
4dc65b92f7 feat: implemented search page
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-18 08:21:03 +05:00
daniyal
9e8aa16297 chore: fix relay controller 2024-09-18 08:21:03 +05:00
daniyal
a90e932ed6 chore: refactore profile section component 2024-09-18 08:21:03 +05:00
daniyal
9730fec14f chore: move pagination for separate component file 2024-09-18 08:21:03 +05:00
daniyal
06f0282cad chore: memoize modCard 2024-09-18 08:21:03 +05:00
daniyal
5b641ff4cc chore: add error boundary component 2024-09-18 08:21:03 +05:00
freakoverse
49ed027b5c Update src/styles/author.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-17 19:45:26 +00:00
freakoverse
aa8d18ea53 Update src/styles/innerPage.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-17 19:36:02 +00:00
freakoverse
f708dd6530 css mobile optimizing
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-17 19:35:23 +00:00
freakoverse
e3f49832f2 slider css mobile optimization
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-17 11:52:46 +00:00
freakoverse
d76676c089 added a new class
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-09-17 11:46:24 +00:00
freakoverse
6b1d4e7322 added a div wrapper for mod summary for slider to fix text overflow issue
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-17 11:45:05 +00:00
freakoverse
05adb00072 mobile css optimizing social nav
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-17 11:14:34 +00:00
freakoverse
4175ebc010 css mobile optimizing slides
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-17 10:50:18 +00:00
freakoverse
3a71a4a297 changed default state of social nav
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-09-16 08:43:07 +00:00
daniyal
d3a93eab3e chore: add social nav component in main layout
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-16 12:36:35 +05:00
daniyal
22fc2b4ba3 chore: used Link component from react-router-dom instead of a tag 2024-09-16 12:35:42 +05:00
freakoverse
d70e302a69 Update src/styles/popup.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-12 14:51:44 +00:00
freakoverse
8b93d0506d Update src/styles/cardBlogs.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-12 12:24:00 +00:00
freakoverse
a56d26387e removed inline css in one element
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-09-12 12:17:48 +00:00
freakoverse
d13c7ca6c3 added hover effect
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-12 12:16:15 +00:00
daniyal
34b096b121 chore(refactor): improve subscription process
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-11 22:28:36 +05:00
daniyal
53d47fcb80 fix: improve findUserRelays method in metadata controller 2024-09-11 22:28:36 +05:00
daniyal
7a1d0bbfb0 fix: immediately setHasZapped to true after zapping 2024-09-11 22:28:36 +05:00
daniyal
d15c5a21d9 chore: fix react render error 2024-09-11 22:28:36 +05:00
daniyal
990460d7cf chore: add keys to list elements in home page 2024-09-11 22:28:36 +05:00
freakoverse
96bf84a0c4 added zap icon / numbers to mod box
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-11 15:14:31 +00:00
freakoverse
aa0b9cf3c3 changed class
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-10 15:52:27 +00:00
daniyal
b808157352 fix: added the publish state for comment
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-10 16:36:35 +05:00
freakoverse
511a67b793 typo fix
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-10 08:40:12 +00:00
freakoverse
5ed6a51e76 added missing class
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-10 08:29:50 +00:00
freakoverse
4d5f132bab seperated the <p>s from each other
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-10 07:54:06 +00:00
freakoverse
50540f0e3f new classes
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-10 07:42:31 +00:00
freakoverse
e31e7d85ac Update src/pages/mod/internal/comment/index.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-09-10 07:18:58 +00:00
daniyal
a3a022c436 feat: implemented comment feature and refactored mod page
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-09-10 11:42:09 +05:00
daniyal
458ad744e4 chore: implemented absolute imports 2024-09-10 11:42:09 +05:00
freakoverse
d77d9f7bcc link change
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-05 13:00:10 +00:00
freakoverse
23ad13fa85 shortcodes "::" fix
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-05 10:29:22 +00:00
daniyal
87359a914e fix: add signed event to reactionEvents array even before publishing so that UI can be updated immediately
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-05 15:17:51 +05:00
freakoverse
3d20163b08 adjusted and added more emojis/shortcodes
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-05 09:38:15 +00:00
daniyal
0ac31675f9 chore: display reactions UI after reactionEvents are loaded
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-09-05 13:30:10 +05:00
daniyal
98f4666f96 chore: add game name in mod card and featured slider on landing page
All checks were successful
Release to Staging / build_and_release (push) Successful in 52s
2024-09-05 12:46:14 +05:00
daniyal
2dd2992810 feat: implemented the logic for handling reactions on mods 2024-09-05 12:39:52 +05:00
daniyal
a85314f0a7 chore(refactor): reduce code duplication in zap 2024-09-05 12:39:52 +05:00
freakoverse
b12887cdf5 Update src/styles/SimpleSlider.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-03 18:00:37 +00:00
freakoverse
3e3f5fe82b Update src/styles/SimpleSlider.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-03 17:57:32 +00:00
freakoverse
a661b3f781 added game name to game mod slide
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-03 17:56:33 +00:00
freakoverse
1fde36bc5c new class
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-03 15:16:58 +00:00
freakoverse
77d849e3ab added placeholder gamename for a mod card
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-03 15:15:04 +00:00
daniyal
822d5110a8 fix: in setting page display admin tab to only admin users
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-03 16:47:47 +05:00
daniyal
03f9269eb6 fix: fetch mods withoud limit filter on landing page and apply limit once retrived
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-03 15:23:07 +05:00
daniyal
4b51fa55f5 fix: change the route for mod detail page from mods-inner to mod 2024-09-03 15:13:51 +05:00
daniyal
018536e11d fix: sort the mods in by published_at before displaying in latest mods section of landing page
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-03 14:59:23 +05:00
daniyal
c44a28f755 fix: fixed profile picture and bio in profile box
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-03 14:37:54 +05:00
daniyal
8fea6fa27f chore: quick fix
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-09-03 13:15:47 +05:00
daniyal
fad1ff98b3 feat: implemented logic for profile box
Some checks failed
Release to Staging / build_and_release (push) Failing after 23s
2024-09-03 13:05:37 +05:00
freakoverse
4a7899cfde Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-09-02 18:52:21 +00:00
freakoverse
0c32999df8 Update src/pages/home.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-02 16:04:18 +00:00
freakoverse
b4465ee1c6 Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-09-02 10:23:13 +00:00
freakoverse
b492f97795 Update src/constants.ts
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-02 10:01:30 +00:00
freakoverse
f13d800d85 Update src/styles/cardMod.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-09-02 09:57:28 +00:00
freakoverse
8726d042f2 Update src/styles/cardMod.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-02 09:55:00 +00:00
freakoverse
17ef110f6f wrapped the mod image with a div to fix its presentation
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-02 09:53:22 +00:00
freakoverse
764c936ff8 Update src/components/GameCard.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-02 09:49:53 +00:00
freakoverse
1b2926ae77 Update src/components/GameCard.tsx
Some checks failed
Release to Staging / build_and_release (push) Failing after 20s
2024-09-02 09:48:51 +00:00
freakoverse
9a192451f6 Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-02 09:47:55 +00:00
freakoverse
16b3c7684b Update src/styles/cardGames.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-02 09:38:43 +00:00
freakoverse
24ea309dd1 Update src/styles/SimpleSlider.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-02 09:27:23 +00:00
freakoverse
41240ee3fb Update src/styles/SimpleSlider.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-09-02 09:04:57 +00:00
freakoverse
098068acef div wrapped the slider image
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-02 09:03:41 +00:00
daniyal
56ec37e57b feat: display data on landing page
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-09-02 13:50:52 +05:00
daniyal
c6831f3fb2 chore: add a map in relay controller for storing events 2024-09-02 13:50:52 +05:00
daniyal
0733849b25 chore: use fallback images for mod and games if provided image gives error 2024-09-02 13:50:52 +05:00
freakoverse
faf30a89b0 Update src/styles/tags.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-31 12:03:57 +00:00
daniyal
0b2e5c29d5 feat: add image gallery
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-29 23:01:41 +05:00
daniyal
e1da323c2f fix: add fully unmoderated option for admin
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-29 22:13:36 +05:00
s
9893373f75 Merge pull request 'feat: added the ability to unblock the posts' (#26) from unblock into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
Reviewed-on: #26
2024-08-29 13:28:55 +00:00
daniyal
203e27b19d feat: added the ability to unblock the posts 2024-08-29 18:26:06 +05:00
daniyal
47cc4a19ea fix: prepend https:// to url if no protocol is specified
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-29 14:32:27 +05:00
daniyal
ab27a1f9e1 chore: quick fix
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-29 13:29:01 +05:00
daniyal
8a232c7d91 fix: ability to right-click on a mod post to open in new tab or copy link
Some checks failed
Release to Staging / build_and_release (push) Failing after 22s
2024-08-29 13:24:15 +05:00
daniyal
1e98b16c14 fix: reset body field in mod form when route changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-28 22:57:26 +05:00
freakoverse
d9f0972961 added more report options (2)
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-28 17:40:33 +00:00
freakoverse
aa9884b9fa Update src/styles/author.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-08-28 17:28:37 +00:00
freakoverse
5cb20794d0 Update src/styles/author.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-08-28 17:27:02 +00:00
s
4d64c33597 Merge pull request 'feat: added the ability to report and block posts' (#25) from block-report into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
Reviewed-on: #25
2024-08-28 17:05:21 +00:00
daniyal
d9347014ec feat: added the ability to report and block posts 2024-08-28 22:03:43 +05:00
daniyal
1259144228 fix: use published_at for latest/oldest sort
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-27 20:46:42 +05:00
freakoverse
a5018d9a1f Update src/styles/tiptap.scss
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-27 12:47:24 +00:00
freakoverse
e0440e1638 temp fix targeting div
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-27 12:41:54 +00:00
freakoverse
74e38eac50 Update src/styles/tiptap.scss
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-27 12:27:40 +00:00
freakoverse
eb450839d5 Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-27 12:25:33 +00:00
freakoverse
733c155447 Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-27 12:12:27 +00:00
freakoverse
9bdc8678c4 Update src/styles/tiptap.scss
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-27 12:08:50 +00:00
freakoverse
7c2dd9fe7a Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-27 12:03:19 +00:00
freakoverse
9782256483 Update src/styles/tiptap.scss
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-27 11:58:58 +00:00
freakoverse
1cd898eae7 adding classes and adjusting them
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-27 11:51:28 +00:00
freakoverse
926d29a36e typo fix
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-27 11:39:04 +00:00
freakoverse
29947757af new class for tiptap btn
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-27 11:38:13 +00:00
freakoverse
b9b1e1457c added class to toolbar btn
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-27 11:36:26 +00:00
freakoverse
a88ef61eb7 Update src/styles/tiptap.scss
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-27 11:20:23 +00:00
daniyal
4de54f7688 feat: use tiptap for rich text editor
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-27 15:53:34 +05:00
daniyal
c429dfa322 fix: use @uiw/react-md-editor as rich text editor and displaying markdown
All checks were successful
Release to Staging / build_and_release (push) Successful in 50s
2024-08-27 00:35:37 +05:00
s
26dcd5463d Merge branch 'master' into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-26 13:55:53 +00:00
daniyal
1927887992 fix: reset mod form when route changes from edit to submit
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-08-26 18:44:00 +05:00
s
dc19e614df Merge pull request 'fix: use naddr instead of nevent' (#22) from naddr into staging
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
Reviewed-on: #22
2024-08-26 12:13:10 +00:00
daniyal
6377da94c6 fix: use naddr instead of nevent 2024-08-26 17:11:33 +05:00
freakoverse
848af66a75 Merge pull request 'nav/header adjustments' (#21) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
Reviewed-on: #21
2024-08-26 10:28:23 +00:00
freakoverse
aca6908ce9 Update src/styles/nav.module.scss
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-26 10:09:14 +00:00
freakoverse
be7e506457 Update src/styles/nav.module.scss
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-26 09:59:40 +00:00
freakoverse
b0f8c647ee Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-26 09:51:20 +00:00
freakoverse
b259623ab6 Update src/layout/header.tsx
Some checks failed
Release to Staging / build_and_release (push) Failing after 17s
2024-08-26 09:46:18 +00:00
freakoverse
2a28b068b6 Update src/layout/header.tsx
Some checks failed
Release to Staging / build_and_release (push) Failing after 18s
2024-08-26 09:39:14 +00:00
freakoverse
e335b05290 Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-26 08:27:38 +00:00
freakoverse
b1b0876238 Update src/styles/nav.module.scss
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-08-26 08:25:20 +00:00
freakoverse
d18528bcf6 Update src/layout/header.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-26 08:20:45 +00:00
freakoverse
7743f30faa Update src/styles/nav.module.scss
Some checks failed
Release to Staging / build_and_release (push) Failing after 25s
2024-08-26 08:14:44 +00:00
freakoverse
8bd4373546 Update src/styles/nav.module.scss
Some checks failed
Release to Staging / build_and_release (push) Failing after 21s
2024-08-26 08:06:25 +00:00
freakoverse
9a1cc39027 Update src/styles/nav.module.scss
Some checks failed
Release to Staging / build_and_release (push) Failing after 21s
2024-08-26 07:55:53 +00:00
freakoverse
1e6b3d2c0b slightly adjusted structure of nav with new links
Some checks failed
Release to Staging / build_and_release (push) Failing after 20s
2024-08-26 07:51:18 +00:00
freakoverse
d9129ab4da Merge pull request 'color adjustments and effects' (#20) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
Reviewed-on: #20
2024-08-21 20:18:14 +00:00
freakoverse
47ce263c37 Update src/styles/popup.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 49s
2024-08-21 19:00:42 +00:00
freakoverse
7b25e7653c background color change
All checks were successful
Release to Staging / build_and_release (push) Successful in 51s
2024-08-21 18:59:44 +00:00
freakoverse
4d7e69b089 background color change
All checks were successful
Release to Staging / build_and_release (push) Successful in 51s
2024-08-21 18:51:22 +00:00
freakoverse
3f873c410c Merge pull request 'color adjustments' (#19) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
Reviewed-on: #19
2024-08-21 05:51:00 +00:00
freakoverse
42d20857f5 color adjustment
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
2024-08-21 05:12:04 +00:00
freakoverse
837be77d9c color adjustment
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
2024-08-21 05:10:00 +00:00
freakoverse
d83f19b1ca color adjustment
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-21 05:07:47 +00:00
freakoverse
a89a9582fd Merge pull request 'Updated tags in index.html' (#18) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
Reviewed-on: #18
2024-08-20 12:37:56 +00:00
freakoverse
a5fe2c1622 Update index.html
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-20 12:33:57 +00:00
freakoverse
de21af1b4a Merge pull request 'removed comments' (#17) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
Reviewed-on: #17
2024-08-19 14:38:56 +00:00
freakoverse
54911d3efa removed comments
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-19 14:37:46 +00:00
freakoverse
1a52bfd30b Merge pull request 'temp fix for blockquote style' (#16) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
Reviewed-on: #16
2024-08-19 10:56:41 +00:00
freakoverse
1f93e9c6b6 temp fix for blockquote style
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-19 10:44:36 +00:00
freakoverse
23088cb3ec Merge pull request 'css edit + games list update' (#15) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
Reviewed-on: #15
2024-08-19 09:03:42 +00:00
freakoverse
a5627000ae Upload files to "public/assets"
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-19 09:00:53 +00:00
freakoverse
e40c900499 Update src/styles/cardMod.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-19 08:32:40 +00:00
freakoverse
d8d04b8ae0 Merge pull request 'updated readme and added license' (#14) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
Reviewed-on: #14
2024-08-19 07:36:41 +00:00
freakoverse
021b3fcece Update README.md
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-19 07:34:48 +00:00
freakoverse
40630a6513 Add LICENSE.txt
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-19 07:11:53 +00:00
s
f8ca32c143 Merge pull request 'fix routing for mod edit page' (#13) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
Reviewed-on: #13
2024-08-19 06:42:49 +00:00
daniyal
da88a969f3 fix routing for mod edit page
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-19 11:41:11 +05:00
s
60317c2e8c Merge pull request 'remove url reachability check for download url as it may give cors error' (#12) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
Reviewed-on: #12
2024-08-19 06:23:59 +00:00
daniyal
012e868ad3 remove url reachability check for download url as it may give cors error
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-19 11:22:55 +05:00
freakoverse
46b0384bc6 Merge pull request 'text and link and style changes,' (#11) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
Reviewed-on: #11
2024-08-18 23:36:54 +00:00
freakoverse
3ddb95fda2 text edit
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
2024-08-18 23:34:22 +00:00
freakoverse
434fbd99c0 adjusted registration popup buttons/links
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-18 23:32:47 +00:00
freakoverse
d7246e3cb0 link edit
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-18 19:08:27 +00:00
freakoverse
d425b9ec87 text edits
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-18 19:05:06 +00:00
freakoverse
d462c9bd93 Update src/pages/about.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-18 18:43:37 +00:00
freakoverse
3440cc0fb6 text edits
Some checks failed
Release to Staging / build_and_release (push) Failing after 18s
2024-08-18 18:38:36 +00:00
freakoverse
2222c78742 text edit
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-08-18 18:21:04 +00:00
freakoverse
02843028f7 Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 48s
2024-08-18 17:22:11 +00:00
freakoverse
fd5a9e7e4f Update src/index.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-18 10:30:41 +00:00
freakoverse
e4c09399ec Update src/styles/post.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-18 10:25:15 +00:00
freakoverse
50de8ef84c headings font size changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-18 10:19:16 +00:00
freakoverse
21ef8c61f4 Update public/.well-known/nostr.json
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-18 09:07:23 +00:00
freakoverse
0211e380a0 color adjustment
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-17 15:40:35 +00:00
freakoverse
8395d4fc31 text change
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-17 14:31:40 +00:00
freakoverse
b73f3632df font-size change
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-17 14:30:30 +00:00
freakoverse
99463e3fd2 Merge pull request 'well-known fix' (#10) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
Reviewed-on: #10
2024-08-17 14:04:14 +00:00
b
81fa41635c fix: removing old well known, updating pipeline
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
2024-08-17 14:50:22 +01:00
b
c0eab5dd13 fix: well-known setup
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-17 14:46:30 +01:00
freakoverse
9cea402479 added the .well-know/nostr.json to the public folder
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-08-16 19:18:31 +00:00
freakoverse
00e73aaad8 Merge pull request 'deleted some media files, adjusted some text, added new static elements in the site tip popup, new css classes, and added a well-know with nostr json file for nip-05' (#9) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
Reviewed-on: #9
2024-08-16 19:12:07 +00:00
freakoverse
acfdd21c4d added to manage nip-05 addresses for the domain
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 19:06:23 +00:00
freakoverse
a7b110503f added link
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 19:02:21 +00:00
freakoverse
a26453c16f new classes
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 18:59:43 +00:00
freakoverse
658cf0ab23 text fix + added a new elements for silent payment donation address
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-16 18:57:23 +00:00
freakoverse
8e791075da text fix + added a new elements for silent payment donation address
Some checks failed
Release to Staging / build_and_release (push) Failing after 18s
2024-08-16 18:54:51 +00:00
freakoverse
b9799c3924 Delete public/assets/img/ProfileLinkQR.png
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-16 18:05:29 +00:00
freakoverse
a4789e7f5f Delete public/assets/img/media-cache (1).jpg
Some checks failed
Release to Staging / build_and_release (push) Has been cancelled
2024-08-16 18:05:19 +00:00
freakoverse
3d230eddf7 Delete public/assets/img/media-cache (4).png
Some checks failed
Release to Staging / build_and_release (push) Has been cancelled
2024-08-16 18:05:04 +00:00
freakoverse
241a1dcec9 Delete public/assets/img/uBlog Logo.svg
Some checks failed
Release to Staging / build_and_release (push) Has been cancelled
2024-08-16 18:04:47 +00:00
freakoverse
16b8107731 Merge pull request 'fixed game list issue not appearing in mod submit + temp-fixed its scroll and style ux' (#8) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
Reviewed-on: #8
2024-08-16 15:37:52 +00:00
freakoverse
cc31325bb7 Update src/styles/styles.css
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 15:35:08 +00:00
freakoverse
f9498d8d8f test temp fix for game list ux in game submission
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-16 15:30:23 +00:00
freakoverse
3b594fb5eb test temp fix for game list ux in game submission
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 15:27:37 +00:00
freakoverse
b5fd5ded6a fixed an issue that was preventing the games list from visually appearing
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
2024-08-16 14:32:49 +00:00
freakoverse
999301a4d9 created a new class to handle input dropdown so it doesn't overflow
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-16 14:29:11 +00:00
s
d1f84ddd90 Merge pull request 'Update .gitea/workflows/release-production.yaml' (#7) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
Reviewed-on: #7
2024-08-16 12:51:44 +00:00
s
2cd972f0e1 Update .gitea/workflows/release-production.yaml
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-16 12:48:13 +00:00
freakoverse
17e28ede53 Merge pull request 'Mostly text, images, and link changes' (#6) from staging into master
Some checks failed
Release to Staging / build_and_release (push) Failing after 40s
Reviewed-on: #6
2024-08-16 12:36:01 +00:00
freakoverse
fc8bd16995 link hover effect
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 12:21:00 +00:00
freakoverse
bdd44681d7 link styles
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-16 12:18:28 +00:00
freakoverse
8e53b8af87 to affect style of links on hover of their parent
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-16 12:15:02 +00:00
freakoverse
dd22b780a7 Update src/components/Banner.tsx
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 12:09:12 +00:00
freakoverse
1d04002b8d style change
Some checks failed
Release to Staging / build_and_release (push) Failing after 20s
2024-08-16 12:05:47 +00:00
freakoverse
f4ced90acb temp styling
Some checks failed
Release to Staging / build_and_release (push) Failing after 19s
2024-08-16 12:04:43 +00:00
freakoverse
90581fe630 link and text changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 12:01:15 +00:00
freakoverse
5771f81061 link and text changes
Some checks failed
Release to Staging / build_and_release (push) Failing after 18s
2024-08-16 11:47:54 +00:00
freakoverse
38f46bf01d link and text changes
Some checks failed
Release to Staging / build_and_release (push) Failing after 18s
2024-08-16 11:18:47 +00:00
freakoverse
0f688b2a59 img fix
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-16 11:09:10 +00:00
freakoverse
a88a2d93b0 text change
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
2024-08-16 11:03:48 +00:00
freakoverse
b6f5dfa720 text and media changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-16 11:02:39 +00:00
freakoverse
5d890d86f6 media change
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-16 10:58:30 +00:00
freakoverse
a05c2e0164 removed Amber option in register popup
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 10:55:05 +00:00
freakoverse
d62b9b1c20 link to div and icon change
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-16 10:50:03 +00:00
freakoverse
ed8ff79373 text and media changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-16 10:46:21 +00:00
freakoverse
c01f3279cc text and media changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 10:39:08 +00:00
freakoverse
8191534cf2 text and media changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-08-16 10:27:20 +00:00
freakoverse
1eb25d1e72 text changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-16 10:23:32 +00:00
freakoverse
f35ebe4bee text and media changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-16 10:22:22 +00:00
freakoverse
83cc74606c text and media changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-16 10:14:12 +00:00
freakoverse
a0d2dd48e9 text and media changes
All checks were successful
Release to Staging / build_and_release (push) Successful in 51s
2024-08-16 10:12:22 +00:00
s
24203884fb Merge pull request 'chore: add production release workflow' (#5) from staging into master
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
Reviewed-on: #5
2024-08-16 06:22:45 +00:00
daniyal
6152b895bc chore: add production release workflow
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-16 11:15:53 +05:00
daniyal
75d8839330 feat: add register popup
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-15 19:31:39 +05:00
daniyal
f544d8ab15 feat: update login methods in nostr-login configuration
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-14 21:33:32 +05:00
daniyal
c978c50168 fix: add class bodyMain to root element
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-14 15:17:37 +05:00
daniyal
36f9f976ac feat: fix download flow
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-14 15:06:13 +05:00
daniyal
62a8207bcd feat: export nsec
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-14 14:50:28 +05:00
freakoverse
0c74bfbb58 removed link
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-14 09:28:06 +00:00
freakoverse
144a091b7f icon fixes and removal of profile link (since there already is one above it)
Some checks failed
Release to Staging / build_and_release (push) Failing after 21s
2024-08-14 09:24:59 +00:00
freakoverse
0384dc668e swapped an icon
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-14 09:20:44 +00:00
freakoverse
929800a543 fixed a overflow issues (by adding padding)
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-14 09:17:56 +00:00
freakoverse
a68a906cbe fixed spacing issue
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-14 09:08:30 +00:00
freakoverse
6c6381791e Made the social posts scrollable with a bit of redesign
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-14 08:59:49 +00:00
freakoverse
9c2ec3a5ea added wrapping div .IBMSMSplitMainSmallSideSecWrapper
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-14 08:41:43 +00:00
freakoverse
563119e1a1 Added position: relative; to aid with the sticky position of .IBMSMSplitMainSmallSideSecWrapper
All checks were successful
Release to Staging / build_and_release (push) Successful in 40s
2024-08-14 08:20:15 +00:00
freakoverse
d22b007fd9 added a new class for side column to make it sticky
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
all divs with .IBMSMSplitMainSmallSideSec should have a div wrapped around them with .IBMSMSplitMainSmallSideSecWrapper
2024-08-14 08:16:17 +00:00
daniyal
6acf4c3028 feat: display extra auth details along with downdload links
All checks were successful
Release to Staging / build_and_release (push) Successful in 42s
2024-08-14 12:08:58 +05:00
freakoverse
7fa59d6a52 added white background to the QR code for zaps
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
(reason: a lot of the time the camera can't read the QR because it was almost black on black)
2024-08-13 19:00:14 +00:00
freakoverse
f3dde26d88 fixed an overflow issue
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-13 18:53:05 +00:00
daniyal
0cc0d82e68 feat: sanitizeAndAddTargetBlank to body of mod
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-13 23:27:49 +05:00
daniyal
01af3fa72e feat: show time of publishing/editing 2024-08-13 23:15:45 +05:00
daniyal
e06cec84fb fix: month format of publish and updated date fixed
All checks were successful
Release to Staging / build_and_release (push) Successful in 41s
2024-08-13 18:22:25 +05:00
daniyal
ff737e5486 fix: removed removed optional lnurl from tags of payment zap request 2024-08-13 18:13:30 +05:00
daniyal
51bd5a4d5e fix: replace browser router with hash router 2024-08-13 16:49:11 +05:00
daniyal
a8a2d3dbf3 feat: implemented the logic for zapping mod
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-13 15:51:05 +05:00
freakoverse
42c40c2d8e logo updated (for favicon)
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-11 07:00:37 +00:00
freakoverse
f9735128c7 Updated logo
All checks were successful
Release to Staging / build_and_release (push) Successful in 44s
2024-08-11 06:56:09 +00:00
daniyal
9e4b9472ee chore: fix workflow
All checks were successful
Release to Staging / build_and_release (push) Successful in 46s
2024-08-10 00:19:37 +05:00
daniyal
f1f75a42df chore: debug metadata controller
All checks were successful
Release to Staging / build_and_release (push) Successful in 47s
2024-08-10 00:09:21 +05:00
daniyal
a4c2b50a16 chore: debug metadata controller
All checks were successful
Release to Staging / build_and_release (push) Successful in 45s
2024-08-10 00:04:14 +05:00
daniyal
0668226df1 chore(debug): debugging metadata contoller
All checks were successful
Release to Staging / build_and_release (push) Successful in 43s
2024-08-09 15:50:34 +05:00
daniyal
d451eb129d chore(refactor): code refactored for zapping app account
All checks were successful
Release to Staging / build_and_release (push) Successful in 1m8s
2024-08-08 23:01:50 +05:00
daniyal
5796bb18e3 chore(workflow): set target branch to master
Some checks failed
Release to Staging / build_and_release (push) Has been cancelled
2024-08-08 22:32:59 +05:00
daniyal
4b6b0b73d6 chore: fix build errors 2024-08-08 22:28:28 +05:00
daniyal
f06801d80b chore: add workflow for deploying to staging 2024-08-08 22:17:51 +05:00
s
0591dfecd6 Merge pull request 'feat: add the feature to zap admin/app account' (#4) from zap into master
Reviewed-on: #4
2024-08-08 17:02:12 +00:00
daniyal
44d0100341 chore: quick fix 2024-08-08 21:56:30 +05:00
daniyal
7b9c95c863 feat: add NSFW tag in mod details page 2024-08-08 21:47:38 +05:00
daniyal
0e14336e8b feat: add the feature to zap admin 2024-08-08 21:18:37 +05:00
s
c5b75c088e Merge pull request 'feat: implemented mod details page' (#3) from mods-inner into master
Reviewed-on: #3
2024-08-06 10:57:27 +00:00
234 changed files with 187164 additions and 208177 deletions

View File

@ -1,4 +1,20 @@
# This relay will be used to publish/retrieve events along with other relays (user's relays, admin relays)
VITE_APP_RELAY=wss://relay.degmods.com
# A comma separated list of npubs, Relay list will be extracted for these npubs and this relay list will be used to publish event
VITE_ADMIN_NPUBS= <A comma separated list of npubs>
VITE_ADMIN_NPUBS= <A comma separated list of npubs>
# A dedicated npub used for reporting mods, blogs, profile and etc.
VITE_REPORTING_NPUB= <npub1...>
# A dedicated npub used for site WOT.
VITE_SITE_WOT_NPUB= <npub1...>
# if there's no featured image, or if the image breaks somewhere down the line, then it should default to this image
VITE_FALLBACK_MOD_IMAGE=https://image.nostr.build/1fa7afd3489302c2da8957993ac0fd6c4308eedd4b1b95b24ecfabe3651b2183.png
# if there's no image, or if the image breaks somewhere down the line, then it should default to this image
VITE_FALLBACK_GAME_IMAGE=https://image.nostr.build/1fa7afd3489302c2da8957993ac0fd6c4308eedd4b1b95b24ecfabe3651b2183.png
# A comma separated list of npubs, this list is used to fetch just the posts from the admin
VITE_BLOG_NPUBS= <A comma separated list of npubs>

View File

@ -1,10 +1,10 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
env: { browser: true, es2022: true },
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:react-hooks/recommended',
'plugin:react-hooks/recommended'
],
ignorePatterns: ['dist', '.eslintrc.cjs'],
parser: '@typescript-eslint/parser',
@ -12,7 +12,7 @@ module.exports = {
rules: {
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
{ allowConstantExport: true }
]
}
}

View File

@ -0,0 +1,42 @@
name: Release to Production
on:
push:
branches:
- master
jobs:
build_and_release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: 18
- name: Install Dependencies
run: npm ci
- name: Create .env File
run: |
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
cat .env
- name: Create Build
run: npm run build
- name: Release Build
run: |
npm -g install cloudron-surfer
surfer config --token ${{ secrets.PRODUCTION_CLOUDRON_SURFER_TOKEN }} --server degmods.com
surfer put dist/* / --all -d
surfer put dist/.well-known / --all

View File

@ -0,0 +1,42 @@
name: Release to Staging
on:
push:
branches:
- staging
jobs:
build_and_release:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: 18
- name: Install Dependencies
run: npm ci
- name: Create .env File
run: |
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
cat .env
- name: Create Build
run: npm run build
- name: Release Build
run: |
npm -g install cloudron-surfer
surfer config --token ${{ secrets.STAGING_CLOUDRON_SURFER_TOKEN }} --server dev.degmods.com
surfer put dist/* / --all -d
surfer put dist/.well-known / --all

View File

@ -0,0 +1,86 @@
# Simple workflow for deploying static content to GitHub Pages
name: Release to DEG-Mods.github.io
on:
push:
branches: ['master']
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Allow one concurrent deployment
concurrency:
group: 'pages'
cancel-in-progress: true
jobs:
build:
environment:
name: github-pages
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Node
uses: actions/setup-node@v4
with:
node-version: 18
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Create .env File
run: |
echo "VITE_APP_RELAY=${{ vars.VITE_APP_RELAY }}" >> .env
echo "VITE_ADMIN_NPUBS=${{ vars.VITE_ADMIN_NPUBS }}" >> .env
echo "VITE_SITE_WOT_NPUB=${{ vars.VITE_SITE_WOT_NPUB }}" >> .env
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .env
echo "VITE_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
echo "VITE_REPORTING_NPUB=${{ vars.VITE_REPORTING_NPUB }}" >> .env
echo "VITE_BLOG_NPUBS=${{ vars.VITE_BLOG_NPUBS }}" >> .env
cat .env
- name: Build
run: npm run build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: './dist'
deploy:
environment:
name: github-pages
runs-on: ubuntu-latest
needs: build
steps:
- name: Checkout
uses: actions/checkout@v4
with:
repository: ${{ vars.ORGANIZATION_NAME }}/${{ vars.REPOSITORY_NAME }}
path: ${{ vars.REPOSITORY_NAME }}
token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
- name: Clear target repo
run: |
rm -rf ${{ vars.REPOSITORY_NAME }}/*
- name: Download build artifacts
uses: actions/download-artifact@v4
with:
name: dist
path: ./dist
- name: Prepare files
run: |
cp -r dist/* ${{ vars.REPOSITORY_NAME }}/
echo ${{ vars.CUSTOM_DOMAIN }} > ${{ vars.REPOSITORY_NAME }}/CNAME
touch ${{ vars.REPOSITORY_NAME }}/.nojekyll
- name: Commit and push
run: |
cd ${{ vars.REPOSITORY_NAME }}
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add .
git commit -m 'Deploy from source repo'
git push origin main

21
LICENSE.txt Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Freakoverse
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,11 +1,91 @@
### Local testing
[DEG Mods](https://degmods.com)
===============================
- clone the repo
**DEG Mods** (Decentralized Game Mods) is a censorship-resistant platform for game mods, built on the Nostr protocol. It allows creators to thrive without the fear of censorship, bans, or losing their connection with fans. At DEG Mods, game mod creators and enthusiasts are empowered because, well, we literally can't mess with them.
```sh
npm ci
```
Features
--------
```sh
npm run dev
```
- **Never Get Your Game Mod Page Taken Down**: Enjoy peace of mind knowing that your content won't be removed or censored.
- **Never Get Your Profile Banned**: Your profile remains active and accessible, without fear of bans.
- **Never Lose Your Followers**: Maintain a steady connection with your followers and fans.
- **Get 100% of Tips Revenue**: Keep all the revenue from tips without any cuts.
Installation
------------
To get started with DEG Mods, follow these steps:
1. **Clone the repository**:
bash
Copy code
`git clone <URL-of-repo>`
2. **Navigate to the project directory**:
bash
Copy code
`cd <project-directory>`
3. **Install dependencies**:
DEG Mods is a React project, so use `npm ci` to install the exact versions of dependencies as specified in `package-lock.json`:
bash
Copy code
`npm ci`
Usage
-----
To run DEG Mods, follow these instructions:
1. **Start the development server**:
bash
Copy code
`npm run dev`
Contributing
------------
We welcome contributions from the community! To contribute to DEG Mods:
- **Submit Issues**: Report bugs, provide feedback, or request features.
- **Submit Pull Requests**: Contribute code improvements or new features.
Screenshots
-----------
Here are some screenshots of DEG Mods in action:
- ![Screenshot 1](https://image.nostr.build/9ea5f70a647581e5ea8855edcc2afb59b33195a3498c4052fa6350e414c03c50.jpg)
- ![Screenshot 2](https://image.nostr.build/f9a74d52547e84e51934f03358e5c41a2f99b896f1b2369fc3f4027d0f1a0d6f.jpg)
- ![Screenshot 3](https://image.nostr.build/cbd806c58a0f5e33b721cd9205c53aae64a422f271a8434b4d9bb8c3ba4e5c90.jpg)
- ![Screenshot 4](https://image.nostr.build/374bb4ca83211fd1f5646b611af188a424ebb1b44df1cb7ad29208868ac12675.jpg)
Nostr Implementations
---------------------
- ✅ **Text**
- ⬜ **Text**
License
-------
This project is licensed under the MIT License. See the `LICENSE` file for more details.
Acknowledgments
----------------
- **Nostr Protocol**: Thanks to the Nostr community for the foundational technology that makes DEG Mods possible.
- **Contributors**: A shout-out to everyone who has contributed to making DEG Mods a reality.

View File

@ -3,50 +3,57 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="/assets/img/DEGM%20Thumb.png" />
<meta name="twitter:title" content="DEG Mods - Liberating Game Mods" />
<meta
name="description"
content="Never get your game mods censored, get banned, lose your history, nor lose the connection between game mod creators and fans. Download your mods freely."
/>
<meta property="og:image" content="/assets/img/DEGM%20Thumb.png" />
<meta
name="twitter:description"
content="Never get your game mods censored, get banned, lose your history, nor lose the connection between game mod creators and fans. Download your mods freely."
/>
<meta property="og:type" content="website" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/Swiper/6.4.8/swiper-bundle.min.css"
/>
<!-- Links and Stylesheets -->
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css" />
<link
rel="icon"
type="image/png"
sizes="935x934"
href="/assets/img/Logo%20with%20circle.png"
/>
<link
rel="icon"
type="image/png"
sizes="935x934"
href="/assets/img/Logo%20with%20circle.png"
/>
<link rel="icon" type="image/png" sizes="935x934" href="/index.html" />
<link
rel="icon"
type="image/png"
sizes="935x934"
href="/assets/img/Logo%20with%20circle.png"
/>
<title>DEG Mods - Liberating Game Mods</title>
<!-- Start Hash Router Backwards Compatibility -->
<script type="text/javascript">
;(function (l) {
if (l.hash.startsWith('#/')) {
l.href = l.href.replace('#/', '')
}
})(window.location)
</script>
<!-- End Hash Router Backwards Compatibility -->
<!-- Start Single Page Apps for GitHub Pages -->
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// MIT License
// https://github.com/rafgraph/spa-github-pages
// This script checks to see if a redirect is present in the query string,
// converts it back into the correct url and adds it to the
// browser's history using window.history.replaceState(...),
// which won't cause the browser to attempt to load the new url.
// When the single page app is loaded further down in this file,
// the correct url will be waiting in the browser's history for
// the single page app to route accordingly.
;(function (l) {
if (l.search[1] === '/') {
var decoded = l.search
.slice(1)
.split('&')
.map(function (s) {
return s.replace(/~and~/g, '&')
})
.join('?')
window.history.replaceState(
null,
null,
l.pathname.slice(0, -1) + decoded + l.hash
)
}
})(window.location)
</script>
<!-- End Single Page Apps for GitHub Pages -->
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Swiper/6.4.8/swiper-bundle.min.js"></script>
<script src="assets/bootstrap/js/bootstrap.min.js"></script>
<script src="/assets/bootstrap/js/bootstrap.min.js"></script>
</body>
</html>

4802
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
{
"name": "degmods.com",
"private": true,
"version": "0.0.0",
"version": "0.0.0-alpha-1",
"type": "module",
"scripts": {
"dev": "vite",
@ -10,32 +10,50 @@
"preview": "vite preview"
},
"dependencies": {
"@nostr-dev-kit/ndk": "2.8.2",
"@getalby/lightning-tools": "5.0.3",
"@mdxeditor/editor": "^3.20.0",
"@nostr-dev-kit/ndk": "2.11.0",
"@nostr-dev-kit/ndk-cache-dexie": "2.5.9",
"@reduxjs/toolkit": "2.2.6",
"@types/react-helmet": "^6.1.11",
"axios": "^1.7.9",
"bech32": "2.0.0",
"buffer": "6.0.3",
"date-fns": "3.6.0",
"dexie": "4.0.8",
"dompurify": "3.1.6",
"file-saver": "2.0.5",
"fslightbox-react": "1.7.6",
"lodash": "4.17.21",
"marked": "^14.1.3",
"marked-directive": "^1.0.7",
"nostr-login": "1.5.2",
"nostr-tools": "2.7.1",
"papaparse": "5.4.1",
"qrcode.react": "3.1.0",
"react": "^18.3.1",
"react-countdown": "2.3.5",
"react-dom": "^18.3.1",
"react-quill": "2.0.0",
"react-dropzone": "^14.3.5",
"react-helmet": "^6.1.0",
"react-redux": "9.1.2",
"react-router-dom": "^6.24.1",
"react-toastify": "10.0.5",
"react-window": "1.8.10",
"uuid": "10.0.0"
"swiper": "11.1.11",
"uuid": "10.0.0",
"webln": "0.3.2"
},
"devDependencies": {
"@types/dompurify": "3.0.5",
"@types/file-saver": "2.0.7",
"@types/fslightbox-react": "1.7.7",
"@types/lodash": "4.17.7",
"@types/papaparse": "5.3.14",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/react-window": "1.8.8",
"@types/turndown": "^5.0.5",
"@types/uuid": "10.0.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
@ -45,6 +63,7 @@
"eslint-plugin-react-refresh": "^0.4.7",
"ts-css-modules-vite-plugin": "1.0.20",
"typescript": "^5.2.2",
"vite": "^5.3.1"
"vite": "^5.3.1",
"vite-tsconfig-paths": "5.0.1"
}
}

View File

@ -0,0 +1,13 @@
{
"names": {
"degmods": "f4bf1fb5ba8be839f70c7331733e309f780822b311f63e01f9dc8abbb428f8d5",
"degmodsreposter": "7382a4cc21742ac3e3581f1c653a41f912e985e6a941439377803b866042e53f",
"degmodsreport": "ca2734bb5e59427dd5d66f59dde3b4a045110b7a12eb99a4a862bf012b7850d9",
"freakoverse": "3cea4806b1e1a9829d30d5cb8a78011d4271c6474eb31531ec91f28110fe3f40",
"nostrdev": "27487c9600b16b24a1bfb0519cfe4a5d1ad84959e3cce5d6d7a99d48660a1f78",
"Merlin": "76dd32f31619b8e35e9f32e015224b633a0df8be8d5613c25b8838a370407698",
"makano": "fd5989ddfadd9e2af6ceb8b63942a9e31b37367e89917931ede3b2ea76823f10",
"reya": "126103bfddc8df256b6e0abfd7f3797c80dcc4ea88f7c2f87dd4104220b4d65f",
"podcast_at_melonmancy.net": "4f66998fc435425257e5672a58b5c6fefda86a8b33514780e52d024a54f50ede"
}
}

51
public/404.html Normal file
View File

@ -0,0 +1,51 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Single Page Apps for GitHub Pages</title>
<script type="text/javascript">
// Single Page Apps for GitHub Pages
// MIT License
// https://github.com/rafgraph/spa-github-pages
// This script takes the current url and converts the path and query
// string into just a query string, and then redirects the browser
// to the new url with only a query string and hash fragment,
// e.g. https://www.foo.tld/one/two?a=b&c=d#qwe, becomes
// https://www.foo.tld/?/one/two&a=b~and~c=d#qwe
// Note: this 404.html file must be at least 512 bytes for it to work
// with Internet Explorer (it is currently > 512 bytes)
// If you're creating a Project Pages site and NOT using a custom domain,
// then set pathSegmentsToKeep to 1 (enterprise users may need to set it to > 1).
// This way the code will only replace the route part of the path, and not
// the real directory in which the app resides, for example:
// https://username.github.io/repo-name/one/two?a=b&c=d#qwe becomes
// https://username.github.io/repo-name/?/one/two&a=b~and~c=d#qwe
// Otherwise, leave pathSegmentsToKeep as 0.
var pathSegmentsToKeep = 0
var l = window.location
l.replace(
l.protocol +
'//' +
l.hostname +
(l.port ? ':' + l.port : '') +
l.pathname
.split('/')
.slice(0, 1 + pathSegmentsToKeep)
.join('/') +
'/?/' +
l.pathname
.slice(1)
.split('/')
.slice(pathSegmentsToKeep)
.join('/')
.replace(/&/g, '~and~') +
(l.search ? '&' + l.search.slice(1).replace(/&/g, '~and~') : '') +
l.hash
)
</script>
</head>
<body></body>
</html>

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 277 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 382 KiB

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1080 1080">
<defs>
<style>
.cls-1 {
fill: #fff;
}
</style>
</defs>
<path class="cls-1" d="M346.26,1070.9c-5.53,0-11.05-.24-16.55-.71-33.85-2.91-65.97-14.97-92.87-34.87-35.12-25.99-61.25-64.4-77.65-114.16-17.6-53.39-23.73-119.98-18.23-197.91,1.51-21.43,20.11-37.59,41.55-36.07,21.43,1.51,37.58,20.12,36.07,41.55-4.79,67.78,.1,124.33,14.52,168.07,11.27,34.18,28.1,59.74,50.03,75.97,34.85,25.79,77.61,23.3,106,12.39,41.25-15.87,71.89-36.44,60.28-142.57-12.19-111.45-69.11-296.1-190.35-617.42-16.35-43.34-31.68-85.14-43.17-117.72-5.03-14.26-9.14-26.25-11.89-34.67-1.47-4.5-2.55-7.97-3.3-10.63-.56-1.97-.97-3.58-1.28-5.07-3.55-16.78,3.5-28.44,8.03-33.89,8.93-10.76,22.99-15.92,36.76-13.49,7.31,1.29,20.79,5.93,28.98,22.47,.8,1.61,1.68,3.56,2.78,6.12,1.81,4.22,4.3,10.37,7.41,18.27,6.7,17.06,16.11,41.86,27.95,73.7,1.02,2.74,2.08,5.6,3.19,8.59,64.44-31.89,167.78-43.6,251.31-8.8,45.84,19.1,90.93,53.01,126.97,95.48,36.25,42.72,61.23,91.6,70.34,137.64,11.65,58.88-2.72,112.1-40.46,149.84-6.53,6.53-13.68,12.32-21.3,17.41,12.47,3.98,24.96,8.57,37.38,13.91,35.49,15.26,67.23,35.55,94.33,60.32,30.81,28.15,56.9,63.18,77.54,104.12,19.42,38.51,29.64,78.31,30.37,118.31,.7,38.28-7.71,75.44-24.31,107.45-28.81,55.55-80.46,92.61-141.7,101.67-54.5,8.07-100.82,3.51-141.61-13.93-38.31-16.38-70.85-43.81-99.49-83.86-2.54-3.56-5.04-7.19-7.49-10.9-10.31,65-45.42,105.7-109.31,130.27-22.52,8.66-46.71,13.12-70.8,13.12Zm121.68-501.85c5.5,16.67,10.85,33.08,16.15,49.32,39.13,119.89,70.03,214.59,113.07,274.78,41.66,58.26,89.87,77.39,166.41,66.07,36.76-5.44,66.6-26.93,84.01-60.52,22.75-43.86,20.33-101.77-6.46-154.9-69.22-137.27-190.86-151.43-298.18-163.92-26.34-3.07-51.75-6.02-74.99-10.83Zm-32-94.3c2.47,.39,5.26,.86,8.41,1.38,24.74,4.11,70.78,11.76,117.62,11.18,50.54-.62,87.08-10.76,105.65-29.33,19.34-19.34,25.79-46.16,19.15-79.71-13.72-69.37-80.01-146.85-150.91-176.39-66.87-27.87-153.56-13.19-194.29,10.66,21.21,58.09,50.17,138.2,88.82,246.56,1.87,5.25,3.72,10.47,5.54,15.65Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 2.1 KiB

View File

@ -1,21 +1,31 @@
import { Route, Routes } from 'react-router-dom'
import { Layout } from './layout'
import { routes } from './routes'
import { RouterProvider } from 'react-router-dom'
import { useEffect, useMemo } from 'react'
import { routerWithNdkContext as routerWithState } from 'routes'
import { useNDKContext } from 'hooks'
import './styles/styles.css'
function App() {
return (
<Routes>
<Route element={<Layout />}>
{routes.map((route, index) => (
<Route
key={route.path + index}
path={route.path}
element={route.element}
/>
))}
</Route>
</Routes>
)
const ndkContext = useNDKContext()
const router = useMemo(() => routerWithState(ndkContext), [ndkContext])
useEffect(() => {
// Find the element with id 'root'
const rootElement = document.getElementById('root')
if (rootElement) {
// Add the class to the element
rootElement.classList.add('bodyMain')
}
// Cleanup function (optional): Remove the class when the component unmounts
return () => {
if (rootElement) {
rootElement.classList.remove('bodyMain')
}
}
}, [])
return <RouterProvider router={router} />
}
export default App

170
src/actions/report.ts Normal file
View File

@ -0,0 +1,170 @@
import { NDKFilter } from '@nostr-dev-kit/ndk'
import { NDKContextType } from 'contexts/NDKContext'
import { kinds, nip19, UnsignedEvent } from 'nostr-tools'
import { ActionFunctionArgs } from 'react-router-dom'
import { toast } from 'react-toastify'
import { store } from 'store'
import { UserRelaysType } from 'types'
import {
log,
LogType,
now,
npubToHex,
parseFormData,
sendDMUsingRandomKey,
signAndPublish
} from 'utils'
export const reportRouteAction =
(ndkContext: NDKContextType) =>
async ({ params, request }: ActionFunctionArgs) => {
// Check which post type is reported
const url = new URL(request.url)
const isModReport = url.pathname.startsWith('/mod/')
const isBlogReport = url.pathname.startsWith('/blog/')
const title = isModReport ? 'Mod' : isBlogReport ? 'Blog' : 'Post'
const requestData = await request.formData()
const { naddr } = params
if (!naddr) {
log(true, LogType.Error, 'Required naddr.')
return false
}
// Decode author from naddr
let aTag: string | undefined
try {
const decoded = nip19.decode<'naddr'>(naddr as `naddr1${string}`)
const { identifier, kind, pubkey } = decoded.data
aTag = `${kind}:${pubkey}:${identifier}`
if (isModReport) {
aTag = identifier
}
} catch (error) {
log(true, LogType.Error, 'Failed to decode naddr')
return false
}
if (!aTag) {
log(true, LogType.Error, 'Missing #a Tag')
return false
}
const userState = store.getState().user
let hexPubkey: string | undefined
if (userState.auth && userState.user?.pubkey) {
hexPubkey = userState.user.pubkey as string
}
const reportingNpub = import.meta.env.VITE_REPORTING_NPUB
const reportingPubkey = npubToHex(reportingNpub)
// Parse the the data
const formSubmit = parseFormData(requestData)
const selectedOptionsCount = Object.values(formSubmit).filter(
(checked) => checked === 'on'
).length
if (selectedOptionsCount === 0) {
toast.error('At least one option should be checked!')
return false
}
if (reportingPubkey === hexPubkey) {
// Define the event filter to search for the user's mute list events.
// We look for events of a specific kind (Mutelist) authored by the given hexPubkey.
const filter: NDKFilter = {
kinds: [kinds.Mutelist],
authors: [hexPubkey]
}
// Fetch the mute list event from the relays. This returns the event containing the user's mute list.
const muteListEvent = await ndkContext.fetchEventFromUserRelays(
filter,
hexPubkey,
UserRelaysType.Write
)
let unsignedEvent: UnsignedEvent
if (muteListEvent) {
const tags = muteListEvent.tags
const alreadyExists =
tags.findIndex((item) => item[0] === 'a' && item[1] === aTag) !== -1
if (alreadyExists) {
toast.warn(`${title} reference is already in user's mute list`)
return false
}
tags.push(['a', aTag])
unsignedEvent = {
pubkey: muteListEvent.pubkey,
kind: kinds.Mutelist,
content: muteListEvent.content,
created_at: now(),
tags: [...tags]
}
} else {
unsignedEvent = {
pubkey: hexPubkey,
kind: kinds.Mutelist,
content: '',
created_at: now(),
tags: [['a', aTag]]
}
}
try {
hexPubkey = await window.nostr?.getPublicKey()
} catch (error) {
log(
true,
LogType.Error,
`Could not get pubkey for reporting ${title.toLowerCase()}!`,
error
)
toast.error(
`Could not get pubkey for reporting ${title.toLowerCase()}!`
)
return false
}
const isUpdated = await signAndPublish(
unsignedEvent,
ndkContext.ndk,
ndkContext.publish
)
return { isSent: isUpdated }
} else if (reportingPubkey) {
const href = window.location.href
let message = `I'd like to report ${href} due to following reasons:\n`
Object.entries(formSubmit).forEach(([key, value]) => {
if (value === 'on') {
message += `* ${key}\n`
}
})
try {
const isSent = await sendDMUsingRandomKey(
message,
reportingPubkey,
ndkContext.ndk,
ndkContext.publish
)
return { isSent: isSent }
} catch (error) {
log(
true,
LogType.Error,
`Failed to send a ${title.toLowerCase()} report`,
error
)
return false
}
} else {
log(
true,
LogType.Error,
`Failed to send a ${title.toLowerCase()} report: VITE_REPORTING_NPUB missing`
)
return false
}
}

View File

@ -0,0 +1,19 @@
[
{ "name": "gameplay ", "sub": ["difficulty"] },
{ "name": "input", "sub": ["key mapping", "macro"] },
{
"name": "visual",
"sub": ["textures", "lighting", "character models", "environment models"]
},
{ "name": "audio", "sub": ["sfx", "music", "voice"] },
{ "name": "user interface", "sub": ["hud", "menu"] },
{
"name": "quality of life",
"sub": ["bug fixes", "performance", "accessibility"]
},
"total conversions",
"translation",
"multiplayer",
"clothing",
"mod manager"
]

View File

@ -0,0 +1,3 @@
Game Name,16 by 9 image,Boxart image
Voices of the Void,,https://image.nostr.build/472949882d0756c84d3effd9f641b10c88abd48265f0f01f360937b189d50b54.jpg
Shroom and Gloom,,
1 Game Name 16 by 9 image Boxart image
2 Voices of the Void https://image.nostr.build/472949882d0756c84d3effd9f641b10c88abd48265f0f01f360937b189d50b54.jpg
3 Shroom and Gloom

View File

@ -0,0 +1,2 @@
Game Name,16 by 9 image,Boxart image
Fire Emblem Engage,,https://image.nostr.build/f9f883f88c7d1abc38b98b0aa2394684e52e10171b621011f348034ab9973476.jpg
1 Game Name 16 by 9 image Boxart image
2 Fire Emblem Engage https://image.nostr.build/f9f883f88c7d1abc38b98b0aa2394684e52e10171b621011f348034ab9973476.jpg

View File

@ -0,0 +1,14 @@
Game Name,16 by 9 image,Boxart image
(Unlisted Game),,
Minecraft,,https://image.nostr.build/b75b2d3a7855370230f2976567e2d5f913a567c57ac61adfb60c7e1102f05117.jpg
Vintage Story,,https://image.nostr.build/9efe683d339cc864032a99047ce26b2b5c19fab1ec4dcc6d4db96e2785c44eda.png
Yandere Simulator,,https://image.nostr.build/54ba56b752bb9d411cbdc1d249fa0cb74c6062a305bcd0a70ecacb61b8d50030.png
Genshin Impact,,https://image.nostr.build/999fccf93cf16a2e0dd8e6f00595b0ab3b5cc6beff9fe4a52f64f427cce9aedd.jpg
Zenless Zone Zero,,https://image.nostr.build/4a9b9c2cbef619552d0c123f8794286f35710dc7ca1ca0010380a630883eb2ca.jpg
Ananta,,https://image.nostr.build/09fb30fe2c22784079e4c0a848410e709ff359af09b3f96b651c7dc249a35cdd.jpg
Bloodborne,,https://image.nostr.build/9d20c246b539e43f1bebcf602f996fa6eb45cf585f05cc19a1d9f86a53201485.jpg
The Elder Scrolls: Skyblivion,,https://cdn.nostrcheck.me/3cea4806b1e1a9829d30d5cb8a78011d4271c6474eb31531ec91f28110fe3f40/87868c8134d0ed30e74b99750e292fc85ad708d0add24c7c9113b086cd0784c3.webp
Stellar Blade,,https://image.nostr.build/4dd76ef8ff985d2afc8b3eba323f18de7165659c4e925b2f06ae8b130372d5ae.jpg
Bayonetta 2,,https://image.nostr.build/916f0b1fd4938114867654d7625f8e817b7f710d7729c81c911e1011fa74afad.jpg
Grand Theft Auto: Vice City,,https://image.nostr.build/6f364ebb635cc878b284a06e8131dcec5eb4b574eece7ab206df8a9af639ddf6.jpg
Alan Wake 2,,https://image.nostr.build/9c185ddb6f3c3b1f56835be8d36200eda7de0f36888b02523f4d39ade235ffab.jpg
1 Game Name 16 by 9 image Boxart image
2 (Unlisted Game)
3 Minecraft https://image.nostr.build/b75b2d3a7855370230f2976567e2d5f913a567c57ac61adfb60c7e1102f05117.jpg
4 Vintage Story https://image.nostr.build/9efe683d339cc864032a99047ce26b2b5c19fab1ec4dcc6d4db96e2785c44eda.png
5 Yandere Simulator https://image.nostr.build/54ba56b752bb9d411cbdc1d249fa0cb74c6062a305bcd0a70ecacb61b8d50030.png
6 Genshin Impact https://image.nostr.build/999fccf93cf16a2e0dd8e6f00595b0ab3b5cc6beff9fe4a52f64f427cce9aedd.jpg
7 Zenless Zone Zero https://image.nostr.build/4a9b9c2cbef619552d0c123f8794286f35710dc7ca1ca0010380a630883eb2ca.jpg
8 Ananta https://image.nostr.build/09fb30fe2c22784079e4c0a848410e709ff359af09b3f96b651c7dc249a35cdd.jpg
9 Bloodborne https://image.nostr.build/9d20c246b539e43f1bebcf602f996fa6eb45cf585f05cc19a1d9f86a53201485.jpg
10 The Elder Scrolls: Skyblivion https://cdn.nostrcheck.me/3cea4806b1e1a9829d30d5cb8a78011d4271c6474eb31531ec91f28110fe3f40/87868c8134d0ed30e74b99750e292fc85ad708d0add24c7c9113b086cd0784c3.webp
11 Stellar Blade https://image.nostr.build/4dd76ef8ff985d2afc8b3eba323f18de7165659c4e925b2f06ae8b130372d5ae.jpg
12 Bayonetta 2 https://image.nostr.build/916f0b1fd4938114867654d7625f8e817b7f710d7729c81c911e1011fa74afad.jpg
13 Grand Theft Auto: Vice City https://image.nostr.build/6f364ebb635cc878b284a06e8131dcec5eb4b574eece7ab206df8a9af639ddf6.jpg
14 Alan Wake 2 https://image.nostr.build/9c185ddb6f3c3b1f56835be8d36200eda7de0f36888b02523f4d39ade235ffab.jpg

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,10 @@
Game Name,16 by 9 image,Boxart image
Marvel's Spider-Man 2,,https://image.nostr.build/b5ef5ef8bd99daab73148145b024a1e6177160fd287ce829f82ba46e821490b6.jpg
S.T.A.L.K.E.R. 2: Heart of Chornobyl,,https://image.nostr.build/f5b61071bebcc8deccfd71362696fb708649b9c528bec1c6964262ded4157843.jpg
FINAL FANTASY VII REBIRTH,,https://image.nostr.build/e16228ccfad19669f17f83517ac621142ad7129c82d0e5f346a3523643f98a28.jpg
NINJA GAIDEN 2 Black,,https://image.nostr.build/867b5d4ae7580f138b9d7e3bc41c0184e59fc22a758d2dcd9e941f8adf6d6e6e.jpg
Rise of the Ronin,,https://image.nostr.build/5eba5c6efed335e1e6c74e7af29790a9cc519dbccdd81122e84336848a7e7866.jpg
NINJA GAIDEN 4,,https://image.nostr.build/2b49b1571ba90450f95a9eb306d2ef9f3ad632dc6282125cc1651d17da17a439.jpg
Batman Arkham Asylum,,https://image.nostr.build/ba5c07be4747957380213ad86ab83b8a4cb6b8ef0123ebb9863318ed1de6e43e.jpg
Kingdom Hearts,,https://image.nostr.build/883b71c52b5b498aac20218b52af471ba89afb5cbb7072dc403da7446ca04e39.jpg
Kingdom Hearts II,,https://image.nostr.build/24b6002029e91e4ad99b56aca9f20b076feb594ae48b320ba9122254add6b57e.jpg
1 Game Name 16 by 9 image Boxart image
2 Marvel's Spider-Man 2 https://image.nostr.build/b5ef5ef8bd99daab73148145b024a1e6177160fd287ce829f82ba46e821490b6.jpg
3 S.T.A.L.K.E.R. 2: Heart of Chornobyl https://image.nostr.build/f5b61071bebcc8deccfd71362696fb708649b9c528bec1c6964262ded4157843.jpg
4 FINAL FANTASY VII REBIRTH https://image.nostr.build/e16228ccfad19669f17f83517ac621142ad7129c82d0e5f346a3523643f98a28.jpg
5 NINJA GAIDEN 2 Black https://image.nostr.build/867b5d4ae7580f138b9d7e3bc41c0184e59fc22a758d2dcd9e941f8adf6d6e6e.jpg
6 Rise of the Ronin https://image.nostr.build/5eba5c6efed335e1e6c74e7af29790a9cc519dbccdd81122e84336848a7e7866.jpg
7 NINJA GAIDEN 4 https://image.nostr.build/2b49b1571ba90450f95a9eb306d2ef9f3ad632dc6282125cc1651d17da17a439.jpg
8 Batman Arkham Asylum https://image.nostr.build/ba5c07be4747957380213ad86ab83b8a4cb6b8ef0123ebb9863318ed1de6e43e.jpg
9 Kingdom Hearts https://image.nostr.build/883b71c52b5b498aac20218b52af471ba89afb5cbb7072dc403da7446ca04e39.jpg
10 Kingdom Hearts II https://image.nostr.build/24b6002029e91e4ad99b56aca9f20b076feb594ae48b320ba9122254add6b57e.jpg

View File

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

@ -12,7 +12,7 @@
<g>
<path class="cls-1" d="M379.5,237.42V3.38h75.39c20.68,0,39.27,4.69,55.78,14.06,16.5,9.38,29.39,22.61,38.66,39.7,9.27,17.09,13.96,36.25,14.06,57.47v10.77c0,21.43-4.53,40.64-13.58,57.63-9.06,16.99-21.81,30.27-38.26,39.86-16.45,9.59-34.8,14.44-55.05,14.55h-77Zm56.42-190.48V194.02h19.61c16.18,0,28.61-5.76,37.29-17.28,8.68-11.52,13.02-28.64,13.02-51.36v-10.13c0-22.61-4.34-39.65-13.02-51.12-8.68-11.47-21.33-17.2-37.94-17.2h-18.97Z"/>
<path class="cls-1" d="M737.47,138.08h-88.73v55.94h104.8v43.4h-161.22V3.38h161.55V46.94h-105.12v49.35h88.73v41.79Z"/>
<path class="cls-1" d="M968.14,208.48c-8.68,9.64-21.38,17.42-38.1,23.31-16.72,5.89-35.04,8.84-54.97,8.84-30.65,0-55.13-9.38-73.46-28.13-18.32-18.75-28.13-44.85-29.42-78.28l-.16-20.25c0-23.04,4.07-43.16,12.22-60.36,8.14-17.2,19.8-30.43,34.96-39.7C834.37,4.64,851.92,0,871.85,0,901,0,923.64,6.67,939.77,20.01c16.13,13.34,25.53,33.25,28.21,59.72h-54.33c-1.93-13.07-6.11-22.4-12.54-27.97-6.43-5.57-15.54-8.36-27.33-8.36-14.14,0-25.08,6-32.79,18-7.72,12-11.63,29.15-11.73,51.44v14.14c0,23.36,3.99,40.91,11.98,52.64,7.98,11.73,20.55,17.6,37.69,17.6,14.68,0,25.61-3.27,32.79-9.81v-36.33h-39.22v-38.74h95.64v96.12Z"/>
<path class="cls-1" d="M968.14,208.48c-8.68,9.64-21.38,17.42-38.1,23.31-16.72,5.89-35.04,8.84-54.97,8.84-30.65,0-55.13-9.38-73.46-28.13-18.32-18.75-28.13-44.85-29.42-78.28l-.16-20.25c0-23.04,4.07-43.16,12.22-60.36,8.14-17.2,19.8-30.43,34.96-39.7,15.16-9.27,32.71-13.9,52.64-13.9,29.15,0,51.78,6.67,67.91,20.01,16.13,13.34,25.53,33.25,28.21,59.72h-54.33c-1.93-13.07-6.11-22.4-12.54-27.97-6.43-5.57-15.54-8.36-27.33-8.36-14.14,0-25.08,6-32.79,18-7.72,12-11.63,29.15-11.73,51.44v14.14c0,23.36,3.99,40.91,11.98,52.64,7.98,11.73,20.55,17.6,37.69,17.6,14.68,0,25.61-3.27,32.79-9.81v-36.33h-39.22v-38.74h95.64v96.12Z"/>
</g>
<g>
<path class="cls-1" d="M427.66,266.86l33.95,105.82,33.84-105.82h48.26v152.1h-36.77v-35.52l3.55-72.71-36.77,108.22h-24.23l-36.88-108.33,3.55,72.81v35.52h-36.67v-152.1h48.16Z"/>
@ -22,11 +22,9 @@
</g>
</g>
<g>
<path class="cls-1" d="M228.02,300.66c9.1,0,16.48-7.38,16.48-16.48s-7.38-16.48-16.48-16.48-16.48,7.38-16.48,16.48,7.38,16.48,16.48,16.48Z"/>
<path class="cls-1" d="M228.02,322.68c21.26,0,38.5-17.24,38.5-38.5s-17.24-38.5-38.5-38.5-38.5,17.24-38.5,38.5,17.24,38.5,38.5,38.5Zm0-65.72c14.92,0,27.01,12.09,27.01,27.01s-12.09,27.01-27.01,27.01-27.01-12.09-27.01-27.01,12.09-27.01,27.01-27.01Z"/>
<path class="cls-1" d="M78.57,322.68c21.26,0,38.5-17.24,38.5-38.5s-17.24-38.5-38.5-38.5-38.5,17.24-38.5,38.5,17.24,38.5,38.5,38.5Zm0-65.72c14.92,0,27.01,12.09,27.01,27.01s-12.09,27.01-27.01,27.01-27.01-12.09-27.01-27.01,12.09-27.01,27.01-27.01Z"/>
<circle class="cls-1" cx="78.57" cy="284.18" r="16.48" transform="translate(-19.95 6.28) rotate(-4.06)"/>
<path class="cls-1" d="M153.62,418.95c84.62,0,153.19-68.95,153.19-153.57,.28-16.19-2.24-35.53-7.28-53.71,0,0,0,0,0,0,0,0,0-.02,0-.02-2.61-9.42-5.9-18.53-9.82-26.73-16.4-38.76-43.83-83.92-71.53-115.33,4.09,160.17-66.59,58.67-48.5-69.58-59.22,49.02-91.1,134.42-91.1,164.47-9.62-12.19-17.37-32.1-10.81-55.35C19.61,148.65-6.72,227.18,1.48,283.63c8.87,76.18,73.58,135.32,152.14,135.32Zm49.49-216.24c13.57-4.9,27-2.15,30,6.16,3,8.31-6.9,9.93-20.47,14.83-13.57,4.9-25.66,11.23-28.66,2.93-3-8.3,5.56-19.01,19.13-23.92Zm24.9,32.22c17.3,0,32.51,8.93,41.3,22.43,3.49-2.32,7.98-5.66,13.79-10.42-.64,17.02-2.93,28.26-5.88,35.64,.02,.53,.04,1.06,.04,1.6,0,27.2-22.05,49.25-49.25,49.25s-49.25-22.05-49.25-49.25,22.05-49.25,49.25-49.25Zm-71.52,119.09c4.91-.54,10.07-1.22,15.61-2.08l15.43,13.19,10.39-17.94c6.17-1.28,12.8-2.75,19.99-4.43-3.47,34.68-25.65,53.03-56.47,56.41-30.82,3.38-62.2-12.83-74.16-39.77,23.61-1.03,53.06-3.61,69.21-5.38ZM73.5,209.05c3-8.3,16.43-11.06,30-6.16,13.57,4.9,22.13,15.61,19.13,23.92-3,8.3-15.09,1.97-28.66-2.93s-23.47-6.53-20.47-14.83Zm-36.22,48.3c8.78-13.49,23.99-22.42,41.29-22.42,27.2,0,49.25,22.05,49.25,49.25s-22.05,49.25-49.25,49.25-49.25-22.05-49.25-49.25c0-.56,.02-1.11,.04-1.66-2.94-7.37-5.22-18.6-5.86-35.57,5.8,4.75,10.29,8.08,13.77,10.4Z"/>
<path class="cls-1" d="M115.73,277.18c0-18.87-15.29-34.16-34.16-34.16s-34.16,15.29-34.16,34.16,15.29,34.16,34.16,34.16,34.16-15.29,34.16-34.16Zm-54.93-.19c0-11.47,9.3-20.77,20.77-20.77s20.77,9.3,20.77,20.77-9.3,20.77-20.77,20.77-20.77-9.3-20.77-20.77Z"/>
<path class="cls-1" d="M225.02,243.02c-18.86,0-34.16,15.29-34.16,34.16s15.29,34.16,34.16,34.16,34.16-15.29,34.16-34.16-15.29-34.16-34.16-34.16Zm0,54.74c-11.47,0-20.77-9.3-20.77-20.77s9.3-20.77,20.77-20.77,20.77,9.3,20.77,20.77-9.3,20.77-20.77,20.77Z"/>
<path class="cls-1" d="M299.53,211.67s0,0,0,0c0,0,0-.02,0-.02-2.61-9.42-5.9-18.53-9.82-26.73-16.4-38.76-43.83-83.92-71.53-115.33C222.26,229.75,151.58,128.25,169.67,0c-59.22,49.02-91.1,134.42-91.1,164.47-9.62-12.19-17.37-32.1-10.81-55.35C19.61,148.65-6.72,227.18,1.48,283.63c8.87,76.18,73.58,135.33,152.14,135.33,84.62,0,153.19-68.95,153.19-153.57,.28-16.19-2.24-35.53-7.28-53.71Zm-96.3-17.98c12.94-3.65,25.22,4.55,26.37,8.64s-8.17,11.2-21.11,14.85-24.09,4.29-25.76-1.62,7.56-18.22,20.5-21.88Zm-127.61,8.64c1.15-4.09,13.43-12.29,26.37-8.64,12.94,3.66,22.17,15.97,20.5,21.88-1.67,5.91-12.82,5.27-25.76,1.62s-22.26-10.77-21.11-14.85Zm-43.29,74.85c0-.56,.02-1.11,.04-1.66-2.94-7.37-5.22-18.6-5.86-35.57,5.8,4.75,10.29,8.08,13.77,10.4,8.78-13.49,23.99-22.42,41.29-22.42,27.2,0,49.25,22.05,49.25,49.25s-22.05,49.25-49.25,49.25-49.25-22.05-49.25-49.25Zm130.22,125.42c-34.61,3.8-69.85-14.41-83.27-44.65,26.51-1.15,59.58-4.05,77.71-6.04,5.51-.6,11.3-1.37,17.53-2.34l17.33,14.81,11.66-20.15c6.93-1.44,14.38-3.09,22.45-4.98-3.9,38.94-28.8,59.55-63.41,63.34Zm111.73-125.42c0,27.2-22.05,49.25-49.25,49.25s-49.25-22.05-49.25-49.25,22.05-49.25,49.25-49.25c17.3,0,32.51,8.93,41.3,22.43,3.49-2.32,7.98-5.66,13.79-10.42-.64,17.02-2.93,28.26-5.88,35.64,.02,.53,.04,1.06,.04,1.6Z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.6 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

View File

Before

Width:  |  Height:  |  Size: 6.5 KiB

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 687 B

After

Width:  |  Height:  |  Size: 687 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 284 KiB

After

Width:  |  Height:  |  Size: 284 KiB

View File

@ -0,0 +1,72 @@
import { createPortal } from 'react-dom'
import { AlertPopupProps } from 'types'
export const AlertPopup = ({
header,
label,
handleConfirm,
handleClose
}: AlertPopupProps) => {
return createPortal(
<div className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard popUpMainCardQR'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>{header}</h3>
</div>
<div className='popUpMainCardTopClose' onClick={handleClose}>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
style={{ zIndex: 1 }}
>
<path d='M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z'></path>
</svg>
</div>
</div>
<div className='pUMCB_Zaps'>
<div className='pUMCB_ZapsInside'>
<div className='inputLabelWrapperMain'>
<label
className='form-label labelMain'
style={{ fontWeight: 'bold' }}
>
{label}
</label>
</div>
<div
style={{
display: 'flex',
width: '100%',
gap: '10px'
}}
>
<button
className='btn btnMain btnMainPopup'
type='button'
onPointerDown={() => handleConfirm(true)}
>
Yes
</button>
<button
className='btn btnMain btnMainPopup'
type='button'
onPointerDown={() => handleConfirm(false)}
>
No
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
document.body
)
}

View File

@ -3,14 +3,12 @@ import navStyles from '../styles/nav.module.scss'
export const Banner = () => {
return (
<div className={navStyles.FundingCampaign}>
<a
<p
className={navStyles.FundingCampaignLink}
href='https://geyser.fund/project/degmods'
target='_blank'
>
DEG Mods is running a crowd funding campaign. Chip-in or share the link
to help bring the project to life (click me).
</a>
DEG Mods is currently in alpha (<a href="https://geyser.fund/project/degmods/posts/view/3411" target="_blank">Learn more</a>).
Check out its funding campaign (<a href="https://geyser.fund/project/degmods" target="_blank">Learn more</a>). (Soon on Kickstarter)
</p>
</div>
)
}

View File

@ -1,41 +1,33 @@
import { Link } from 'react-router-dom'
import { BlogCardDetails } from 'types'
import { getBlogPageRoute } from 'routes'
import '../styles/cardBlogs.css'
import placeholder from '../assets/img/DEGMods Placeholder Img.png'
type BlogCardProps = {
backgroundLink: string
}
type BlogCardProps = Partial<BlogCardDetails>
export const BlogCard = ({ title, image, nsfw, naddr }: BlogCardProps) => {
if (!naddr) return null
export const BlogCard = ({ backgroundLink }: BlogCardProps) => {
return (
<a className='cardBlogMainWrapperLink' href='blog-inner.html'>
<Link to={getBlogPageRoute(naddr)} className='cardBlogMainWrapperLink'>
<div
className='cardBlogMain'
style={{
background: `url("${backgroundLink}") center / cover no-repeat`
background: `url("${
image ? image : placeholder
}") center / cover no-repeat`
}}
>
<div
className='cardBlogMainInside'
style={{
background:
'linear-gradient( rgba(255, 255, 255, 0) 0%, #232323 100%)'
}}
>
<h3
style={{
display: '-webkit-box',
WebkitBoxOrient: 'vertical',
overflow: 'hidden',
WebkitLineClamp: 2,
fontSize: '20px',
lineHeight: 1.5,
color: 'rgba(255, 255, 255, 0.75)',
textShadow: '0 0 8px rgba(0, 0, 0, 0.25)'
}}
>
This is a blog title, the best blog title in the world!
</h3>
<div className='cardBlogMainInside'>
<h3 className='cardBlogMainInsideTitle'>{title}</h3>
{nsfw && (
<div className='IBMSMSMBSSTagsTag IBMSMSMBSSTagsTagNSFW IBMSMSMBSSTagsTagNSFWCard IBMSMSMBSSTagsTagNSFWCardAlt'>
<p>NSFW</p>
</div>
)}
</div>
</div>{' '}
</a>
</div>
</Link>
)
}

View File

@ -0,0 +1,333 @@
import { useLocalStorage } from 'hooks'
import { useMemo, useState } from 'react'
import { Link } from 'react-router-dom'
import { getGamePageRoute } from 'routes'
import { ModFormState, Categories, Category } from 'types'
import {
getCategories,
flattenCategories,
addToUserCategories,
capitalizeEachWord
} from 'utils'
interface CategoryAutocompleteProps {
game: string
LTags: string[]
setFormState: (value: React.SetStateAction<ModFormState>) => void
}
export const CategoryAutocomplete = ({
game,
LTags,
setFormState
}: CategoryAutocompleteProps) => {
// Fetch the hardcoded categories from assets
const flattenedCategories = useMemo(() => getCategories(), [])
// Fetch the user categories from local storage
const [userHierarchies, setUserHierarchies] = useLocalStorage<
(string | Category)[]
>('user-hierarchies', [])
const flattenedUserCategories = useMemo(
() => flattenCategories(userHierarchies, []),
[userHierarchies]
)
// Create options and select categories from the mod LTags (hierarchies)
const { selectedCategories, combinedOptions } = useMemo(() => {
const combinedCategories = [
...flattenedCategories,
...flattenedUserCategories
]
const hierarchies = LTags.map((hierarchy) => {
const existingCategory = combinedCategories.find(
(cat) => cat.hierarchy === hierarchy.replace(/:/g, ' > ')
)
if (existingCategory) {
return existingCategory
} else {
const segments = hierarchy.split(':')
const lastSegment = segments[segments.length - 1]
return { name: lastSegment, hierarchy: hierarchy, l: [lastSegment] }
}
})
// Selected categorires (based on the LTags)
const selectedCategories = Array.from(new Set([...hierarchies]))
// Combine user, predefined category hierarchies and selected values (LTags in case some are missing)
const combinedOptions = Array.from(
new Set([...combinedCategories, ...selectedCategories])
)
return { selectedCategories, combinedOptions }
}, [LTags, flattenedCategories, flattenedUserCategories])
const [inputValue, setInputValue] = useState<string>('')
const filteredOptions = useMemo(
() =>
combinedOptions.filter((option) =>
option.hierarchy.toLowerCase().includes(inputValue.toLowerCase())
),
[combinedOptions, inputValue]
)
const getSelectedCategories = (cats: Categories[]) => {
const uniqueValues = new Set(
cats.reduce<string[]>((prev, cat) => [...prev, ...cat.l], [])
)
const concatenatedValue = Array.from(uniqueValues)
return concatenatedValue
}
const getSelectedHierarchy = (cats: Categories[]) => {
const hierarchies = cats.reduce<string[]>(
(prev, cat) => [...prev, cat.hierarchy.replace(/ > /g, ':')],
[]
)
const concatenatedValue = Array.from(hierarchies)
return concatenatedValue
}
const handleReset = () => {
setFormState((prevState) => ({
...prevState,
['lTags']: [],
['LTags']: []
}))
setInputValue('')
}
const handleRemove = (option: Categories) => {
const updatedCategories = selectedCategories.filter(
(cat) => cat.hierarchy !== option.hierarchy
)
setFormState((prevState) => ({
...prevState,
['lTags']: getSelectedCategories(updatedCategories),
['LTags']: getSelectedHierarchy(updatedCategories)
}))
}
const handleSelect = (option: Categories) => {
if (!selectedCategories.some((cat) => cat.hierarchy === option.hierarchy)) {
const updatedCategories = [...selectedCategories, option]
setFormState((prevState) => ({
...prevState,
['lTags']: getSelectedCategories(updatedCategories),
['LTags']: getSelectedHierarchy(updatedCategories)
}))
}
setInputValue('')
}
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value)
}
const handleAddNew = () => {
if (inputValue) {
const value = inputValue.trim().toLowerCase()
const values = value.split('>').map((s) => s.trim())
const newOption: Categories = {
name: value,
hierarchy: value,
l: values
}
setUserHierarchies((prev) => {
addToUserCategories(prev, value)
return [...prev]
})
const updatedCategories = [...selectedCategories, newOption]
setFormState((prevState) => ({
...prevState,
['lTags']: getSelectedCategories(updatedCategories),
['LTags']: getSelectedHierarchy(updatedCategories)
}))
setInputValue('')
}
}
const handleAddNewCustom = (option: Categories) => {
setUserHierarchies((prev) => {
addToUserCategories(prev, option.hierarchy)
return [...prev]
})
}
const Row = ({ index }: { index: number }) => {
return (
<div
className='dropdown-item dropdownMainMenuItem dropdownMainMenuItemCategory'
onClick={() => handleSelect(filteredOptions[index])}
>
{capitalizeEachWord(filteredOptions[index].hierarchy)}
{/* Show "Remove" button when the category is selected */}
{selectedCategories.some(
(cat) => cat.hierarchy === filteredOptions[index].hierarchy
) && (
<button
type='button'
className='btn btnMain btnMainInsideField btnMainRemove'
onClick={() => handleRemove(filteredOptions[index])}
title='Remove'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M323.3 32.01H188.7C172.3 32.01 160 44.31 160 60.73V96.01H32C14.33 96.01 0 110.3 0 128S14.33 160 32 160H480c17.67 0 32-14.33 32-32.01S497.7 96.01 480 96.01H352v-35.28C352 44.31 339.7 32.01 323.3 32.01zM64.9 477.5C66.5 492.3 79.31 504 94.72 504H417.3c15.41 0 28.22-11.72 29.81-26.5L480 192.2H32L64.9 477.5z'></path>
</svg>
</button>
)}
{/* Show "Add" button when the category is not included in the predefined or userdefined lists */}
{!flattenedCategories.some(
(cat) => cat.hierarchy === filteredOptions[index].hierarchy
) &&
!flattenedUserCategories.some(
(cat) => cat.hierarchy === filteredOptions[index].hierarchy
) && (
<button
type='button'
className='btn btnMain btnMainInsideField btnMainAdd'
onClick={() => handleAddNewCustom(filteredOptions[index])}
title='Add'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M432 256c0 17.69-14.33 32.01-32 32.01H256v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144H48c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99H192v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144C417.7 224 432 238.3 432 256z'></path>
</svg>
</button>
)}
</div>
)
}
return (
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>Categories</label>
<p className='labelDescriptionMain'>You can select multiple categories</p>
<div className='dropdown dropdownMain'>
<div className='inputWrapperMain inputWrapperMainAlt'>
<input
type='text'
className='inputMain inputMainWithBtn dropdown-toggle'
placeholder='Select some categories...'
data-bs-toggle='dropdown'
value={inputValue}
onChange={handleInputChange}
/>
<button
className='btn btnMain btnMainInsideField btnMainRemove'
title='Remove'
type='button'
onClick={handleReset}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M480 416C497.7 416 512 430.3 512 448C512 465.7 497.7 480 480 480H150.6C133.7 480 117.4 473.3 105.4 461.3L25.37 381.3C.3786 356.3 .3786 315.7 25.37 290.7L258.7 57.37C283.7 32.38 324.3 32.38 349.3 57.37L486.6 194.7C511.6 219.7 511.6 260.3 486.6 285.3L355.9 416H480zM265.4 416L332.7 348.7L195.3 211.3L70.63 336L150.6 416L265.4 416z'></path>
</svg>
</button>
<div
className='dropdown-menu dropdownMainMenu dropdownMainMenuAlt category'
style={{
maxHeight: '500px'
}}
>
{filteredOptions.length > 0 ? (
filteredOptions.map((c, i) => <Row key={c.hierarchy} index={i} />)
) : (
<div
className='dropdown-item dropdownMainMenuItem dropdownMainMenuItemCategory'
onClick={handleAddNew}
>
{inputValue &&
!filteredOptions?.find(
(option) =>
option.hierarchy.toLowerCase() === inputValue.toLowerCase()
) ? (
<>
Add "{inputValue}"
<button
type='button'
className='btn btnMain btnMainInsideField btnMainAdd'
title='Add'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M432 256c0 17.69-14.33 32.01-32 32.01H256v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144H48c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99H192v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144C417.7 224 432 238.3 432 256z'></path>
</svg>
</button>
</>
) : (
<>No matches</>
)}
</div>
)}
</div>
</div>
</div>
{LTags.length > 0 && (
<div className='IBMSMSMBSSCategories'>
{LTags.map((hierarchy) => {
const hierarchicalCategories = hierarchy.split(`:`)
const categories = hierarchicalCategories
.map<React.ReactNode>((c, i) => {
const partialHierarchy = hierarchicalCategories
.slice(0, i + 1)
.join(':')
return game ? (
<Link
key={`category-${i}`}
target='_blank'
to={{
pathname: getGamePageRoute(game),
search: `h=${partialHierarchy}`
}}
className='IBMSMSMBSSCategoriesBoxItem'
>
<p>{capitalizeEachWord(c)}</p>
</Link>
) : (
<p className='IBMSMSMBSSCategoriesBoxItem'>
{capitalizeEachWord(c)}
</p>
)
})
.reduce((prev, curr, i) => [
prev,
<div
key={`separator-${i}`}
className='IBMSMSMBSSCategoriesBoxSeparator'
>
<p>&gt;</p>
</div>,
curr
])
return (
<div key={hierarchy} className='IBMSMSMBSSCategoriesBox'>
{categories}
</div>
)
})}
</div>
)}
</div>
)
}

View File

@ -0,0 +1,113 @@
import { createPortal } from 'react-dom'
import { DownloadUrl } from '../types'
export const DownloadDetailsPopup = ({
title,
url,
hash,
signatureKey,
malwareScanLink,
modVersion,
customNote,
mediaUrl,
handleClose
}: DownloadUrl & {
handleClose: () => void
}) => {
return createPortal(
<div className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>{title || 'Authentication Details'}</h3>
</div>
<div className='popUpMainCardTopClose' onClick={handleClose}>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
style={{ zIndex: 1 }}
>
<path d='M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z'></path>
</svg>
</div>
</div>
<div className='pUMCB_Zaps'>
<div className='pUMCB_ZapsInside'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTable'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRow'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol IBMSMSMBSSDownloadsElementInsideAltTableRowColFirst'>
<p>Download URL</p>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol'>
<p>{url}</p>
</div>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRow'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol IBMSMSMBSSDownloadsElementInsideAltTableRowColFirst'>
<p>SHA-256 hash</p>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol'>
<p>{hash}</p>
</div>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRow'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol IBMSMSMBSSDownloadsElementInsideAltTableRowColFirst'>
<p>Signature from</p>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol'>
<p>{signatureKey}</p>
</div>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRow'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol IBMSMSMBSSDownloadsElementInsideAltTableRowColFirst'>
<p>Scan</p>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol'>
<p>{malwareScanLink}</p>
</div>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRow'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol IBMSMSMBSSDownloadsElementInsideAltTableRowColFirst'>
<p>Mod Version</p>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol'>
<p>{modVersion}</p>
</div>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRow'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol IBMSMSMBSSDownloadsElementInsideAltTableRowColFirst'>
<p>Note</p>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol'>
<p>{customNote}</p>
</div>
</div>
{typeof mediaUrl !== 'undefined' && mediaUrl !== '' && (
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRow'>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol IBMSMSMBSSDownloadsElementInsideAltTableRowColFirst'>
<p>Media</p>
</div>
<div className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol'>
<img
src={mediaUrl}
className='IBMSMSMBSSDownloadsElementInsideAltTableRowCol_Img'
alt=''
/>
</div>
</div>
)}
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
document.body
)
}

View File

@ -0,0 +1,48 @@
import { Component, ErrorInfo, ReactNode } from 'react'
// Define the state interface for error boundary
interface ErrorBoundaryState {
hasError: boolean
}
// Define the props interface (if you want to pass any props)
interface ErrorBoundaryProps {
children: ReactNode
}
export class ErrorBoundary extends Component<
ErrorBoundaryProps,
ErrorBoundaryState
> {
constructor(props: ErrorBoundaryProps) {
super(props)
this.state = { hasError: false }
}
// Update state so the next render will show the fallback UI.
static getDerivedStateFromError(_: Error): ErrorBoundaryState {
return { hasError: true }
}
// Log the error and error info (optional)
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error('Uncaught error:', error, errorInfo)
// You could also send the error to a logging service here
console.error('props', this.props)
}
render() {
if (this.state.hasError) {
// You can render any fallback UI here
return (
<div>
<h1>Oops! Something went wrong.</h1>
<p>Please check console.</p>
</div>
)
}
// If no error, render children
return this.props.children
}
}

View File

@ -0,0 +1,148 @@
import { useAppSelector, useLocalStorage } from 'hooks'
import React from 'react'
import { FilterOptions, ModeratedFilter, SortBy, WOTFilterOptions } from 'types'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
import { Dropdown } from './Dropdown'
import { Option } from './Option'
import { Filter } from '.'
import { NsfwFilterOptions } from './NsfwFilterOptions'
type Props = {
author?: string | undefined
filterKey?: string | undefined
}
export const BlogsFilter = React.memo(
({ author, filterKey = 'filter-blog' }: Props) => {
const userState = useAppSelector((state) => state.user)
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
return (
<Filter>
{/* sort filter options */}
<Dropdown label={filterOptions.sort}>
{Object.values(SortBy).map((item, index) => (
<div
key={`sortByItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
sort: item
}))
}
>
{item}
</div>
))}
</Dropdown>
{/* moderation filter options */}
<Dropdown label={filterOptions.moderated}>
{Object.values(ModeratedFilter).map((item) => {
if (item === ModeratedFilter.Unmoderated_Fully) {
const isAdmin =
userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
const isOwnProfile =
author && userState.auth && userState.user?.pubkey === author
if (!(isAdmin || isOwnProfile)) return null
}
return (
<Option
key={`sort-${item}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
moderated: item
}))
}
>
{item}
</Option>
)
})}
</Dropdown>
{/* wot filter options */}
<Dropdown label={<>Trust: {filterOptions.wot}</>}>
{Object.values(WOTFilterOptions).map((item, index) => {
// when user is not logged in
if (item === WOTFilterOptions.Site_And_Mine && !userState.auth) {
return null
}
// when logged in user not admin
if (
item === WOTFilterOptions.None ||
item === WOTFilterOptions.Mine_Only ||
item === WOTFilterOptions.Exclude
) {
const isWoTNpub =
userState.user?.npub === import.meta.env.VITE_SITE_WOT_NPUB
const isOwnProfile =
author && userState.auth && userState.user?.pubkey === author
if (!(isWoTNpub || isOwnProfile)) return null
}
return (
<Option
key={`wotFilterOption-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
wot: item
}))
}
>
{item}
</Option>
)
})}
</Dropdown>
{/* nsfw filter options */}
<Dropdown label={filterOptions.nsfw}>
<NsfwFilterOptions filterKey={filterKey} />
</Dropdown>
{/* source filter options */}
<Dropdown
label={
filterOptions.source === window.location.host
? `Show From: ${filterOptions.source}`
: 'Show All'
}
>
<Option
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: window.location.host
}))
}
>
Show From: {window.location.host}
</Option>
<Option
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: 'Show All'
}))
}
>
Show All
</Option>
</Dropdown>
</Filter>
)
}
)

View File

@ -0,0 +1,3 @@
.noResult:not(:only-child) {
display: none;
}

View File

@ -0,0 +1,550 @@
import React, { useEffect, useMemo, useState } from 'react'
import { createPortal } from 'react-dom'
import { Category } from 'types'
import {
addToUserCategories,
capitalizeEachWord,
deleteFromUserCategories,
flattenCategories
} from 'utils'
import { useLocalStorage } from 'hooks'
import { useSearchParams } from 'react-router-dom'
import styles from './CategoryFilterPopup.module.scss'
import categoriesData from './../../assets/categories/categories.json'
interface CategoryFilterPopupProps {
categories: string[]
setCategories: React.Dispatch<React.SetStateAction<string[]>>
hierarchies: string[]
setHierarchies: React.Dispatch<React.SetStateAction<string[]>>
handleClose: () => void
}
export const CategoryFilterPopup = ({
categories,
setCategories,
hierarchies,
setHierarchies,
handleClose
}: CategoryFilterPopupProps) => {
const [searchParams, setSearchParams] = useSearchParams()
const linkedHierarchy = searchParams.get('h')
const [userHierarchies, setUserHierarchies] = useLocalStorage<
(string | Category)[]
>('user-hierarchies', [])
const [filterCategories, setFilterCategories] = useState(categories)
const [filterHierarchies, setFilterHierarchies] = useState(hierarchies)
const handleApply = () => {
// Update selection with linked category if it exists
if (linkedHierarchy !== null && linkedHierarchy !== '') {
// Combine existing selection with the linked
setFilterHierarchies((prev) => {
prev.push(linkedHierarchy)
const newFilterHierarchies = Array.from(new Set([...prev]))
setHierarchies(newFilterHierarchies)
return newFilterHierarchies
})
// Clear hierarchy link in search params
searchParams.delete('h')
setSearchParams(searchParams)
} else {
setHierarchies(filterHierarchies)
}
setCategories(filterCategories)
}
const [inputValue, setInputValue] = useState<string>('')
const userHierarchiesMatching = useMemo(
() =>
flattenCategories(userHierarchies, []).some((h) =>
h.hierarchy.includes(inputValue.toLowerCase())
),
[inputValue, userHierarchies]
)
// const hierarchiesMatching = useMemo(
// () =>
// flattenCategories(categoriesData, []).some((h) =>
// h.hierarchy.includes(inputValue.toLowerCase())
// ),
// [inputValue]
// )
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setInputValue(e.target.value)
}
const handleSingleSelection = (category: string, isSelected: boolean) => {
let updatedCategories = [...filterCategories]
if (isSelected) {
updatedCategories.push(category)
} else {
updatedCategories = updatedCategories.filter((item) => item !== category)
}
setFilterCategories(updatedCategories)
}
const handleCombinationSelection = (path: string[], isSelected: boolean) => {
const pathString = path.join(':')
let updatedHierarchies = [...filterHierarchies]
if (isSelected) {
updatedHierarchies.push(pathString)
} else {
updatedHierarchies = updatedHierarchies.filter(
(item) => item !== pathString
)
}
setFilterHierarchies(updatedHierarchies)
}
const handleAddNew = () => {
if (inputValue) {
const value = inputValue.toLowerCase()
const values = value
.trim()
.split('>')
.map((s) => s.trim())
setUserHierarchies((prev) => {
addToUserCategories(prev, value)
return [...prev]
})
const path = values.join(':')
// Add new hierarchy to current selection and active selection
// Convert through set to remove duplicates
setFilterHierarchies((prev) => {
prev.push(path)
return Array.from(new Set([...prev]))
})
setHierarchies((prev) => {
prev.push(path)
return Array.from(new Set([...prev]))
})
setInputValue('')
}
}
return createPortal(
<div className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>Categories filter</h3>
</div>
<div className='popUpMainCardTopClose' onClick={handleClose}>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
style={{ zIndex: 1 }}
>
<path d='M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z'></path>
</svg>
</div>
</div>
<div className='pUMCB_Zaps'>
<div className='pUMCB_ZapsInside'>
<div className='inputLabelWrapperMain'>
<label
className='form-label labelMain'
style={{ fontWeight: 'bold' }}
>
Choose categories...
</label>
<p className='labelDescriptionMain'>
Choose one or more pre-definied or custom categories to filter out mods with.
</p>
</div>
<input
type='text'
className='inputMain inputMainWithBtn'
placeholder='Select some categories...'
value={inputValue}
onChange={handleInputChange}
/>
{userHierarchies.length > 0 && (
<>
<div className='inputLabelWrapperMain'>
<label
className='form-label labelMain'
style={{ fontWeight: 'bold' }}
>
Custom categories
</label>
<p className='labelDescriptionMain'>Here&apos;s where your custom categories appear (You can add them in the above field. Example &gt; banana &gt; seed)</p>
</div>
<div
className='inputMain'
style={{
minHeight: '40px',
maxHeight: '500px',
height: '100%',
overflow: 'auto'
}}
>
{!userHierarchiesMatching && <div>No results.</div>}
{userHierarchies
.filter((c) => typeof c !== 'string')
.map((c, i) => (
<CategoryCheckbox
key={`${c}_${i}`}
inputValue={inputValue}
category={c}
path={[c.name]}
handleSingleSelection={handleSingleSelection}
handleCombinationSelection={
handleCombinationSelection
}
selectedSingles={filterCategories}
selectedCombinations={filterHierarchies}
handleRemove={(path) => {
setUserHierarchies((prev) => {
deleteFromUserCategories(prev, path.join('>'))
return [...prev]
})
// Remove the deleted hierarchies from current filter selection and active selection
setFilterHierarchies((prev) =>
prev.filter(
(h) => !h.startsWith(path.join(':'))
)
)
setHierarchies((prev) =>
prev.filter(
(h) => !h.startsWith(path.join(':'))
)
)
}}
/>
))}
</div>
</>
)}
<div className='inputLabelWrapperMain'>
<div className='inputLabelWrapperMain'>
<label
className='form-label labelMain'
style={{ fontWeight: 'bold' }}
>
Categories
</label>
<p className='labelDescriptionMain'>Here&apos;s where you select any of the pre-defined categories</p>
</div>
<div
className='inputMain'
style={{
minHeight: '40px',
maxHeight: '500px',
height: '100%',
overflow: 'auto'
}}
>
<div className={`${styles.noResult}`}>
<div>No results.</div>
<br />
{userHierarchiesMatching ? (
<div>Already defined in your categories</div>
) : (
<div
className='dropdown-item dropdownMainMenuItem dropdownMainMenuItemCategory'
onClick={handleAddNew}
>
Add and search for "{inputValue}" category
<button
type='button'
className='btn btnMain btnMainInsideField btnMainAdd'
title='Add'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M432 256c0 17.69-14.33 32.01-32 32.01H256v144c0 17.69-14.33 31.99-32 31.99s-32-14.3-32-31.99v-144H48c-17.67 0-32-14.32-32-32.01s14.33-31.99 32-31.99H192v-144c0-17.69 14.33-32.01 32-32.01s32 14.32 32 32.01v144h144C417.7 224 432 238.3 432 256z'></path>
</svg>
</button>
</div>
)}
</div>
{(categoriesData as Category[]).map((category) => {
const name =
typeof category === 'string' ? category : category.name
return (
<CategoryCheckbox
key={name}
inputValue={inputValue}
category={category}
path={[name]}
handleSingleSelection={handleSingleSelection}
handleCombinationSelection={
handleCombinationSelection
}
selectedSingles={filterCategories}
selectedCombinations={filterHierarchies}
/>
)
})}
</div>
</div>
<div
style={{
display: 'flex',
width: '100%',
gap: '10px'
}}
>
<button
className='btn btnMain btnMainPopup'
type='button'
onPointerDown={handleClose}
>
Cancel
</button>
<button
className='btn btnMain btnMainPopup'
type='button'
onPointerDown={() => {
// Clear the linked hierarchy
searchParams.delete('h')
setSearchParams(searchParams)
// Clear current filters
setFilterCategories([])
setFilterHierarchies([])
}}
>
Reset
</button>
<button
className='btn btnMain btnMainPopup'
type='button'
onPointerDown={() => {
handleApply()
handleClose()
}}
>
Apply
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>,
document.body
)
}
interface CategoryCheckboxProps {
inputValue: string
category: Category | string
path: string[]
handleSingleSelection: (category: string, isSelected: boolean) => void
handleCombinationSelection: (path: string[], isSelected: boolean) => void
selectedSingles: string[]
selectedCombinations: string[]
indentLevel?: number
handleRemove?: (path: string[]) => void
}
const CategoryCheckbox: React.FC<CategoryCheckboxProps> = ({
inputValue,
category,
path,
handleSingleSelection,
handleCombinationSelection,
selectedSingles,
selectedCombinations,
indentLevel = 0,
handleRemove
}) => {
const [searchParams, setSearchParams] = useSearchParams()
const linkedHierarchy = searchParams.get('h')
const name = typeof category === 'string' ? category : category.name
const hierarchy = path.join(' > ').toLowerCase()
const isMatching = hierarchy.includes(inputValue.toLowerCase())
const isLinked =
linkedHierarchy !== null &&
hierarchy === linkedHierarchy.replace(/:/g, ' > ')
const [isSingleChecked, setIsSingleChecked] = useState<boolean>(false)
const [isCombinationChecked, setIsCombinationChecked] =
useState<boolean>(false)
const [isIndeterminate, setIsIndeterminate] = useState<boolean>(false)
useEffect(() => {
const pathString = path.join(':')
setIsSingleChecked(selectedSingles.includes(name))
setIsCombinationChecked(selectedCombinations.includes(pathString))
// Recursive function to gather all descendant paths
const collectChildPaths = (
category: string | Category,
basePath: string[]
) => {
if (!category.sub || !Array.isArray(category.sub)) {
return []
}
let paths: string[] = []
for (const sub of category.sub) {
const subPath =
typeof sub === 'string'
? [...basePath, sub].join(':')
: [...basePath, sub.name].join(':')
paths.push(subPath)
if (typeof sub === 'object') {
paths = paths.concat(collectChildPaths(sub, [...basePath, sub.name]))
}
}
return paths
}
const childPaths = collectChildPaths(category, path)
const anyChildCombinationSelected = childPaths.some((childPath) =>
selectedCombinations.includes(childPath)
)
const anyChildCombinationLinked = childPaths.some(
(childPath) =>
linkedHierarchy !== null && linkedHierarchy.includes(childPath)
)
setIsIndeterminate(
(anyChildCombinationSelected || anyChildCombinationLinked) &&
!selectedCombinations.includes(pathString)
)
}, [
category,
linkedHierarchy,
name,
path,
selectedCombinations,
selectedSingles
])
const handleSingleChange = () => {
setIsSingleChecked(!isSingleChecked)
handleSingleSelection(name, !isSingleChecked)
}
const handleCombinationChange = () => {
// If combination is linked, clicking it again we will delete it
if (isLinked) {
searchParams.delete('h')
setSearchParams(searchParams)
} else {
setIsCombinationChecked(!isCombinationChecked)
handleCombinationSelection(path, !isCombinationChecked)
}
}
return (
<>
{isMatching && (
<div
className='dropdown-item dropdownMainMenuItem dropdownMainMenuItemCategory dropdownMainMenuItemCategoryAlt'
style={{
marginLeft: `${indentLevel * 20}px`,
width: `calc(100% - ${indentLevel * 20}px)`
}}
>
<div
className={`inputLabelWrapperMain inputLabelWrapperMainAlt stylized`}
style={{
overflow: 'hidden'
}}
>
<input
id={name}
type='checkbox'
ref={(input) => {
if (input) {
input.indeterminate = isIndeterminate
}
}}
className={`CheckboxMain ${
isIndeterminate ? 'CheckboxIndeterminate' : ''
}`}
checked={isCombinationChecked || isLinked}
onChange={handleCombinationChange}
/>
<label
htmlFor={name}
className='form-label labelMain labelMainCategory'
>
{capitalizeEachWord(name)}
</label>
<input
style={{
display: 'none'
}}
id={name}
type='checkbox'
className='CheckboxMain'
name={name}
checked={isSingleChecked}
onChange={handleSingleChange}
/>
{typeof handleRemove === 'function' && (
<button
className='btn btnMain btnMainInsideField btnMainRemove'
title='Remove'
type='button'
onClick={() => handleRemove(path)}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M323.3 32.01H188.7C172.3 32.01 160 44.31 160 60.73V96.01H32C14.33 96.01 0 110.3 0 128S14.33 160 32 160H480c17.67 0 32-14.33 32-32.01S497.7 96.01 480 96.01H352v-35.28C352 44.31 339.7 32.01 323.3 32.01zM64.9 477.5C66.5 492.3 79.31 504 94.72 504H417.3c15.41 0 28.22-11.72 29.81-26.5L480 192.2H32L64.9 477.5z'></path>
</svg>
</button>
)}
</div>
</div>
)}
{typeof category !== 'string' &&
category.sub &&
Array.isArray(category.sub) && (
<>
{category.sub.map((subCategory) => {
if (typeof subCategory === 'string') {
return (
<CategoryCheckbox
inputValue={inputValue}
key={`${category.name}-${subCategory}`}
category={{ name: subCategory }}
path={[...path, subCategory]}
handleSingleSelection={handleSingleSelection}
handleCombinationSelection={handleCombinationSelection}
selectedSingles={selectedSingles}
selectedCombinations={selectedCombinations}
indentLevel={indentLevel + 1}
handleRemove={handleRemove}
/>
)
} else {
return (
<CategoryCheckbox
inputValue={inputValue}
key={subCategory.name}
category={subCategory}
path={[...path, subCategory.name]}
handleSingleSelection={handleSingleSelection}
handleCombinationSelection={handleCombinationSelection}
selectedSingles={selectedSingles}
selectedCombinations={selectedCombinations}
indentLevel={indentLevel + 1}
handleRemove={handleRemove}
/>
)
}
})}
</>
)}
</>
)
}

View File

@ -0,0 +1,25 @@
import { PropsWithChildren } from 'react'
interface DropdownProps {
label: React.ReactNode
}
export const Dropdown = ({
label,
children
}: PropsWithChildren<DropdownProps>) => {
return (
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{label}
</button>
<div className='dropdown-menu dropdownMainMenu'>{children}</div>
</div>
</div>
)
}

View File

@ -0,0 +1,176 @@
import { useAppSelector, useLocalStorage } from 'hooks'
import React, { PropsWithChildren } from 'react'
import {
FilterOptions,
SortBy,
ModeratedFilter,
WOTFilterOptions,
RepostFilter
} from 'types'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
import { Filter } from '.'
import { Dropdown } from './Dropdown'
import { Option } from './Option'
import { NsfwFilterOptions } from './NsfwFilterOptions'
type Props = {
author?: string | undefined
filterKey?: string | undefined
}
export const ModFilter = React.memo(
({ author, filterKey = 'filter', children }: PropsWithChildren<Props>) => {
const userState = useAppSelector((state) => state.user)
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
return (
<Filter>
{/* sort filter options */}
<Dropdown label={filterOptions.sort}>
{Object.values(SortBy).map((item, index) => (
<Option
key={`sortByItem-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
sort: item
}))
}
>
{item}
</Option>
))}
</Dropdown>
{/* moderation filter options */}
<Dropdown label={filterOptions.moderated}>
{Object.values(ModeratedFilter).map((item, index) => {
const isAdmin =
userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
if (item === ModeratedFilter.Only_Blocked && !isAdmin) {
return null
}
if (item === ModeratedFilter.Unmoderated_Fully) {
const isOwnProfile =
author && userState.auth && userState.user?.pubkey === author
if (!(isAdmin || isOwnProfile)) return null
}
return (
<Option
key={`moderatedFilterItem-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
moderated: item
}))
}
>
{item}
</Option>
)
})}
</Dropdown>
{/* wot filter options */}
<Dropdown label={<>Trust: {filterOptions.wot}</>}>
{Object.values(WOTFilterOptions).map((item, index) => {
// when user is not logged in
if (item === WOTFilterOptions.Site_And_Mine && !userState.auth) {
return null
}
// when logged in user not admin
if (
item === WOTFilterOptions.None ||
item === WOTFilterOptions.Mine_Only ||
item === WOTFilterOptions.Exclude
) {
const isWoTNpub =
userState.user?.npub === import.meta.env.VITE_SITE_WOT_NPUB
const isOwnProfile =
author && userState.auth && userState.user?.pubkey === author
if (!(isWoTNpub || isOwnProfile)) return null
}
return (
<Option
key={`wotFilterOption-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
wot: item
}))
}
>
{item}
</Option>
)
})}
</Dropdown>
{/* nsfw filter options */}
<Dropdown label={filterOptions.nsfw}>
<NsfwFilterOptions filterKey={filterKey} />
</Dropdown>
{/* repost filter options */}
<Dropdown label={filterOptions.repost}>
{Object.values(RepostFilter).map((item, index) => (
<Option
key={`repostFilterItem-${index}`}
onClick={() =>
setFilterOptions((prev) => ({
...prev,
repost: item
}))
}
>
{item}
</Option>
))}
</Dropdown>
{/* source filter options */}
<Dropdown
label={
filterOptions.source === window.location.host
? `Show From: ${filterOptions.source}`
: 'Show All'
}
>
<Option
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: window.location.host
}))
}
>
Show From: {window.location.host}
</Option>
<Option
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: 'Show All'
}))
}
>
Show All
</Option>
</Dropdown>
{children}
</Filter>
)
}
)

View File

@ -0,0 +1,64 @@
import { FilterOptions, NSFWFilter } from 'types'
import { Option } from './Option'
import { NsfwAlertPopup } from 'components/NsfwAlertPopup'
import { useState } from 'react'
import { useLocalStorage } from 'hooks'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
interface NsfwFilterOptionsProps {
filterKey: string
}
export const NsfwFilterOptions = ({ filterKey }: NsfwFilterOptionsProps) => {
const [, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
const [showNsfwPopup, setShowNsfwPopup] = useState<boolean>(false)
const [selectedNsfwOption, setSelectedNsfwOption] = useState<
NSFWFilter | undefined
>()
const [confirmNsfw] = useLocalStorage<boolean>('confirm-nsfw', false)
const handleConfirm = (confirm: boolean) => {
if (confirm && selectedNsfwOption) {
setFilterOptions((prev) => ({
...prev,
nsfw: selectedNsfwOption
}))
}
}
return (
<>
{Object.values(NSFWFilter).map((item, index) => (
<Option
key={`nsfwFilterItem-${index}`}
onClick={() => {
// Trigger NSFW popup
if (
(item === NSFWFilter.Only_NSFW ||
item === NSFWFilter.Show_NSFW) &&
!confirmNsfw
) {
setSelectedNsfwOption(item)
setShowNsfwPopup(true)
} else {
setFilterOptions((prev) => ({
...prev,
nsfw: item
}))
}
}}
>
{item}
</Option>
))}
{showNsfwPopup && (
<NsfwAlertPopup
handleConfirm={handleConfirm}
handleClose={() => setShowNsfwPopup(false)}
/>
)}
</>
)
}

View File

@ -0,0 +1,16 @@
import { PropsWithChildren } from 'react'
interface OptionProps {
onClick: React.MouseEventHandler<HTMLDivElement>
}
export const Option = ({
onClick,
children
}: PropsWithChildren<OptionProps>) => {
return (
<div className='dropdown-item dropdownMainMenuItem' onClick={onClick}>
{children}
</div>
)
}

View File

@ -0,0 +1,9 @@
import { PropsWithChildren } from 'react'
export const Filter = ({ children }: PropsWithChildren) => {
return (
<div className='IBMSecMain'>
<div className='FiltersMain'>{children}</div>
</div>
)
}

View File

@ -1,21 +1,28 @@
import { Link } from 'react-router-dom'
import { getGamePageRoute } from 'routes'
import '../styles/cardGames.css'
import { handleGameImageError } from '../utils'
type GameCardProps = {
backgroundLink: string
title: string
imageUrl: string
}
export const GameCard = ({ backgroundLink }: GameCardProps) => {
export const GameCard = ({ title, imageUrl }: GameCardProps) => {
const route = getGamePageRoute(title)
return (
<a className='cardGameMainWrapperLink' href='search.html'>
<div
className='cardGameMain'
style={{
background: `url("${backgroundLink}") center / cover no-repeat`
}}
></div>
<div className='cardGameMainTitle'>
<p>This is a game title, the best game title</p>
<Link className='cardGameMainWrapperLink' to={route}>
<div className='cardGameMainWrapper'>
<img
src={imageUrl}
onError={handleGameImageError}
className='cardGameMain'
/>
</div>
</a>
<div className='cardGameMainTitle'>
<p>{title}</p>
</div>
</Link>
)
}

View File

@ -1,140 +0,0 @@
import React from 'react'
import ReactQuill from 'react-quill'
import 'react-quill/dist/quill.snow.css'
import '../styles/customQuillStyles.css'
import '../styles/styles.css'
const editorFormats = [
'header',
'font',
'size',
'bold',
'italic',
'underline',
'strike',
'blockquote',
'list',
'bullet',
'indent',
'link'
]
const editorModules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['bold', 'italic', 'underline', 'strike', 'blockquote'],
[
{ list: 'ordered' },
{ list: 'bullet' },
{ indent: '-1' },
{ indent: '+1' }
],
['link']
]
}
interface InputFieldProps {
label: string
description?: string
type?: 'text' | 'textarea' | 'richtext'
placeholder: string
name: string
inputMode?: 'url'
value: string
error?: string
onChange: (name: string, value: string) => void
}
export const InputField = React.memo(
({
label,
description,
type = 'text',
placeholder,
name,
inputMode,
value,
error,
onChange
}: InputFieldProps) => {
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
onChange(name, e.target.value)
}
return (
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>{label}</label>
{description && <p className='labelDescriptionMain'>{description}</p>}
{type === 'textarea' ? (
<textarea
className='inputMain'
placeholder={placeholder}
name={name}
value={value}
onChange={handleChange}
></textarea>
) : type === 'richtext' ? (
<ReactQuill
className='inputMain'
formats={editorFormats}
modules={editorModules}
placeholder={placeholder}
value={value}
onChange={(content) => onChange(name, content)}
/>
) : (
<input
type={type}
className='inputMain'
placeholder={placeholder}
name={name}
inputMode={inputMode}
value={value}
onChange={handleChange}
/>
)}
{error && <InputError message={error} />}
</div>
)
}
)
type InputErrorProps = {
message: string
}
export const InputError = ({ message }: InputErrorProps) => {
if (!message) return null
return (
<div className='errorMain'>
<div className='errorMainColor'></div>
<p className='errorMainText'>{message}</p>
</div>
)
}
interface CheckboxFieldProps {
label: string
name: string
isChecked: boolean
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void
}
export const CheckboxField = React.memo(
({ label, name, isChecked, handleChange }: CheckboxFieldProps) => (
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
<label className='form-label labelMain'>{label}</label>
<input
type='checkbox'
className='CheckboxMain'
name={name}
checked={isChecked}
onChange={handleChange}
/>
</div>
)
)

View File

@ -0,0 +1,14 @@
type InputErrorProps = {
message: string
}
export const InputError = ({ message }: InputErrorProps) => {
if (!message) return null
return (
<div className='errorMain'>
<div className='errorMainColor'></div>
<p className='errorMainText'>{message}</p>
</div>
)
}

View File

@ -0,0 +1,11 @@
.spinner {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
-webkit-backdrop-filter: blur(1px);
backdrop-filter: blur(1px);
pointer-events: none;
}

View File

@ -0,0 +1,126 @@
import { useDropzone } from 'react-dropzone'
import React, { useCallback, useMemo, useState } from 'react'
import {
MediaOption,
MEDIA_OPTIONS,
ImageController,
MEDIA_DROPZONE_OPTIONS
} from '../../controllers'
import { errorFeedback } from '../../types'
import { MediaInputPopover } from './MediaInputPopover'
import { Spinner } from 'components/Spinner'
import styles from './ImageUpload.module.scss'
export interface ImageUploadProps {
multiple?: boolean | undefined
onChange: (values: string[]) => void
}
export const ImageUpload = React.memo(
({ multiple = false, onChange }: ImageUploadProps) => {
const [isLoading, setIsLoading] = useState(false)
const [mediaOption, setMediaOption] = useState<MediaOption>(
MEDIA_OPTIONS[0]
)
const handleOptionChange = useCallback(
(mo: MediaOption) => () => {
setMediaOption(mo)
},
[]
)
const handleUpload = useCallback(
async (acceptedFiles: File[]) => {
if (acceptedFiles.length) {
try {
setIsLoading(true)
const imageController = new ImageController(mediaOption)
const urls: string[] = []
for (let i = 0; i < acceptedFiles.length; i++) {
const file = acceptedFiles[i]
urls.push(await imageController.post(file))
}
onChange(urls)
} catch (error) {
errorFeedback(error)
} finally {
setIsLoading(false)
}
}
},
[mediaOption, onChange]
)
const {
getRootProps,
getInputProps,
isDragActive,
acceptedFiles,
isFileDialogActive,
isDragAccept,
isDragReject,
fileRejections
} = useDropzone({
...MEDIA_DROPZONE_OPTIONS,
onDrop: handleUpload,
multiple: multiple
})
const dropzoneLabel = useMemo(
() =>
isFileDialogActive
? 'Select files in dialog'
: isDragActive
? isDragAccept
? 'Drop the files here...'
: isDragReject
? 'Drop the files here (one more more unsupported types)...'
: 'Drop the files here...'
: 'Click or drag files here',
[isDragAccept, isDragActive, isDragReject, isFileDialogActive]
)
return (
<div aria-label='upload featuredImageUrl' className='uploadBoxMain'>
<MediaInputPopover
acceptedFiles={acceptedFiles}
fileRejections={fileRejections}
/>
<div className='uploadBoxMainInside' {...getRootProps()} tabIndex={-1}>
<input id='featuredImageUrl-upload' {...getInputProps()} />
<span>{dropzoneLabel}</span>
<div
className='FiltersMainElement'
onClick={(e) => e.stopPropagation()}
>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
Image Host: {mediaOption.name}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{MEDIA_OPTIONS.map((mo) => {
return (
<div
key={mo.host}
onClick={handleOptionChange(mo)}
className='dropdown-item dropdownMainMenuItem'
>
{mo.name}
</div>
)
})}
</div>
</div>
</div>
{isLoading && (
<div className={styles.spinner}>
<Spinner />
</div>
)}
</div>
</div>
)
}
)

View File

@ -0,0 +1,14 @@
.accordion-button::after {
position: absolute;
right: 0.75rem;
color: rgba(255, 255, 255, 0.5) !important;
top: unset !important;
bottom: unset !important;
}
.accordion-body > * {
margin-top: 10px;
}
.accordion-item + .accordion-item {
margin-top: 10px;
}

View File

@ -0,0 +1,64 @@
import { FileError } from 'react-dropzone'
import styles from './MediaInputError.module.scss'
type MediaInputErrorProps = {
rootId: string
index: number
message: string
errors?: readonly FileError[] | undefined
}
export const MediaInputError = ({
rootId,
index,
message,
errors
}: MediaInputErrorProps) => {
if (!message) return null
return (
<div className={['accordion-item', styles['accordion-item']].join(' ')}>
<h2 className='accordion-header' role='tab'>
<button
className={[
'accordion-button collapsed',
styles['accordion-button']
].join(' ')}
type='button'
data-bs-toggle='collapse'
data-bs-target={`#${rootId} .item-${index}`}
aria-expanded='false'
aria-controls={`${rootId} .item-${index}`}
>
<div className='errorMain'>
<div className='errorMainColor'></div>
<p className='errorMainText'>{message}</p>
</div>
</button>
</h2>
{errors && (
<div
className={`accordion-collapse collapse item-${index}`}
role='tabpanel'
data-bs-parent={`#${rootId}`}
>
<div
className={['accordion-body', styles['accordion-body']].join(' ')}
>
{errors.map((e) => {
return typeof e === 'string' ? (
<div className='errorMain' key={e}>
{e}
</div>
) : (
<div className='errorMain' key={e.code}>
{e.message}
</div>
)
})}
</div>
</div>
)}
</div>
)
}

View File

@ -0,0 +1,45 @@
.popover {
border-radius: 15px;
box-shadow: 0 0 16px 0px rgb(0 0 0 / 15%);
background: #232323;
z-index: 2;
}
.content {
max-height: 500px;
overflow-y: auto;
padding: 25px;
> *:not(:first-child) {
margin-top: 10px;
}
}
.trigger {
position: absolute;
top: 25px;
right: 25px;
color: rgba(255, 255, 255, 0.5);
}
.mediaInputError {
--bs-accordion-color: unset;
--bs-accordion-bg: unset;
--bs-accordion-transition: unset;
--bs-accordion-border-color: unset;
--bs-accordion-border-width: unset;
--bs-accordion-border-radius: unset;
--bs-accordion-inner-border-radius: unset;
--bs-accordion-btn-padding-x: unset;
--bs-accordion-btn-padding-y: unset;
--bs-accordion-btn-color: unset;
--bs-accordion-btn-bg: unset;
--bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='gray'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
--bs-accordion-btn-icon-width: 1.25rem;
--bs-accordion-btn-icon-transform: rotate(-180deg);
--bs-accordion-btn-icon-transition: transform 0.2s ease-in-out;
--bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='gray'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e");
--bs-accordion-btn-focus-border-color: unset;
--bs-accordion-btn-focus-box-shadow: unset;
--bs-accordion-body-padding-x: unset;
--bs-accordion-body-padding-y: unset;
--bs-accordion-active-color: unset;
--bs-accordion-active-bg: unset;
}

View File

@ -0,0 +1,108 @@
import * as Popover from '@radix-ui/react-popover'
import { v4 as uuidv4 } from 'uuid'
import { useMemo } from 'react'
import { FileRejection, FileWithPath } from 'react-dropzone'
import { MediaInputError } from './MediaInputError'
import { InputSuccess } from './Success'
import styles from './MediaInputPopover.module.scss'
interface MediaInputPopoverProps {
acceptedFiles: readonly FileWithPath[]
fileRejections: readonly FileRejection[]
}
export const MediaInputPopover = ({
acceptedFiles,
fileRejections
}: MediaInputPopoverProps) => {
const uuid = useMemo(() => uuidv4(), [])
const acceptedFileItems = useMemo(
() =>
acceptedFiles.map((file) => (
<InputSuccess
key={file.path}
message={`${file.path} - ${file.size} bytes`}
/>
)),
[acceptedFiles]
)
const fileRejectionItems = useMemo(() => {
const id = `errors-${uuid}`
return (
<div
className={`accordion accordion-flush ${styles.mediaInputError}`}
role='tablist'
id={id}
>
{fileRejections.map(({ file, errors }, index) => (
<MediaInputError
rootId={id}
index={index}
key={file.path}
message={`${file.path} - ${file.size} bytes`}
errors={errors}
/>
))}
</div>
)
}, [fileRejections, uuid])
if (acceptedFiles.length === 0 && fileRejections.length === 0) return null
return (
<Popover.Root>
<Popover.Trigger asChild>
<div className={styles.trigger}>
{acceptedFiles.length > 0 ? (
<svg
width='1.5em'
height='1.5em'
fill='currentColor'
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 576 512'
>
<path d='M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 38.6C310.1 219.5 256 287.4 256 368c0 59.1 29.1 111.3 73.7 143.3c-3.2 .5-6.4 .7-9.7 .7L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zM288 368a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm211.3-43.3c-6.2-6.2-16.4-6.2-22.6 0L416 385.4l-28.7-28.7c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6l40 40c6.2 6.2 16.4 6.2 22.6 0l72-72c6.2-6.2 6.2-16.4 0-22.6z' />
</svg>
) : (
<svg
width='1.5em'
height='1.5em'
fill='tomato'
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 576 512'
>
<path d='M0 64C0 28.7 28.7 0 64 0L224 0l0 128c0 17.7 14.3 32 32 32l128 0 0 38.6C310.1 219.5 256 287.4 256 368c0 59.1 29.1 111.3 73.7 143.3c-3.2 .5-6.4 .7-9.7 .7L64 512c-35.3 0-64-28.7-64-64L0 64zm384 64l-128 0L256 0 384 128zm48 96a144 144 0 1 1 0 288 144 144 0 1 1 0-288zm0 240a24 24 0 1 0 0-48 24 24 0 1 0 0 48zm0-192c-8.8 0-16 7.2-16 16l0 80c0 8.8 7.2 16 16 16s16-7.2 16-16l0-80c0-8.8-7.2-16-16-16z' />
</svg>
)}
</div>
</Popover.Trigger>
<Popover.Portal>
<Popover.Content className={styles.popover} sideOffset={5}>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>Selected files</h3>
</div>
<Popover.Close asChild aria-label='Close'>
<div className='popUpMainCardTopClose'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
style={{ zIndex: 1 }}
>
<path d='M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z'></path>
</svg>
</div>
</Popover.Close>
</div>
<div className={styles.content}>
{acceptedFileItems}
{fileRejectionItems}
</div>
</Popover.Content>
</Popover.Portal>
</Popover.Root>
)
}

View File

@ -0,0 +1,14 @@
type InputSuccessProps = {
message: string
}
export const InputSuccess = ({ message }: InputSuccessProps) => {
if (!message) return null
return (
<div className='successMain'>
<div className='successMainColor'></div>
<p className='successMainText'>{message}</p>
</div>
)
}

View File

@ -0,0 +1,206 @@
import React, { useCallback } from 'react'
import { InputError } from './Error'
import { ImageUpload } from './ImageUpload'
import '../../styles/styles.css'
interface InputFieldProps {
label: string | React.ReactElement
description?: string
type?: 'text' | 'textarea'
placeholder: string
name: string
inputMode?: 'url'
value: string
error?: string
onChange: (name: string, value: string) => void
}
export const InputField = React.memo(
({
label,
description,
type = 'text',
placeholder,
name,
inputMode,
value,
error,
onChange
}: InputFieldProps) => {
const handleChange = (
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>
) => {
onChange(name, e.target.value)
}
return (
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>{label}</label>
{description && <p className='labelDescriptionMain'>{description}</p>}
{type === 'textarea' ? (
<textarea
className='inputMain'
placeholder={placeholder}
name={name}
value={value}
onChange={handleChange}
></textarea>
) : (
<input
type={type}
className='inputMain'
placeholder={placeholder}
name={name}
inputMode={inputMode}
value={value}
onChange={handleChange}
/>
)}
{error && <InputError message={error} />}
</div>
)
}
)
interface CheckboxFieldProps {
label: string
name: string
isChecked: boolean
handleChange: (e: React.ChangeEvent<HTMLInputElement>) => void
type?: 'default' | 'stylized'
}
export const CheckboxField = React.memo(
({
label,
name,
isChecked,
handleChange,
type = 'default'
}: CheckboxFieldProps) => (
<div
className={`inputLabelWrapperMain inputLabelWrapperMainAlt${
type === 'stylized' ? ` inputLabelWrapperMainAltStylized` : ''
}`}
>
<label htmlFor={name} className='form-label labelMain'>
{label}
</label>
<input
id={name}
type='checkbox'
className='CheckboxMain'
name={name}
checked={isChecked}
onChange={handleChange}
/>
</div>
)
)
interface InputFieldUncontrolledProps extends React.ComponentProps<'input'> {
label: string | React.ReactElement
description?: string
error?: string
}
/**
* Uncontrolled input component with design classes, label, description and error support
*
* Extends {@link React.ComponentProps<'input'> React.ComponentProps<'input'>}
* @param label
* @param description
* @param error
*
* @see {@link React.ComponentProps<'input'>}
*/
export const InputFieldUncontrolled = ({
label,
description,
error,
...rest
}: InputFieldUncontrolledProps) => (
<div className='inputLabelWrapperMain'>
<label htmlFor={rest.id} className='form-label labelMain'>
{label}
</label>
{description && <p className='labelDescriptionMain'>{description}</p>}
<input className='inputMain' {...rest} />
{error && <InputError message={error} />}
</div>
)
interface CheckboxFieldUncontrolledProps extends React.ComponentProps<'input'> {
label: string
}
export const CheckboxFieldUncontrolled = ({
label,
...rest
}: CheckboxFieldUncontrolledProps) => (
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
<label htmlFor={rest.id} className='form-label labelMain'>
{label}
</label>
<input type='checkbox' className='CheckboxMain' {...rest} />
</div>
)
interface InputFieldWithImageUploadProps {
label: string | React.ReactElement
description?: string
placeholder: string
name: string
inputMode?: 'url'
value: string
error?: string
onInputChange: (name: string, value: string) => void
}
export const InputFieldWithImageUpload = React.memo(
({
label,
description,
placeholder,
name,
inputMode,
value,
error,
onInputChange
}: InputFieldWithImageUploadProps) => {
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
onInputChange(name, e.currentTarget.value)
},
[name, onInputChange]
)
const handleFileChange = useCallback(
(values: string[]) => {
onInputChange(name, values[0])
},
[name, onInputChange]
)
return (
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>{label}</label>
{typeof description !== 'undefined' && (
<p className='labelDescriptionMain'>{description}</p>
)}
<ImageUpload onChange={handleFileChange} />
<input
type='text'
className='inputMain'
placeholder={placeholder}
name={name}
inputMode={inputMode}
value={value}
onChange={handleChange}
/>
{error && <InputError message={error} />}
</div>
)
}
)

View File

@ -0,0 +1,42 @@
import { Addressable } from 'types'
import { abbreviateNumber } from 'utils'
import { Zap } from './Zap'
import { Reactions } from './Reactions'
type InteractionsProps = {
addressable: Addressable
commentCount: number
}
export const Interactions = ({
addressable,
commentCount
}: InteractionsProps) => {
return (
<div className='IBMSMSplitMainBigSideSec'>
<div className='IBMSMSMBSS_Details'>
<a style={{ textDecoration: 'unset', color: 'unset' }}>
<div className='IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CComments'>
<div className='IBMSMSMBSS_Details_CardVisual'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSS_Details_CardVisualIcon'
>
<path d='M256 31.1c-141.4 0-255.1 93.12-255.1 208c0 49.62 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734c1.249 3 4.021 4.766 7.271 4.766c66.25 0 115.1-31.76 140.6-51.39c32.63 12.25 69.02 19.39 107.4 19.39c141.4 0 255.1-93.13 255.1-207.1S397.4 31.1 256 31.1zM127.1 271.1c-17.75 0-32-14.25-32-31.1s14.25-32 32-32s32 14.25 32 32S145.7 271.1 127.1 271.1zM256 271.1c-17.75 0-31.1-14.25-31.1-31.1s14.25-32 31.1-32s31.1 14.25 31.1 32S273.8 271.1 256 271.1zM383.1 271.1c-17.75 0-32-14.25-32-31.1s14.25-32 32-32s32 14.25 32 32S401.7 271.1 383.1 271.1z'></path>
</svg>
</div>
<p className='IBMSMSMBSS_Details_CardText'>
{abbreviateNumber(commentCount)}
</p>
</div>
</a>
<Zap addressable={addressable} />
<Reactions addressable={addressable} />
</div>
</div>
)
}

View File

@ -0,0 +1,86 @@
import { formatDate } from 'date-fns'
type PublishDetailsProps = {
published_at: number
edited_at: number
site: string
}
export const PublishDetails = ({
published_at,
edited_at,
site
}: PublishDetailsProps) => {
return (
<div className='IBMSMSplitMainBigSideSec'>
<div className='IBMSMSMBSSPost_PostDetails'>
<div
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement'
title='Publish date'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
data-bs-toggle='tooltip'
data-bss-tooltip
aria-label='Publish date'
>
<path d='M480 32H128C110.3 32 96 46.33 96 64v336C96 408.8 88.84 416 80 416S64 408.8 64 400V96H32C14.33 96 0 110.3 0 128v288c0 35.35 28.65 64 64 64h384c35.35 0 64-28.65 64-64V64C512 46.33 497.7 32 480 32zM272 416h-96C167.2 416 160 408.8 160 400C160 391.2 167.2 384 176 384h96c8.836 0 16 7.162 16 16C288 408.8 280.8 416 272 416zM272 320h-96C167.2 320 160 312.8 160 304C160 295.2 167.2 288 176 288h96C280.8 288 288 295.2 288 304C288 312.8 280.8 320 272 320zM432 416h-96c-8.836 0-16-7.164-16-16c0-8.838 7.164-16 16-16h96c8.836 0 16 7.162 16 16C448 408.8 440.8 416 432 416zM432 320h-96C327.2 320 320 312.8 320 304C320 295.2 327.2 288 336 288h96C440.8 288 448 295.2 448 304C448 312.8 440.8 320 432 320zM448 208C448 216.8 440.8 224 432 224h-256C167.2 224 160 216.8 160 208v-96C160 103.2 167.2 96 176 96h256C440.8 96 448 103.2 448 112V208z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>
{formatDate(
(published_at !== -1 ? published_at : edited_at) * 1000,
'dd/MM/yyyy hh:mm:ss aa'
)}
</p>
</div>
<div
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement'
title='Last modified'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
>
<path d='M362.7 19.32C387.7-5.678 428.3-5.678 453.3 19.32L492.7 58.75C517.7 83.74 517.7 124.3 492.7 149.3L444.3 197.7L314.3 67.72L362.7 19.32zM421.7 220.3L188.5 453.4C178.1 463.8 165.2 471.5 151.1 475.6L30.77 511C22.35 513.5 13.24 511.2 7.03 504.1C.8198 498.8-1.502 489.7 .976 481.2L36.37 360.9C40.53 346.8 48.16 333.9 58.57 323.5L291.7 90.34L421.7 220.3z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>
{formatDate(edited_at * 1000, 'dd/MM/yyyy hh:mm:ss aa')}
</p>
</div>
<a
data-bs-toggle='tooltip'
data-bs-placement='left'
className='IBMSMSMBSSPost_PDElement IBMSMSMBSSPost_PDElementLink'
href='#'
title='Published on'
target='_blank'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 -64 640 640'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSPost_PDElementIcon'
>
<path d='M172.5 131.1C228.1 75.51 320.5 75.51 376.1 131.1C426.1 181.1 433.5 260.8 392.4 318.3L391.3 319.9C381 334.2 361 337.6 346.7 327.3C332.3 317 328.9 297 339.2 282.7L340.3 281.1C363.2 249 359.6 205.1 331.7 177.2C300.3 145.8 249.2 145.8 217.7 177.2L105.5 289.5C73.99 320.1 73.99 372 105.5 403.5C133.3 431.4 177.3 435 209.3 412.1L210.9 410.1C225.3 400.7 245.3 404 255.5 418.4C265.8 432.8 262.5 452.8 248.1 463.1L246.5 464.2C188.1 505.3 110.2 498.7 60.21 448.8C3.741 392.3 3.741 300.7 60.21 244.3L172.5 131.1zM467.5 380C411 436.5 319.5 436.5 263 380C213 330 206.5 251.2 247.6 193.7L248.7 192.1C258.1 177.8 278.1 174.4 293.3 184.7C307.7 194.1 311.1 214.1 300.8 229.3L299.7 230.9C276.8 262.1 280.4 306.9 308.3 334.8C339.7 366.2 390.8 366.2 422.3 334.8L534.5 222.5C566 191 566 139.1 534.5 108.5C506.7 80.63 462.7 76.99 430.7 99.9L429.1 101C414.7 111.3 394.7 107.1 384.5 93.58C374.2 79.2 377.5 59.21 391.9 48.94L393.5 47.82C451 6.731 529.8 13.25 579.8 63.24C636.3 119.7 636.3 211.3 579.8 267.7L467.5 380z' />
</svg>
<p className='IBMSMSMBSSPost_PDElementText'>{site}</p>
</a>
</div>
</div>
)
}

View File

@ -0,0 +1,77 @@
import { Dots } from 'components/Spinner'
import { useReactions } from 'hooks'
import { Addressable } from 'types'
type ReactionsProps = {
addressable: Addressable
}
export const Reactions = ({ addressable }: ReactionsProps) => {
const {
isDataLoaded,
likesCount,
disLikesCount,
handleReaction,
hasReactedPositively,
hasReactedNegatively
} = useReactions({
pubkey: addressable.author,
eTag: addressable.id,
aTag: addressable.aTag
})
return (
<>
<div
className={`IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CReactUp ${
hasReactedPositively ? 'IBMSMSMBSS_D_CRUActive' : ''
}`}
onClick={isDataLoaded ? () => handleReaction(true) : undefined}
>
<div className='IBMSMSMBSS_Details_CardVisual'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSS_Details_CardVisualIcon'
>
<path d='M0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84.02L256 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 .0003 232.4 .0003 190.9L0 190.9z'></path>
</svg>
</div>
<p className='IBMSMSMBSS_Details_CardText'>
{isDataLoaded ? likesCount : <Dots />}
</p>
<div className='IBMSMSMBSSCL_CAElementLoadWrapper'>
<div className='IBMSMSMBSSCL_CAElementLoad'></div>
</div>
</div>
<div
className={`IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CReactDown ${
hasReactedNegatively ? 'IBMSMSMBSS_D_CRDActive' : ''
}`}
onClick={isDataLoaded ? () => handleReaction() : undefined}
>
<div className='IBMSMSMBSS_Details_CardVisual'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSS_Details_CardVisualIcon'
>
<path d='M512 440.1C512 479.9 479.7 512 439.1 512H71.92C32.17 512 0 479.8 0 440c0-35.88 26.19-65.35 60.56-70.85C43.31 356 32 335.4 32 312C32 272.2 64.25 240 104 240h13.99C104.5 228.2 96 211.2 96 192c0-35.38 28.56-64 63.94-64h16C220.1 128 256 92.12 256 48c0-17.38-5.784-33.35-15.16-46.47C245.8 .7754 250.9 0 256 0c53 0 96 43 96 96c0 11.25-2.288 22-5.913 32h5.879C387.3 128 416 156.6 416 192c0 19.25-8.59 36.25-22.09 48H408C447.8 240 480 272.2 480 312c0 23.38-11.38 44.01-28.63 57.14C485.7 374.6 512 404.3 512 440.1z'></path>
</svg>
</div>
<p className='IBMSMSMBSS_Details_CardText'>
{isDataLoaded ? disLikesCount : <Dots />}
</p>
<div className='IBMSMSMBSSCL_CAElementLoadWrapper'>
<div className='IBMSMSMBSSCL_CAElementLoad'></div>
</div>
</div>
</>
)
}

View File

@ -0,0 +1,100 @@
import { Dots } from 'components/Spinner'
import { ZapSplit } from 'components/Zap'
import {
useAppSelector,
useBodyScrollDisable,
useDidMount,
useNDKContext
} from 'hooks'
import { useState } from 'react'
import { toast } from 'react-toastify'
import { Addressable } from 'types'
import { abbreviateNumber, log, LogType } from 'utils'
type ZapProps = {
addressable: Addressable
}
export const Zap = ({ addressable }: ZapProps) => {
const [isOpen, setIsOpen] = useState(false)
const [isLoading, setIsLoading] = useState(true)
const [isAvailable, setIsAvailable] = useState(false)
const [totalZappedAmount, setTotalZappedAmount] = useState(0)
const [hasZapped, setHasZapped] = useState(false)
const userState = useAppSelector((state) => state.user)
const { getTotalZapAmount, findMetadata } = useNDKContext()
useBodyScrollDisable(isOpen)
useDidMount(() => {
findMetadata(addressable.author)
.then((res) => {
setIsAvailable(typeof res?.lud16 !== 'undefined' && res.lud16 !== '')
})
.catch((err) => {
log(true, LogType.Error, err.message || err)
})
getTotalZapAmount(
addressable.author,
addressable.id,
addressable.aTag,
userState.user?.pubkey as string
)
.then((res) => {
setTotalZappedAmount(res.accumulatedZapAmount)
setHasZapped(res.hasZapped)
})
.catch((err) => {
toast.error(err.message || err)
})
.finally(() => {
setIsLoading(false)
})
})
// Hide button if the author hasn't set lud16
if (!isAvailable) return null
return (
<>
<div
id='reactBolt'
className={`IBMSMSMBSS_Details_Card IBMSMSMBSS_D_CBolt ${
hasZapped ? 'IBMSMSMBSS_D_CBActive' : ''
}`}
onClick={isLoading ? undefined : () => setIsOpen(true)}
>
<div className='IBMSMSMBSS_Details_CardVisual'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-64 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSS_Details_CardVisualIcon'
>
<path d='M240.5 224H352C365.3 224 377.3 232.3 381.1 244.7C386.6 257.2 383.1 271.3 373.1 280.1L117.1 504.1C105.8 513.9 89.27 514.7 77.19 505.9C65.1 497.1 60.7 481.1 66.59 467.4L143.5 288H31.1C18.67 288 6.733 279.7 2.044 267.3C-2.645 254.8 .8944 240.7 10.93 231.9L266.9 7.918C278.2-1.92 294.7-2.669 306.8 6.114C318.9 14.9 323.3 30.87 317.4 44.61L240.5 224z'></path>
</svg>
</div>
<p className='IBMSMSMBSS_Details_CardText'>
{isLoading ? <Dots /> : abbreviateNumber(totalZappedAmount)}
</p>
<div className='IBMSMSMBSSCL_CAElementLoadWrapper'>
<div className='IBMSMSMBSSCL_CAElementLoad'></div>
</div>
</div>
{isOpen && (
<ZapSplit
pubkey={addressable.author}
eventId={addressable.id}
aTag={addressable.aTag}
setTotalZapAmount={setTotalZappedAmount}
setHasZapped={setHasZapped}
handleClose={() => setIsOpen(false)}
/>
)}
</>
)
}

View File

@ -1,12 +1,12 @@
import { PropsWithChildren, useEffect, useMemo, useState } from 'react'
import { useNavigation } from 'react-router-dom'
import styles from '../../styles/loadingSpinner.module.scss'
interface Props {
desc: string
}
export const LoadingSpinner = (props: Props) => {
const { desc } = props
export const LoadingSpinner = ({ desc }: Props) => {
return (
<div className={styles.loadingSpinnerOverlay}>
<div className={styles.loadingSpinnerContainer}>
@ -16,3 +16,62 @@ export const LoadingSpinner = (props: Props) => {
</div>
)
}
export const RouterLoadingSpinner = () => {
const navigation = useNavigation()
if (navigation.state === 'idle') return null
const desc =
navigation.state.charAt(0).toUpperCase() + navigation.state.slice(1)
return <LoadingSpinner desc={`${desc}...`} />
}
interface TimerLoadingSpinner {
timeoutMs?: number
countdownMs?: number
}
export const TimerLoadingSpinner = ({
timeoutMs = 10000,
countdownMs = 30000,
children
}: PropsWithChildren<TimerLoadingSpinner>) => {
const [show, setShow] = useState(false)
const [timer, setTimer] = useState(
Math.floor((countdownMs - timeoutMs) / 1000)
)
const startTime = useMemo(() => Date.now(), [])
useEffect(() => {
let interval: number
const timeout = window.setTimeout(() => {
setShow(true)
interval = window.setInterval(() => {
const time = Date.now() - startTime
const diff = Math.max(0, countdownMs - time)
setTimer(Math.floor(diff / 1000))
}, 1000)
}, timeoutMs)
return () => {
clearTimeout(timeout)
clearInterval(interval)
}
}, [countdownMs, startTime, timeoutMs])
return (
<div className={styles.loadingSpinnerOverlay}>
<div className={styles.loadingSpinnerContainer}>
<div className={styles.loadingSpinner}></div>
{children}
{show && (
<>
<div>You can try again in {timer}s...</div>
</>
)}
</div>
</div>
)
}

View File

@ -0,0 +1,10 @@
.formAction {
display: flex;
width: 100%;
justify-content: flex-end;
gap: var(--spacing-2);
}
.wrapper {
border-radius: 0;
}

View File

@ -0,0 +1,148 @@
import {
BlockTypeSelect,
BoldItalicUnderlineToggles,
codeBlockPlugin,
CodeToggle,
CreateLink,
diffSourcePlugin,
DiffSourceToggleWrapper,
directivesPlugin,
headingsPlugin,
imagePlugin,
InsertCodeBlock,
InsertImage,
InsertTable,
InsertThematicBreak,
linkDialogPlugin,
linkPlugin,
listsPlugin,
ListsToggle,
markdownShortcutPlugin,
MDXEditor,
MDXEditorMethods,
MDXEditorProps,
quotePlugin,
Separator,
StrikeThroughSupSubToggles,
tablePlugin,
thematicBreakPlugin,
toolbarPlugin,
UndoRedo
} from '@mdxeditor/editor'
import { PlainTextCodeEditorDescriptor } from './PlainTextCodeEditorDescriptor'
import { YoutubeDirectiveDescriptor } from './YoutubeDirectiveDescriptor'
import { YouTubeButton } from './YoutubeButton'
import '@mdxeditor/editor/style.css'
import '../../styles/mdxEditor.scss'
import React, {
forwardRef,
useCallback,
useImperativeHandle,
useMemo,
useRef
} from 'react'
import { ImageDialog } from './ImageDialog'
import { LinkDialog } from './LinkDialog'
export interface EditorRef {
setMarkdown: (md: string) => void
}
interface EditorProps extends MDXEditorProps {}
/**
* The editor component is small wrapper (`forwardRef`) around {@link MDXEditor MDXEditor} that sets up the toolbars and plugins, and requires `markdown` and `onChange`.
* To reset editor markdown it's required to pass the {@link EditorRef EditorRef}.
*
* Extends {@link MDXEditorProps MDXEditorProps}
*
* **Important**: the markdown is not a state, but an _initialState_ and is not "controlled".
* All updates are handled with onChange and will not be reflected on markdown prop.
* This component should never re-render if used correctly.
* @see https://mdxeditor.dev/editor/docs/getting-started#basic-usage
*/
export const Editor = React.memo(
forwardRef<EditorRef, EditorProps>(({ markdown, onChange, ...rest }, ref) => {
const editorRef = useRef<MDXEditorMethods>(null)
const setMarkdown = useCallback((md: string) => {
editorRef.current?.setMarkdown(md)
}, [])
useImperativeHandle(ref, () => ({ setMarkdown }))
const plugins = useMemo(
() => [
toolbarPlugin({
toolbarContents: () => (
<DiffSourceToggleWrapper
children={
<>
<UndoRedo />
<Separator />
<BoldItalicUnderlineToggles />
<CodeToggle />
<Separator />
<StrikeThroughSupSubToggles />
<Separator />
<ListsToggle />
<Separator />
<BlockTypeSelect />
<Separator />
<CreateLink />
<InsertImage />
<YouTubeButton />
<Separator />
<InsertTable />
<InsertThematicBreak />
<Separator />
<InsertCodeBlock />
</>
}
/>
)
}),
headingsPlugin(),
diffSourcePlugin({
viewMode: 'rich-text',
diffMarkdown: markdown
}),
quotePlugin(),
imagePlugin({
ImageDialog: ImageDialog
}),
tablePlugin(),
linkPlugin(),
linkDialogPlugin({
LinkDialog: LinkDialog
}),
listsPlugin(),
thematicBreakPlugin(),
directivesPlugin({
directiveDescriptors: [YoutubeDirectiveDescriptor]
}),
markdownShortcutPlugin(),
// HACK: due to a bug with shortcut interaction shortcut for code block is disabled
// Editor freezes if you type in ```word and put a space in between ``` word
codeBlockPlugin({
defaultCodeBlockLanguage: '',
codeBlockEditorDescriptors: [PlainTextCodeEditorDescriptor]
})
],
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
)
return (
<MDXEditor
ref={editorRef}
contentEditableClassName='editor'
className='dark-theme dark-editor'
markdown={markdown}
plugins={plugins}
onChange={onChange}
{...rest}
/>
)
}),
() => true
)

View File

@ -0,0 +1,166 @@
import React, { useCallback, useEffect, useState } from 'react'
import { useForm } from 'react-hook-form'
import { useCellValues, usePublisher } from '@mdxeditor/gurx'
import {
closeImageDialog$,
editorRootElementRef$,
imageDialogState$,
imageUploadHandler$,
saveImage$
} from '@mdxeditor/editor'
import styles from './Dialog.module.scss'
import { createPortal } from 'react-dom'
interface ImageFormFields {
src: string
title: string
altText: string
file: FileList
}
export const ImageDialog: React.FC = () => {
const [state, editorRootElementRef, imageUploadHandler] = useCellValues(
imageDialogState$,
editorRootElementRef$,
imageUploadHandler$
)
const saveImage = usePublisher(saveImage$)
const closeImageDialog = usePublisher(closeImageDialog$)
const { register, handleSubmit, setValue, reset } = useForm<ImageFormFields>({
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
values: state.type === 'editing' ? (state.initialValues as any) : {}
})
const [open, setOpen] = useState(state.type !== 'inactive')
useEffect(() => {
setOpen(state.type !== 'inactive')
}, [state.type])
useEffect(() => {
if (!open) {
closeImageDialog()
reset({ src: '', title: '', altText: '' })
}
}, [closeImageDialog, open, reset])
const handleClose = useCallback(() => {
setOpen(false)
}, [])
if (!open) return null
if (!editorRootElementRef?.current) return null
return createPortal(
<div className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard popUpMainCardQR'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>Add an image</h3>
</div>
<div className='popUpMainCardTopClose' onClick={handleClose}>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
style={{ zIndex: 1 }}
>
<path d='M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z'></path>
</svg>
</div>
</div>
<div className='pUMCB_Zaps'>
<form
className='pUMCB_ZapsInside'
onSubmit={(e) => {
void handleSubmit(saveImage)(e)
reset({ src: '', title: '', altText: '' })
e.preventDefault()
e.stopPropagation()
}}
>
{imageUploadHandler === null ? (
<input type='hidden' accept='image/*' {...register('file')} />
) : (
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain' htmlFor='file'>
Upload an image from your device:
</label>
<input type='file' accept='image/*' {...register('file')} />
</div>
)}
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain' htmlFor='src'>
{imageUploadHandler !== null
? 'Or add an image from an URL:'
: 'Add an image from an URL:'}
</label>
<input
defaultValue={
state.type === 'editing'
? state.initialValues.src ?? ''
: ''
}
className='inputMain'
size={40}
autoFocus
{...register('src')}
onChange={(e) => setValue('src', e.currentTarget.value)}
placeholder={'Paste an image src'}
/>
</div>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain' htmlFor='alt'>
Alt:
</label>
<input
type='text'
{...register('altText')}
className='inputMain'
/>
</div>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain' htmlFor='title'>
Title:
</label>
<input
type='text'
{...register('title')}
className='inputMain'
/>
</div>
<div className={styles.formAction}>
<button
type='submit'
title={'Save'}
aria-label={'Save'}
className='btn btnMain btnMainPopup'
>
Save
</button>
<button
type='reset'
title={'Cancel'}
aria-label={'Cancel'}
className='btn btnMain btnMainPopup'
onClick={handleClose}
>
Cancel
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>,
editorRootElementRef?.current
)
}

View File

@ -0,0 +1,306 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import * as Popover from '@radix-ui/react-popover'
import * as Tooltip from '@radix-ui/react-tooltip'
import React from 'react'
import {
activeEditor$,
editorRootElementRef$,
iconComponentFor$,
cancelLinkEdit$,
linkDialogState$,
onWindowChange$,
removeLink$,
switchFromPreviewToLinkEdit$,
updateLink$,
ClickLinkCallback
} from '@mdxeditor/editor'
import { useForm } from 'react-hook-form'
import { Cell, useCellValues, usePublisher } from '@mdxeditor/gurx'
import styles from './Dialog.module.scss'
interface LinkEditFormProps {
url: string
title: string
onSubmit: (link: { url: string; title: string }) => void
onCancel: () => void
}
interface LinkFormFields {
url: string
title: string
}
export function LinkEditForm({
url,
title,
onSubmit,
onCancel
}: LinkEditFormProps) {
const { register, handleSubmit, setValue } = useForm<LinkFormFields>({
values: {
url,
title
}
})
return (
<div className='pUMCB_Zaps'>
<form
className='pUMCB_ZapsInside'
onSubmit={(e) => {
void handleSubmit(onSubmit)(e)
e.stopPropagation()
e.preventDefault()
}}
onReset={(e) => {
e.stopPropagation()
onCancel()
}}
>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain' htmlFor='file'>
URL:
</label>
<input
defaultValue={url}
className='inputMain'
size={40}
autoFocus
{...register('url')}
onChange={(e) => setValue('url', e.currentTarget.value)}
placeholder={'Paste an URL'}
/>
</div>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain' htmlFor='link-title'>
Title:
</label>
<input
id='link-title'
className='inputMain'
size={40}
{...register('title')}
/>
</div>
<div className={styles.formAction}>
<button
type='submit'
title={'Set URL'}
aria-label={'Set URL'}
className='btn btnMain btnMainPopup'
>
Save
</button>
<button
type='reset'
title={'Cancel change'}
aria-label={'Cancel change'}
className='btn btnMain btnMainPopup'
>
Cancel
</button>
</div>
</form>
</div>
)
}
export const onClickLinkCallback$ = Cell<ClickLinkCallback | null>(null)
/** @internal */
export const LinkDialog = () => {
const [
editorRootElementRef,
activeEditor,
iconComponentFor,
linkDialogState,
onClickLinkCallback
] = useCellValues(
editorRootElementRef$,
activeEditor$,
iconComponentFor$,
linkDialogState$,
onClickLinkCallback$
)
const publishWindowChange = usePublisher(onWindowChange$)
const updateLink = usePublisher(updateLink$)
const cancelLinkEdit = usePublisher(cancelLinkEdit$)
const switchFromPreviewToLinkEdit = usePublisher(switchFromPreviewToLinkEdit$)
const removeLink = usePublisher(removeLink$)
React.useEffect(() => {
const update = () => {
activeEditor?.getEditorState().read(() => {
publishWindowChange(true)
})
}
window.addEventListener('resize', update)
window.addEventListener('scroll', update)
return () => {
window.removeEventListener('resize', update)
window.removeEventListener('scroll', update)
}
}, [activeEditor, publishWindowChange])
const [copyUrlTooltipOpen, setCopyUrlTooltipOpen] = React.useState(false)
const theRect = linkDialogState.rectangle
const urlIsExternal =
linkDialogState.type === 'preview' && linkDialogState.url.startsWith('http')
return (
<Popover.Root open={linkDialogState.type !== 'inactive'}>
<Popover.Anchor
data-visible={linkDialogState.type === 'edit'}
style={{
position: 'fixed',
top: `${theRect?.top ?? 0}px`,
left: `${theRect?.left ?? 0}px`,
width: `${theRect?.width ?? 0}px`,
height: `${theRect?.height ?? 0}px`
}}
/>
<Popover.Portal container={editorRootElementRef?.current}>
<Popover.Content
sideOffset={5}
onOpenAutoFocus={(e) => {
e.preventDefault()
}}
key={linkDialogState.linkNodeKey}
className={[
'popUpMainCard',
...(linkDialogState.type === 'edit' ? [styles.wrapper] : [])
].join(' ')}
>
{linkDialogState.type === 'edit' && (
<LinkEditForm
url={linkDialogState.url}
title={linkDialogState.title}
onSubmit={updateLink}
onCancel={cancelLinkEdit.bind(null)}
/>
)}
{linkDialogState.type === 'preview' && (
<>
<div className='IBMSMSMSSS_Author_Top_AddressWrapper'>
<div className='IBMSMSMSSS_Author_Top_AddressWrapped'>
<p className='IBMSMSMSSS_Author_Top_Address'>
<a
className={styles.linkDialogPreviewAnchor}
href={linkDialogState.url}
{...(urlIsExternal
? { target: '_blank', rel: 'noreferrer' }
: {})}
onClick={(e) => {
if (
onClickLinkCallback !== null &&
typeof onClickLinkCallback === 'function'
) {
e.preventDefault()
onClickLinkCallback(linkDialogState.url)
}
}}
title={
urlIsExternal
? `Open ${linkDialogState.url} in new window`
: linkDialogState.url
}
>
<span>{linkDialogState.url}</span>
{urlIsExternal && iconComponentFor('open_in_new')}
</a>
</p>
</div>
<div className='IBMSMSMSSS_Author_Top_IconWrapper'>
<div
className='IBMSMSMSSS_Author_Top_IconWrapped'
onClick={() => {
switchFromPreviewToLinkEdit()
}}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M362.7 19.32C387.7-5.678 428.3-5.678 453.3 19.32L492.7 58.75C517.7 83.74 517.7 124.3 492.7 149.3L444.3 197.7L314.3 67.72L362.7 19.32zM421.7 220.3L188.5 453.4C178.1 463.8 165.2 471.5 151.1 475.6L30.77 511C22.35 513.5 13.24 511.2 7.03 504.1C.8198 498.8-1.502 489.7 .976 481.2L36.37 360.9C40.53 346.8 48.16 333.9 58.57 323.5L291.7 90.34L421.7 220.3z'></path>
</svg>
</div>
<Tooltip.Provider>
<Tooltip.Root open={copyUrlTooltipOpen}>
<Tooltip.Trigger asChild>
<div
className='IBMSMSMSSS_Author_Top_IconWrapped'
onClick={() => {
void window.navigator.clipboard
.writeText(linkDialogState.url)
.then(() => {
setCopyUrlTooltipOpen(true)
setTimeout(() => {
setCopyUrlTooltipOpen(false)
}, 1000)
})
}}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M384 96L384 0h-112c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48H464c26.51 0 48-21.49 48-48V128h-95.1C398.4 128 384 113.6 384 96zM416 0v96h96L416 0zM192 352V128h-144c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48h192c26.51 0 48-21.49 48-48L288 416h-32C220.7 416 192 387.3 192 352z'></path>
</svg>
</div>
</Tooltip.Trigger>
<Tooltip.Portal container={editorRootElementRef?.current}>
<Tooltip.Content sideOffset={5}>
{'Copied!'}
<Tooltip.Arrow />
</Tooltip.Content>
</Tooltip.Portal>
</Tooltip.Root>
</Tooltip.Provider>
<div
className='IBMSMSMSSS_Author_Top_IconWrapped'
onClick={() => {
removeLink()
}}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 640 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c8.4-19.3 10.6-41.4 4.8-63.3c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zM373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5L373 389.9z' />
</svg>
</div>
</div>
</div>
</>
)}
<Popover.Arrow />
</Popover.Content>
</Popover.Portal>
</Popover.Root>
)
}

View File

@ -0,0 +1,65 @@
import {
CodeBlockEditorDescriptor,
useCodeBlockEditorContext
} from '@mdxeditor/editor'
import { useCallback, useEffect, useRef } from 'react'
export const PlainTextCodeEditorDescriptor: CodeBlockEditorDescriptor = {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
match: (_language, _meta) => true,
priority: 0,
Editor: ({ code, focusEmitter }) => {
const { parentEditor, lexicalNode, setCode } = useCodeBlockEditorContext()
const defaultValue = useRef(code)
const codeRef = useRef<HTMLElement>(null)
const handleInput = useCallback(
(e: React.FormEvent<HTMLElement>) => {
setCode(e.currentTarget.innerHTML)
},
[setCode]
)
useEffect(() => {
const handleFocus = () => {
if (codeRef.current) {
codeRef.current.focus()
}
}
focusEmitter.subscribe(handleFocus)
}, [focusEmitter])
useEffect(() => {
const currentRef = codeRef.current
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Backspace' || event.key === 'Delete') {
if (codeRef.current?.textContent === '') {
parentEditor.update(() => {
lexicalNode.selectNext()
lexicalNode.remove()
})
}
}
}
if (currentRef) {
currentRef.addEventListener('keydown', handleKeyDown)
}
return () => {
if (currentRef) {
currentRef.removeEventListener('keydown', handleKeyDown)
}
}
}, [lexicalNode, parentEditor])
return (
<pre>
<code
ref={codeRef}
contentEditable={true}
onInput={handleInput}
dangerouslySetInnerHTML={{ __html: defaultValue.current }}
/>
</pre>
)
}
}

View File

@ -0,0 +1,37 @@
import DOMPurify from 'dompurify'
import { marked } from 'marked'
import { createDirectives, presetDirectiveConfigs } from 'marked-directive'
import { youtubeDirective } from './YoutubeDirective'
import { useMemo } from 'react'
interface ViewerProps {
markdown: string
}
export const Viewer = ({ markdown }: ViewerProps) => {
const html = useMemo(() => {
DOMPurify.addHook('beforeSanitizeAttributes', function (node) {
if (node.nodeName && node.nodeName === 'IFRAME') {
const src = node.attributes.getNamedItem('src')
if (!(src && src.value.startsWith('https://www.youtube.com/embed/'))) {
node.remove()
}
}
})
return DOMPurify.sanitize(
marked
.use(createDirectives([...presetDirectiveConfigs, youtubeDirective]))
.parse(`${markdown}`, {
async: false
}),
{
ADD_TAGS: ['iframe']
}
)
}, [markdown])
return (
<div className='viewer' dangerouslySetInnerHTML={{ __html: html }}></div>
)
}

View File

@ -0,0 +1,36 @@
import { LeafDirective } from 'mdast-util-directive'
import { usePublisher, insertDirective$, DialogButton } from '@mdxeditor/editor'
function getId(url: string) {
const regExp =
/^.*((youtu.be\/)|(v\/)|(\/u\/\w\/)|(embed\/)|(watch\?))\??v?=?([^#&?]*).*/
const match = url.match(regExp)
return match && match[7].length == 11 ? match[7] : false
}
export const YouTubeButton = () => {
const insertDirective = usePublisher(insertDirective$)
return (
<DialogButton
tooltipTitle='Insert Youtube video'
submitButtonTitle='Insert video'
dialogInputPlaceholder='Paste the youtube video URL'
buttonContent='YT'
onSubmit={(url) => {
const videoId = getId(url)
if (videoId) {
insertDirective({
name: 'youtube',
type: 'leafDirective',
attributes: { id: videoId },
children: []
} as LeafDirective)
} else {
alert('Invalid YouTube URL')
}
}}
/>
)
}

View File

@ -0,0 +1,37 @@
import { type DirectiveConfig } from 'marked-directive'
// defines `:youtube` directive
export const youtubeDirective: DirectiveConfig = {
level: 'block',
marker: '::',
renderer(token) {
//https://www.youtube.com/embed/<VIDEO_ID>
//::youtube{#<VIDEO_ID>}
let vid: string = ''
if (token.attrs && token.meta.name === 'youtube') {
if (token.attrs.id) {
vid = token.attrs.id as string // Get the video `id` attribute (common id style)
} else if (token.attrs.vid) {
vid = token.attrs.vid as string // Check for the `vid` attribute (youtube directive attribute style)
} else {
// Fallback for id
// In case that video starts with the number it will not be recongizned as an id
// We have to manually fetch it
for (const attr in token.attrs) {
if (
Object.prototype.hasOwnProperty.call(token.attrs, attr) &&
attr.startsWith('#')
) {
vid = attr.replace('#', '')
}
}
}
}
if (vid) {
return `<iframe title="Video embed" width="560" height="315" src="https://www.youtube.com/embed/${vid}" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>`
}
return false
}
}

View File

@ -0,0 +1,59 @@
import { LeafDirective } from 'mdast-util-directive'
import { DirectiveDescriptor } from '@mdxeditor/editor'
interface YoutubeDirectiveNode extends LeafDirective {
name: 'youtube'
attributes: { id: string }
}
export const YoutubeDirectiveDescriptor: DirectiveDescriptor<YoutubeDirectiveNode> =
{
name: 'youtube',
type: 'leafDirective',
testNode(node) {
return node.name === 'youtube'
},
attributes: ['id'],
hasChildren: false,
Editor: ({ mdastNode, lexicalNode, parentEditor }) => {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start'
}}
>
<button
type='button'
title='delete'
className='btnMain'
onClick={() => {
parentEditor.update(() => {
lexicalNode.selectNext()
lexicalNode.remove()
})
}}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 448 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M135.2 17.7L128 32 32 32C14.3 32 0 46.3 0 64S14.3 96 32 96l384 0c17.7 0 32-14.3 32-32s-14.3-32-32-32l-96 0-7.2-14.3C307.4 6.8 296.3 0 284.2 0L163.8 0c-12.1 0-23.2 6.8-28.6 17.7zM416 128L32 128 53.2 467c1.6 25.3 22.6 45 47.9 45l245.8 0c25.3 0 46.3-19.7 47.9-45L416 128z' />
</svg>
</button>
<iframe
width='560'
height='315'
src={`https://www.youtube.com/embed/${mdastNode.attributes.id}`}
title='YouTube video player'
frameBorder='0'
allow='accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share'
/>
</div>
)
}
}

View File

@ -1,30 +1,74 @@
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import '../styles/cardMod.css'
import { handleModImageError } from '../utils'
import { ModDetails } from 'types'
import { getModPageRoute } from 'routes'
import { kinds, nip19 } from 'nostr-tools'
import { useDidMount, useNDKContext, useReactions } from 'hooks'
import { toast } from 'react-toastify'
import { useComments } from 'hooks/useComments'
type ModCardProps = {
title: string
summary: string
backgroundLink: string
handleClick: () => void
}
export const ModCard = React.memo((props: ModDetails) => {
const [totalZappedAmount, setTotalZappedAmount] = useState(0)
const [commentCount, setCommentCount] = useState(0)
const { commentEvents } = useComments(props.author, props.aTag)
const { likesCount, disLikesCount } = useReactions({
pubkey: props.author,
eTag: props.id,
aTag: props.aTag
})
const { getTotalZapAmount } = useNDKContext()
useDidMount(() => {
getTotalZapAmount(props.author, props.id, props.aTag)
.then((res) => {
setTotalZappedAmount(res.accumulatedZapAmount)
})
.catch((err) => {
toast.error(err.message || err)
})
})
useEffect(() => {
setCommentCount(commentEvents.length)
}, [commentEvents])
const route = getModPageRoute(
nip19.naddrEncode({
identifier: props.aTag,
pubkey: props.author,
kind: kinds.ClassifiedListing
})
)
export const ModCard = ({
title,
summary,
backgroundLink,
handleClick
}: ModCardProps) => {
return (
<a className='cardModMainWrapperLink' onClick={handleClick}>
<Link className='cardModMainWrapperLink' to={route}>
<div className='cardModMain'>
<div
className='cMMPicture'
style={{
background: `url("${backgroundLink}") center / cover no-repeat`
}}
></div>
<div className='cMMPictureWrapper'>
<img
src={props.featuredImageUrl}
onError={handleModImageError}
className='cMMPicture'
alt={`featured image for mod ${props.title}`}
/>
{props.nsfw && (
<div className='IBMSMSMBSSTagsTag IBMSMSMBSSTagsTagNSFW IBMSMSMBSSTagsTagNSFWCard'>
<p>NSFW</p>
</div>
)}
{props.repost && (
<div className='IBMSMSMBSSTagsTag IBMSMSMBSSTagsTagRepost IBMSMSMBSSTagsTagRepostCard'>
<p>REPOST</p>
</div>
)}
</div>
<div className='cMMBody'>
<h3 className='cMMBodyTitle'>{title}</h3>
<p className='cMMBodyText'>{summary}</p>
<h3 className='cMMBodyTitle'>{props.title}</h3>
<p className='cMMBodyText'>{props.summary}</p>
<div className='cMMBodyGame'>
<p>{props.game}</p>
</div>
</div>
<div className='cMMFoot'>
<div className='cMMFootReactions'>
@ -38,7 +82,7 @@ export const ModCard = ({
>
<path d='M0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84.02L256 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 .0003 232.4 .0003 190.9L0 190.9z'></path>
</svg>
<p>420</p>
<p>{likesCount}</p>
</div>
<div className='cMMFootReactionsElement'>
<svg
@ -50,7 +94,7 @@ export const ModCard = ({
>
<path d='M512 440.1C512 479.9 479.7 512 439.1 512H71.92C32.17 512 0 479.8 0 440c0-35.88 26.19-65.35 60.56-70.85C43.31 356 32 335.4 32 312C32 272.2 64.25 240 104 240h13.99C104.5 228.2 96 211.2 96 192c0-35.38 28.56-64 63.94-64h16C220.1 128 256 92.12 256 48c0-17.38-5.784-33.35-15.16-46.47C245.8 .7754 250.9 0 256 0c53 0 96 43 96 96c0 11.25-2.288 22-5.913 32h5.879C387.3 128 416 156.6 416 192c0 19.25-8.59 36.25-22.09 48H408C447.8 240 480 272.2 480 312c0 23.38-11.38 44.01-28.63 57.14C485.7 374.6 512 404.3 512 440.1z'></path>
</svg>
<p>420</p>
<p>{disLikesCount}</p>
</div>
<div className='cMMFootReactionsElement'>
<svg
@ -62,11 +106,23 @@ export const ModCard = ({
>
<path d='M256 32C114.6 32 .0272 125.1 .0272 240c0 49.63 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734C1.979 478.2 4.75 480 8 480c66.25 0 115.1-31.76 140.6-51.39C181.2 440.9 217.6 448 256 448c141.4 0 255.1-93.13 255.1-208S397.4 32 256 32z'></path>
</svg>
<p>420</p>
<p>{commentCount}</p>
</div>
<div className='cMMFootReactionsElement'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-64 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M240.5 224H352C365.3 224 377.3 232.3 381.1 244.7C386.6 257.2 383.1 271.3 373.1 280.1L117.1 504.1C105.8 513.9 89.27 514.7 77.19 505.9C65.1 497.1 60.7 481.1 66.59 467.4L143.5 288H31.1C18.67 288 6.733 279.7 2.044 267.3C-2.645 254.8 .8944 240.7 10.93 231.9L266.9 7.918C278.2-1.92 294.7-2.669 306.8 6.114C318.9 14.9 323.3 30.87 317.4 44.61L240.5 224z'></path>
</svg>
<p>{totalZappedAmount}</p>
</div>
</div>
</div>
</div>
</a>
</Link>
)
}
})

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,39 @@
import { AlertPopupProps } from 'types'
import { AlertPopup } from './AlertPopup'
import { useLocalStorage } from 'hooks'
type NsfwAlertPopup = Omit<AlertPopupProps, 'header' | 'label'>
/**
* Triggers when the user wants to switch the filter to see any of the NSFW options
* (including preferences)
*
* Option will be remembered for the session only and will not show the popup again
*/
export const NsfwAlertPopup = ({
handleConfirm,
handleClose
}: NsfwAlertPopup) => {
const [confirmNsfw, setConfirmNsfw] = useLocalStorage<boolean>(
'confirm-nsfw',
false
)
return (
!confirmNsfw && (
<AlertPopup
header='Confirm'
label='Are you above 18 years of age?'
handleClose={() => {
handleConfirm(false)
handleClose()
}}
handleConfirm={(confirm: boolean) => {
setConfirmNsfw(confirm)
handleConfirm(confirm)
handleClose()
}}
/>
)
)
}

View File

@ -0,0 +1,48 @@
import { nip19 } from 'nostr-tools'
import { appRoutes, getProfilePageRoute } from 'routes'
import { npubToHex } from 'utils'
import { ProfileLink } from './ProfileLink'
interface OriginalAuthorProps {
value: string
fallback?: boolean
}
export const OriginalAuthor = ({
value,
fallback = false
}: OriginalAuthorProps) => {
let profilePubkey
let displayName = '[name not set up]'
// Try to decode/encode depending on what we send to link
let profileRoute = appRoutes.home
try {
if (value.startsWith('nprofile1')) {
const decoded = nip19.decode(value as `nprofile1${string}`)
profileRoute = getProfilePageRoute(value)
profilePubkey = decoded?.data.pubkey
} else if (value.startsWith('npub1')) {
profilePubkey = npubToHex(value)
const nprofile = profilePubkey
? nip19.nprofileEncode({
pubkey: profilePubkey
})
: undefined
if (nprofile) {
profileRoute = getProfilePageRoute(nprofile)
}
} else {
displayName = value
}
} catch (error) {
console.error('Failed to create profile link:', error)
displayName = value
}
if (profileRoute && profilePubkey)
return <ProfileLink pubkey={profilePubkey} profileRoute={profileRoute} />
return fallback ? displayName : null
}

View File

@ -0,0 +1,138 @@
import React from 'react'
type PaginationProps = {
page: number
disabledNext: boolean
handlePrev: () => void
handleNext: () => void
}
export const Pagination = React.memo(
({ page, disabledNext, handlePrev, handleNext }: PaginationProps) => {
return (
<div className='IBMSecMain'>
<div className='PaginationMain'>
<div className='PaginationMainInside'>
<button
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
onClick={handlePrev}
disabled={page === 1}
>
<i className='fas fa-chevron-left'></i>
</button>
<div className='PaginationMainInsideBoxGroup'>
<button className='PaginationMainInsideBox PMIBActive'>
<p>{page}</p>
</button>
</div>
<button
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
onClick={handleNext}
disabled={disabledNext}
>
<i className='fas fa-chevron-right'></i>
</button>
</div>
</div>
</div>
)
}
)
type PaginationWithPageNumbersProps = {
currentPage: number
totalPages: number
handlePageChange: (page: number) => void
}
export const PaginationWithPageNumbers = ({
currentPage,
totalPages,
handlePageChange
}: PaginationWithPageNumbersProps) => {
// Function to render the pagination controls with page numbers
const renderPagination = () => {
const pagesToShow = 5 // Number of page numbers to show around the current page
const pageNumbers: (number | string)[] = [] // Array to store page numbers and ellipses
// Case when the total number of pages is less than or equal to the limit
if (totalPages <= pagesToShow + 2) {
for (let i = 1; i <= totalPages; i++) {
pageNumbers.push(i) // Add all pages to the pagination
}
} else {
// Add the first page (always visible)
pageNumbers.push(1)
// Calculate the range of pages to show around the current page
const startPage = Math.max(2, currentPage - Math.floor(pagesToShow / 2))
const endPage = Math.min(
totalPages - 1,
currentPage + Math.floor(pagesToShow / 2)
)
// Add ellipsis if there are pages between the first page and the startPage
if (startPage > 2) pageNumbers.push('...')
// Add the pages around the current page
for (let i = startPage; i <= endPage; i++) {
pageNumbers.push(i)
}
// Add ellipsis if there are pages between the endPage and the last page
if (endPage < totalPages - 1) pageNumbers.push('...')
// Add the last page (always visible)
pageNumbers.push(totalPages)
}
// Map over the array and render each page number or ellipsis
return pageNumbers.map((page, index) => {
if (typeof page === 'number') {
// For actual page numbers, render clickable boxes
return (
<div
key={index}
className={`PaginationMainInsideBox ${
currentPage === page ? 'PMIBActive' : '' // Highlight the current page
}`}
onClick={() => handlePageChange(page)} // Navigate to the selected page
>
<p>{page}</p>
</div>
)
} else {
// For ellipses, render non-clickable dots
return (
<p key={index} className='PaginationMainInsideBox PMIBDots'>
...
</p>
)
}
})
}
return (
<div className='IBMSecMain'>
<div className='PaginationMain'>
<div className='PaginationMainInside'>
<div
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
onClick={() => handlePageChange(currentPage - 1)}
>
<i className='fas fa-chevron-left'></i>
</div>
<div className='PaginationMainInsideBoxGroup'>
{renderPagination()}
</div>
<div
className='PaginationMainInsideBox PaginationMainInsideBoxArrows'
onClick={() => handlePageChange(currentPage + 1)}
>
<i className='fas fa-chevron-right'></i>
</div>
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,22 @@
interface PostWarningsProps {
type: 'user' | 'admin'
}
export const PostWarnings = ({ type }: PostWarningsProps) => (
<div className='IBMSMSMBSSWarning'>
<p>
{type === 'admin' ? (
<>
Warning: This post has been blocked/hidden by the site for one of the
following reasons:
<br />
Malware, Not a Mod, Illegal, Spam, Verified Report of Unauthorized
Repost.
<br />
</>
) : (
<>Notice: You have blocked this post</>
)}
</p>
</div>
)

View File

@ -0,0 +1,15 @@
import { useProfile } from 'hooks/useProfile'
import { Link } from 'react-router-dom'
interface ProfileLinkProps {
pubkey: string
profileRoute: string
}
export const ProfileLink = ({ pubkey, profileRoute }: ProfileLinkProps) => {
const profile = useProfile(pubkey)
const displayName =
profile?.displayName || profile?.name || '[name not set up]'
return <Link to={profileRoute}>{displayName}</Link>
}

View File

@ -1,172 +1,214 @@
import { FALLBACK_PROFILE_IMAGE } from 'constants.ts'
import { Event, Filter, kinds, nip19, UnsignedEvent } from 'nostr-tools'
import { QRCodeSVG } from 'qrcode.react'
import { useState } from 'react'
import { Link } from 'react-router-dom'
import { toast } from 'react-toastify'
import {
useAppSelector,
useBodyScrollDisable,
useDidMount,
useNDKContext
} from '../hooks'
import { appRoutes, getProfilePageRoute } from '../routes'
import '../styles/author.css'
import '../styles/innerPage.css'
import '../styles/socialPosts.css'
import { UserRelaysType } from '../types'
import {
copyTextToClipboard,
hexToNpub,
log,
LogType,
now,
npubToHex
} from '../utils'
import { LoadingSpinner } from './LoadingSpinner'
import { ZapPopUp } from './Zap'
import placeholder from '../assets/img/DEGMods Placeholder Img.png'
import { NDKEvent } from '@nostr-dev-kit/ndk'
import { useProfile } from 'hooks/useProfile'
export const ProfileSection = () => {
type Props = {
pubkey: string
}
export const ProfileSection = ({ pubkey }: Props) => {
return (
<div className='IBMSMSplitMainSmallSide'>
<div className='IBMSMSplitMainSmallSideSec'>
<div className='IBMSMSMSSS_Author'>
<div className='IBMSMSMSSS_Author_Top'>
<div className='IBMSMSMSSS_Author_Top_Left'>
<div className='IBMSMSplitMainSmallSideSecWrapper'>
<div className='IBMSMSplitMainSmallSideSec'>
<Profile pubkey={pubkey} />
</div>
<div className='IBMSMSplitMainSmallSideSec'>
<div className='IBMSMSMSSS_ShortPosts'>
{posts.map((post, index) => (
<a
className='IBMSMSMSSS_Author_Top_Left_InsideLinkWrapper'
href='profile.html'
key={'post' + index}
className='IBMSMSMSSS_ShortPostsPostLink'
href={post.link}
>
<div className='IBMSMSMSSS_Author_Top_Left_Inside'>
<div className='IBMSMSMSSS_Author_Top_Left_InsidePic'>
<div className='IBMSMSMSSS_Author_Top_PPWrapper'>
<div
className='IBMSMSMSSS_Author_Top_PP'
style={{
background:
"url('/assets/img/media-cache%20(4).png') center / cover no-repeat"
}}
></div>
<div className='IBMSMSMSSS_ShortPostsPost'>
<div className='IBMSMSMSSS_ShortPostsPost_Top'>
<p className='IBMSMSMSSS_ShortPostsPost_TopName'>
{post.name}
</p>
<div className='IBMSMSMSSS_ShortPostsPost_TopLink'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_ShortPostsPost_TopLinkIcon'
style={{ width: '100%', height: '100%' }}
>
<path d='M256 64C256 46.33 270.3 32 288 32H415.1C415.1 32 415.1 32 415.1 32C420.3 32 424.5 32.86 428.2 34.43C431.1 35.98 435.5 38.27 438.6 41.3C438.6 41.35 438.6 41.4 438.7 41.44C444.9 47.66 447.1 55.78 448 63.9C448 63.94 448 63.97 448 64V192C448 209.7 433.7 224 416 224C398.3 224 384 209.7 384 192V141.3L214.6 310.6C202.1 323.1 181.9 323.1 169.4 310.6C156.9 298.1 156.9 277.9 169.4 265.4L338.7 96H288C270.3 96 256 81.67 256 64V64zM0 128C0 92.65 28.65 64 64 64H160C177.7 64 192 78.33 192 96C192 113.7 177.7 128 160 128H64V416H352V320C352 302.3 366.3 288 384 288C401.7 288 416 302.3 416 320V416C416 451.3 387.3 480 352 480H64C28.65 480 0 451.3 0 416V128z'></path>
</svg>
</div>
</div>
<div className='IBMSMSMSSS_Author_Top_Left_InsideDetails'>
<div className='IBMSMSMSSS_Author_TopWrapper'>
<p className='IBMSMSMSSS_Author_Top_Name'>
{author.name}
</p>
<p className='IBMSMSMSSS_Author_Top_Handle'>
{author.handle}
</p>
</div>
<div className='IBMSMSMSSS_ShortPostsPost_Bottom'>
<p>{post.content}</p>
{post.imageUrl && (
<div
className='IBMSMSMSSS_ShortPostsPost_BottomImg'
style={{
background: `linear-gradient(0deg, #232323 5%, rgba(255, 255, 255, 0)), url("${post.imageUrl}") top / cover no-repeat`
}}
></div>
)}
</div>
</div>
</a>
<div className='IBMSMSMSSS_Author_Top_AddressWrapper'>
<div className='IBMSMSMSSS_Author_Top_AddressWrapped'>
<p
id='SiteOwnerAddress'
className='IBMSMSMSSS_Author_Top_Address'
>
{author.address}
</p>
</div>
<div className='IBMSMSMSSS_Author_Top_IconWrapper'>
<div
id='copySiteOwnerAddress'
className='IBMSMSMSSS_Author_Top_IconWrapped'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M384 96L384 0h-112c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48H464c26.51 0 48-21.49 48-48V128h-95.1C398.4 128 384 113.6 384 96zM416 0v96h96L416 0zM192 352V128h-144c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48h192c26.51 0 48-21.49 48-48L288 416h-32C220.7 416 192 387.3 192 352z'></path>
</svg>
</div>
<div className='IBMSMSMSSS_Author_Top_IconWrapped IBMSMSMSSS_Author_Top_IconWrappedQR'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M144 32C170.5 32 192 53.49 192 80V176C192 202.5 170.5 224 144 224H48C21.49 224 0 202.5 0 176V80C0 53.49 21.49 32 48 32H144zM128 96H64V160H128V96zM144 288C170.5 288 192 309.5 192 336V432C192 458.5 170.5 480 144 480H48C21.49 480 0 458.5 0 432V336C0 309.5 21.49 288 48 288H144zM128 352H64V416H128V352zM256 80C256 53.49 277.5 32 304 32H400C426.5 32 448 53.49 448 80V176C448 202.5 426.5 224 400 224H304C277.5 224 256 202.5 256 176V80zM320 160H384V96H320V160zM352 448H384V480H352V448zM448 480H416V448H448V480zM416 288H448V416H352V384H320V480H256V288H352V320H416V288z'></path>
</svg>
</div>
<a
className='IBMSMSMSSS_Author_Top_IconWrapped'
href='https://primal.net/p/npub18n4ysp43ux5c98fs6h9c57qpr4p8r3j8f6e32v0vj8egzy878aqqyzzk9r'
target='_blank'
rel='noopener noreferrer'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M256 64C256 46.33 270.3 32 288 32H415.1C415.1 32 415.1 32 415.1 32C420.3 32 424.5 32.86 428.2 34.43C431.1 35.98 435.5 38.27 438.6 41.3C438.6 41.35 438.6 41.4 438.7 41.44C444.9 47.66 447.1 55.78 448 63.9C448 63.94 448 63.97 448 64V192C448 209.7 433.7 224 416 224C398.3 224 384 209.7 384 192V141.3L214.6 310.6C202.1 323.1 181.9 323.1 169.4 310.6C156.9 298.1 156.9 277.9 169.4 265.4L338.7 96H288C270.3 96 256 81.67 256 64V64zM0 128C0 92.65 28.65 64 64 64H160C177.7 64 192 78.33 192 96C192 113.7 177.7 128 160 128H64V416H352V320C352 302.3 366.3 288 384 288C401.7 288 416 302.3 416 320V416C416 451.3 387.3 480 352 480H64C28.65 480 0 451.3 0 416V128z'></path>
</svg>
</a>
</div>
</div>
</div>
<div className='IBMSMSMSSS_Author_Top_Details'>
<p className='IBMSMSMSSS_Author_Top_Bio'>{author.bio}</p>
<div
id='OwnerFollowLogin'
className='IBMSMSMSSS_Author_Top_NostrLinks'
style={{ display: 'flex' }}
></div>
</div>
))}
</div>
<button className='btn btnMain' type='button'>
Follow
</button>
</div>
</div>
<div className='IBMSMSplitMainSmallSideSec'>
<div className='IBMSMSMSSS_ShortPosts'>
{posts.map((post, index) => (
<a
key={'post' + index}
className='IBMSMSMSSS_ShortPostsPostLink'
href={post.link}
>
<div className='IBMSMSMSSS_ShortPostsPost'>
<div className='IBMSMSMSSS_ShortPostsPost_Top'>
<p className='IBMSMSMSSS_ShortPostsPost_TopName'>
{post.name}
</p>
<div className='IBMSMSMSSS_ShortPostsPost_TopLink'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_ShortPostsPost_TopLinkIcon'
style={{ width: '100%', height: '100%' }}
>
<path d='M256 64C256 46.33 270.3 32 288 32H415.1C415.1 32 415.1 32 415.1 32C420.3 32 424.5 32.86 428.2 34.43C431.1 35.98 435.5 38.27 438.6 41.3C438.6 41.35 438.6 41.4 438.7 41.44C444.9 47.66 447.1 55.78 448 63.9C448 63.94 448 63.97 448 64V192C448 209.7 433.7 224 416 224C398.3 224 384 209.7 384 192V141.3L214.6 310.6C202.1 323.1 181.9 323.1 169.4 310.6C156.9 298.1 156.9 277.9 169.4 265.4L338.7 96H288C270.3 96 256 81.67 256 64V64zM0 128C0 92.65 28.65 64 64 64H160C177.7 64 192 78.33 192 96C192 113.7 177.7 128 160 128H64V416H352V320C352 302.3 366.3 288 384 288C401.7 288 416 302.3 416 320V416C416 451.3 387.3 480 352 480H64C28.65 480 0 451.3 0 416V128z'></path>
</svg>
</div>
</div>
<div className='IBMSMSMSSS_ShortPostsPost_Bottom'>
<p>{post.content}</p>
{post.imageUrl && (
<div
className='IBMSMSMSSS_ShortPostsPost_BottomImg'
style={{
background: `linear-gradient(0deg, #232323 5%, rgba(255, 255, 255, 0)), url("${post.imageUrl}") top / cover no-repeat`
}}
></div>
)}
</div>
</div>
</a>
))}
</div>
</div>
</div>
)
}
interface Author {
name: string
handle: string
address: string
bio: string
type ProfileProps = {
pubkey: string
}
const author: Author = {
name: 'Freakoverse',
handle: 'freakoverse@degmods.com',
address: 'npub18n4ysp43ux5c98fs6h9c57qpr4p8r3j8f6e32v0vj8egzy878aqqyzzk9r',
bio: `I guess I'm one of those #vtubers . Having fun talking about general topics, vrchat/similar, and games. Also #indiedev #gamedev You can call me: Freak فْرِيكٌ <20><><EFBFBD>リク (still learning Nihongo). #envtuber #podcast #gaming #gamedev`
export const Profile = ({ pubkey }: ProfileProps) => {
const profile = useProfile(pubkey)
const displayName =
profile?.displayName || profile?.name || '[name not set up]'
const about = profile?.bio || profile?.about || '[bio not set up]'
const image = profile?.image || FALLBACK_PROFILE_IMAGE
const nip05 = profile?.nip05
const lud16 = profile?.lud16
const npub = hexToNpub(pubkey)
const handleCopy = async () => {
copyTextToClipboard(npub).then((isCopied) => {
if (isCopied) {
toast.success('Npub copied to clipboard!')
} else {
toast.error(
'Failed to copy, look into console for more details on error!'
)
}
})
}
// Try to encode
let profileRoute = appRoutes.home
let nprofile: string | undefined
try {
const hexPubkey = npubToHex(pubkey)
nprofile = hexPubkey
? nip19.nprofileEncode({
pubkey: hexPubkey
})
: undefined
profileRoute = nprofile ? getProfilePageRoute(nprofile) : appRoutes.home
} catch (error) {
// Silently ignore and redirect to home
log(true, LogType.Error, 'Failed to encode profile.', error)
}
return (
<div className='IBMSMSMSSS_Author'>
<div className='IBMSMSMSSS_Author_Top'>
<div className='IBMSMSMSSS_Author_Top_Left'>
<Link
className='IBMSMSMSSS_Author_Top_Left_InsideLinkWrapper'
to={profileRoute}
>
<div className='IBMSMSMSSS_Author_Top_Left_Inside'>
<div className='IBMSMSMSSS_Author_Top_Left_InsidePic'>
<div className='IBMSMSMSSS_Author_Top_PPWrapper'>
<div
className='IBMSMSMSSS_Author_Top_PP'
style={{
background: `url('${image}') center / cover no-repeat`
}}
></div>
</div>
</div>
<div className='IBMSMSMSSS_Author_Top_Left_InsideDetails'>
<div className='IBMSMSMSSS_Author_TopWrapper'>
<p className='IBMSMSMSSS_Author_Top_Name'>{displayName}</p>
{/* Nip05 can sometimes be an empty object '{}' which causes the error */}
{typeof nip05 === 'string' && nip05 !== '' && (
<p className='IBMSMSMSSS_Author_Top_Handle'>{nip05}</p>
)}
</div>
</div>
</div>
</Link>
<div className='IBMSMSMSSS_Author_Top_AddressWrapper'>
<div className='IBMSMSMSSS_Author_Top_AddressWrapped'>
<p
id='SiteOwnerAddress'
className='IBMSMSMSSS_Author_Top_Address'
>
{npub}
</p>
</div>
<div className='IBMSMSMSSS_Author_Top_IconWrapper'>
<div
id='copySiteOwnerAddress'
className='IBMSMSMSSS_Author_Top_IconWrapped'
onClick={handleCopy}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M384 96L384 0h-112c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48H464c26.51 0 48-21.49 48-48V128h-95.1C398.4 128 384 113.6 384 96zM416 0v96h96L416 0zM192 352V128h-144c-26.51 0-48 21.49-48 48v288c0 26.51 21.49 48 48 48h192c26.51 0 48-21.49 48-48L288 416h-32C220.7 416 192 387.3 192 352z'></path>
</svg>
</div>
{typeof nprofile !== 'undefined' && (
<ProfileQRButtonWithPopUp nprofile={nprofile} />
)}
{typeof lud16 !== 'undefined' && lud16 !== '' && (
<ZapButtonWithPopUp pubkey={pubkey} />
)}
</div>
</div>
</div>
<div className='IBMSMSMSSS_Author_Top_Details'>
<p className='IBMSMSMSSS_Author_Top_Bio'>{about}</p>
<div
id='OwnerFollowLogin'
className='IBMSMSMSSS_Author_Top_NostrLinks'
style={{ display: 'flex' }}
></div>
</div>
</div>
<FollowButton pubkey={pubkey} />
</div>
)
}
interface Post {
@ -178,47 +220,335 @@ interface Post {
const posts: Post[] = [
{
name: 'Freakoverse',
name: 'User name',
link: `feed-note.html`,
content: ` So I know HTML/CSS pretty well and I'm confident with
them.\n\n\n\nI also know UI and UX, as
well as graphic design (nowhere near pros, but I'm the
guy they call when the pro isn't around or when
something is needed quickly).\n\n\n\nI
don't know much java. Usually, I'd search for what I
want, find something close, and fiddle with it until
it works/gets the desired result ish. AI is helping
with this a lot actually.\n\n\n\nThis
helped me create my own sites and my own designs to
life, though just at a static level. I always wanted
to make dynamic sites, but the idea of doing backend
stuff is complex to me. However...\n\n\n\n"Let
me look into it again" and thought if I could make a
simple blog. Digging a bit, and watching/skimming
through tutorials, I realized that I think I can.\n\n\n\nNot
sure when I'll start/attempt this, but will journey
into learning the basics of PHP and attempting to make
a blog. I guess I'll learn the basics of PHP, and then
head into Laravel. If I manage to get the hang of it,
I'll attempt to make a complex old project I had, and
if I do manage to do it, I'll be pretty confident
=3\n\n\n\nAside from that, would be
nice to make a website, a personal blog, that shows my
long-form articles only. Hopefully by then things
would be more stable nostr-wise, cleaner, and easier
in terms of learning, so I'd be able to do it (or
collab with someone to do it / to make a template for
all to have and deploy easily).\n\n\n\n`
content: `user text, this is a long string of temporary text that would be replaced with the user post from their short posts`
},
{
name: 'Freakoverse',
link: 'https://primal.net/e/note1j7zmj4g6grc39zq30xq2de95dfszjpwlqvsktv65h7kuzjzsytjqgx73c7',
content: `Happy to see some gamedevs port their games from Unity to Godot, after that Unity fiasco, like this one here called Road To Vostok`
name: 'User name',
link: 'feed-note.html',
content: `user text, this is a long string of temporary text that would be replaced with the user post from their short posts`
},
{
name: 'Freakoverse',
name: 'User name',
link: `feed-note.html`,
content: `This is good.`,
imageUrl: '/assets/img/media-cache%20(1).jpg'
content: `user text, this is a long string of temporary text that would be replaced with the user post from their short posts`,
imageUrl: placeholder
}
]
type QRButtonWithPopUpProps = {
nprofile: string
}
export const ProfileQRButtonWithPopUp = ({
nprofile
}: QRButtonWithPopUpProps) => {
const [isOpen, setIsOpen] = useState(false)
useBodyScrollDisable(isOpen)
const onQrCodeClicked = async () => {
const href = `https://njump.me/${nprofile}`
const a = document.createElement('a')
a.href = href
a.target = '_blank' // Open in a new tab
a.rel = 'noopener noreferrer' // Recommended for security reasons
a.click()
}
return (
<>
<div
className='IBMSMSMSSS_Author_Top_IconWrapped IBMSMSMSSS_Author_Top_IconWrappedQR'
onClick={() => setIsOpen(true)}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M144 32C170.5 32 192 53.49 192 80V176C192 202.5 170.5 224 144 224H48C21.49 224 0 202.5 0 176V80C0 53.49 21.49 32 48 32H144zM128 96H64V160H128V96zM144 288C170.5 288 192 309.5 192 336V432C192 458.5 170.5 480 144 480H48C21.49 480 0 458.5 0 432V336C0 309.5 21.49 288 48 288H144zM128 352H64V416H128V352zM256 80C256 53.49 277.5 32 304 32H400C426.5 32 448 53.49 448 80V176C448 202.5 426.5 224 400 224H304C277.5 224 256 202.5 256 176V80zM320 160H384V96H320V160zM352 448H384V480H352V448zM448 480H416V448H448V480zM416 288H448V416H352V384H320V480H256V288H352V320H416V288z'></path>
</svg>
</div>
{isOpen && (
<div className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard popUpMainCardQR'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>Nostr Address</h3>
</div>
<div
className='popUpMainCardTopClose'
onClick={() => setIsOpen(false)}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
style={{ zIndex: 1 }}
>
<path d='M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z'></path>
</svg>
</div>
</div>
<div className='popUpMainCardBottom'>
<QRCodeSVG
className='popUpMainCardBottomQR'
onClick={onQrCodeClicked}
value={nprofile}
height={235}
width={235}
/>
</div>
</div>
</div>
</div>
</div>
)}
</>
)
}
type ZapButtonWithPopUpProps = {
pubkey: string
}
const ZapButtonWithPopUp = ({ pubkey }: ZapButtonWithPopUpProps) => {
const [isOpen, setIsOpen] = useState(false)
useBodyScrollDisable(isOpen)
return (
<>
<div
className='IBMSMSMSSS_Author_Top_IconWrapped'
onClick={() => setIsOpen(true)}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMSSS_Author_Top_Icon'
>
<path d='M240.5 224H352C365.3 224 377.3 232.3 381.1 244.7C386.6 257.2 383.1 271.3 373.1 280.1L117.1 504.1C105.8 513.9 89.27 514.7 77.19 505.9C65.1 497.1 60.7 481.1 66.59 467.4L143.5 288H31.1C18.67 288 6.733 279.7 2.044 267.3C-2.645 254.8 .8944 240.7 10.93 231.9L266.9 7.918C278.2-1.92 294.7-2.669 306.8 6.114C318.9 14.9 323.3 30.87 317.4 44.61L240.5 224z'></path>
</svg>
</div>
{isOpen && (
<ZapPopUp
title='Tip/Zap'
receiver={pubkey}
handleClose={() => setIsOpen(false)}
/>
)}
</>
)
}
type FollowButtonProps = {
pubkey: string
}
const FollowButton = ({ pubkey }: FollowButtonProps) => {
const { ndk, fetchEventFromUserRelays, publish } = useNDKContext()
const [isFollowing, setIsFollowing] = useState(false)
const [isLoading, setIsLoading] = useState(false)
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
const userState = useAppSelector((state) => state.user)
useDidMount(async () => {
if (userState.auth && userState.user?.pubkey) {
const userHexKey = userState.user.pubkey as string
const { isFollowing: isAlreadyFollowing } = await checkIfFollowing(
userHexKey,
pubkey
)
setIsFollowing(isAlreadyFollowing)
}
})
const getUserPubKey = async (): Promise<string | null> => {
if (userState.auth && userState.user?.pubkey) {
return userState.user.pubkey as string
} else {
try {
return (await window.nostr?.getPublicKey()) as string
} catch (error) {
log(true, LogType.Error, `Could not get pubkey`, error)
return null
}
}
}
const checkIfFollowing = async (
userHexKey: string,
pubkey: string
): Promise<{
isFollowing: boolean
tags: string[][]
}> => {
const filter: Filter = {
kinds: [kinds.Contacts],
authors: [userHexKey]
}
const contactListEvent = await fetchEventFromUserRelays(
filter,
userHexKey,
UserRelaysType.Both
)
if (!contactListEvent)
return {
isFollowing: false,
tags: []
}
return {
isFollowing: contactListEvent.tags.some(
(t) => t[0] === 'p' && t[1] === pubkey
),
tags: contactListEvent.tags
}
}
const signAndPublishEvent = async (
unsignedEvent: UnsignedEvent
): Promise<boolean> => {
const signedEvent = await window.nostr
?.signEvent(unsignedEvent)
.then((event) => event as Event)
.catch((err) => {
toast.error('Failed to sign the event!')
log(true, LogType.Error, 'Failed to sign the event!', err)
return null
})
if (!signedEvent) return false
const ndkEvent = new NDKEvent(ndk, signedEvent)
const publishedOnRelays = await publish(ndkEvent)
if (publishedOnRelays.length === 0) {
toast.error('Failed to publish event on any relay')
return false
}
toast.success(
`Event published successfully to the following relays\n\n${publishedOnRelays.join(
'\n'
)}`
)
return true
}
const handleFollow = async () => {
setIsLoading(true)
setLoadingSpinnerDesc('Processing follow request')
const userHexKey = await getUserPubKey()
if (!userHexKey) {
setIsLoading(false)
toast.error('Could not get pubkey')
return
}
const { isFollowing: isAlreadyFollowing, tags } = await checkIfFollowing(
userHexKey,
pubkey
)
if (isAlreadyFollowing) {
toast.info('Already following!')
setIsFollowing(true)
setIsLoading(false)
return
}
const unsignedEvent: UnsignedEvent = {
content: '',
created_at: now(),
kind: kinds.Contacts,
pubkey: userHexKey,
tags: [...tags, ['p', pubkey]]
}
setLoadingSpinnerDesc('Signing and publishing follow event')
const success = await signAndPublishEvent(unsignedEvent)
setIsFollowing(success)
setIsLoading(false)
}
const handleUnFollow = async () => {
setIsLoading(true)
setLoadingSpinnerDesc('Processing unfollow request')
const userHexKey = await getUserPubKey()
if (!userHexKey) {
setIsLoading(false)
toast.error('Could not get pubkey')
return
}
const filter: Filter = {
kinds: [kinds.Contacts],
authors: [userHexKey]
}
const contactListEvent = await fetchEventFromUserRelays(
filter,
userHexKey,
UserRelaysType.Both
)
if (
!contactListEvent ||
!contactListEvent.tags.some((t) => t[0] === 'p' && t[1] === pubkey)
) {
// could not found target pubkey in user's follow list
// so, just update the status and return
setIsFollowing(false)
setIsLoading(false)
return
}
const unsignedEvent: UnsignedEvent = {
content: '',
created_at: now(),
kind: kinds.Contacts,
pubkey: userHexKey,
tags: contactListEvent.tags.filter(
(t) => !(t[0] === 'p' && t[1] === pubkey)
)
}
setLoadingSpinnerDesc('Signing and publishing unfollow event')
const success = await signAndPublishEvent(unsignedEvent)
setIsFollowing(!success)
setIsLoading(false)
}
return (
<>
<button
className='btn btnMain'
type='button'
onClick={isFollowing ? handleUnFollow : handleFollow}
>
{isFollowing ? 'Un-Follow' : 'Follow'}
</button>
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
</>
)
}

View File

@ -0,0 +1,93 @@
import { useFetcher } from 'react-router-dom'
import { CheckboxFieldUncontrolled } from 'components/Inputs'
import { useEffect } from 'react'
import { ReportReason } from 'types/report'
import { LoadingSpinner } from './LoadingSpinner'
import { PopupProps } from 'types'
type ReportPopupProps = {
openedAt: number
reasons: ReportReason[]
} & PopupProps
export const ReportPopup = ({
openedAt,
reasons,
handleClose
}: ReportPopupProps) => {
// Use openedAt to allow for multiple reports
// by default, fetcher will remember the data
const fetcher = useFetcher({ key: openedAt.toString() })
// Close automatically if action succeeds
useEffect(() => {
if (fetcher.data) {
const { isSent } = fetcher.data
if (isSent) {
handleClose()
}
}
}, [fetcher, handleClose])
return (
<>
{fetcher.state !== 'idle' && <LoadingSpinner desc={''} />}
<div className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard popUpMainCardQR'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>Report Post</h3>
</div>
<div className='popUpMainCardTopClose' onClick={handleClose}>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
style={{ zIndex: 1 }}
>
<path d='M310.6 361.4c12.5 12.5 12.5 32.75 0 45.25C304.4 412.9 296.2 416 288 416s-16.38-3.125-22.62-9.375L160 301.3L54.63 406.6C48.38 412.9 40.19 416 32 416S15.63 412.9 9.375 406.6c-12.5-12.5-12.5-32.75 0-45.25l105.4-105.4L9.375 150.6c-12.5-12.5-12.5-32.75 0-45.25s32.75-12.5 45.25 0L160 210.8l105.4-105.4c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25l-105.4 105.4L310.6 361.4z'></path>
</svg>
</div>
</div>
<div className='pUMCB_Zaps'>
<fetcher.Form
className='pUMCB_ZapsInside'
method='post'
action='report'
>
<div className='inputLabelWrapperMain'>
<label
className='form-label labelMain'
style={{ fontWeight: 'bold' }}
>
Why are you reporting this?
</label>
{reasons.map((r) => (
<CheckboxFieldUncontrolled
key={r.key}
label={r.label}
name={r.key}
defaultChecked={false}
/>
))}
</div>
<button
className='btn btnMain pUMCB_Report'
type='submit'
style={{ width: '100%' }}
>
Submit Report
</button>
</fetcher.Form>
</div>
</div>
</div>
</div>
</div>
</>
)
}

51
src/components/SVGs.tsx Normal file
View File

@ -0,0 +1,51 @@
export const ProfileSVG = (props: React.SVGProps<SVGSVGElement>) => (
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
{...props}
>
<path d='M224 256c70.7 0 128-57.31 128-128s-57.3-128-128-128C153.3 0 96 57.31 96 128S153.3 256 224 256zM274.7 304H173.3C77.61 304 0 381.6 0 477.3c0 19.14 15.52 34.67 34.66 34.67h378.7C432.5 512 448 496.5 448 477.3C448 381.6 370.4 304 274.7 304z'></path>
</svg>
)
export const RelaySVG = (props: React.SVGProps<SVGSVGElement>) => (
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
{...props}
>
<path d='M480 288H32c-17.62 0-32 14.38-32 32v128c0 17.62 14.38 32 32 32h448c17.62 0 32-14.38 32-32v-128C512 302.4 497.6 288 480 288zM352 408c-13.25 0-24-10.75-24-24s10.75-24 24-24s24 10.75 24 24S365.3 408 352 408zM416 408c-13.25 0-24-10.75-24-24s10.75-24 24-24s24 10.75 24 24S429.3 408 416 408zM480 32H32C14.38 32 0 46.38 0 64v128c0 17.62 14.38 32 32 32h448c17.62 0 32-14.38 32-32V64C512 46.38 497.6 32 480 32zM352 152c-13.25 0-24-10.75-24-24S338.8 104 352 104S376 114.8 376 128S365.3 152 352 152zM416 152c-13.25 0-24-10.75-24-24S402.8 104 416 104S440 114.8 440 128S429.3 152 416 152z'></path>
</svg>
)
export const PreferenceSVG = (props: React.SVGProps<SVGSVGElement>) => (
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
{...props}
>
<path d='M0 416C0 398.3 14.33 384 32 384H86.66C99 355.7 127.2 336 160 336C192.8 336 220.1 355.7 233.3 384H480C497.7 384 512 398.3 512 416C512 433.7 497.7 448 480 448H233.3C220.1 476.3 192.8 496 160 496C127.2 496 99 476.3 86.66 448H32C14.33 448 0 433.7 0 416V416zM192 416C192 398.3 177.7 384 160 384C142.3 384 128 398.3 128 416C128 433.7 142.3 448 160 448C177.7 448 192 433.7 192 416zM352 176C384.8 176 412.1 195.7 425.3 224H480C497.7 224 512 238.3 512 256C512 273.7 497.7 288 480 288H425.3C412.1 316.3 384.8 336 352 336C319.2 336 291 316.3 278.7 288H32C14.33 288 0 273.7 0 256C0 238.3 14.33 224 32 224H278.7C291 195.7 319.2 176 352 176zM384 256C384 238.3 369.7 224 352 224C334.3 224 320 238.3 320 256C320 273.7 334.3 288 352 288C369.7 288 384 273.7 384 256zM480 64C497.7 64 512 78.33 512 96C512 113.7 497.7 128 480 128H265.3C252.1 156.3 224.8 176 192 176C159.2 176 131 156.3 118.7 128H32C14.33 128 0 113.7 0 96C0 78.33 14.33 64 32 64H118.7C131 35.75 159.2 16 192 16C224.8 16 252.1 35.75 265.3 64H480zM160 96C160 113.7 174.3 128 192 128C209.7 128 224 113.7 224 96C224 78.33 209.7 64 192 64C174.3 64 160 78.33 160 96z'></path>
</svg>
)
export const AdminSVG = (props: React.SVGProps<SVGSVGElement>) => (
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 -32 576 576'
width='1em'
height='1em'
fill='currentColor'
{...props}
>
<path d='M560 448H512V113.5c0-27.25-21.5-49.5-48-49.5L352 64.01V128h96V512h112c8.875 0 16-7.125 16-15.1v-31.1C576 455.1 568.9 448 560 448zM280.3 1.007l-192 49.75C73.1 54.51 64 67.76 64 82.88V448H16c-8.875 0-16 7.125-16 15.1v31.1C0 504.9 7.125 512 16 512H320V33.13C320 11.63 300.5-4.243 280.3 1.007zM232 288c-13.25 0-24-14.37-24-31.1c0-17.62 10.75-31.1 24-31.1S256 238.4 256 256C256 273.6 245.3 288 232 288z'></path>
</svg>
)

View File

@ -0,0 +1,39 @@
import { forwardRef } from 'react'
interface SearchInputProps {
handleKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void
handleSearch: () => void
}
export const SearchInput = forwardRef<HTMLInputElement, SearchInputProps>(
({ handleKeyDown, handleSearch }, ref) => (
<div className='SearchMain'>
<div className='SearchMainInside'>
<div className='SearchMainInsideWrapper'>
<input
type='text'
className='SMIWInput'
ref={ref}
onKeyDown={handleKeyDown}
placeholder='Enter search term'
/>
<button
className='btn btnMain SMIWButton'
type='button'
onClick={handleSearch}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M500.3 443.7l-119.7-119.7c27.22-40.41 40.65-90.9 33.46-144.7C401.8 87.79 326.8 13.32 235.2 1.723C99.01-15.51-15.51 99.01 1.724 235.2c11.6 91.64 86.08 166.7 177.6 178.9c53.8 7.189 104.3-6.236 144.7-33.46l119.7 119.7c15.62 15.62 40.95 15.62 56.57 0C515.9 484.7 515.9 459.3 500.3 443.7zM79.1 208c0-70.58 57.42-128 128-128s128 57.42 128 128c0 70.58-57.42 128-128 128S79.1 278.6 79.1 208z'></path>
</svg>
</button>
</div>
</div>
</div>
)
)

View File

@ -0,0 +1,9 @@
import styles from '../styles/dotsSpinner.module.scss'
export const Spinner = () => (
<div className='spinner'>
<div className='spinnerCircle'></div>
</div>
)
export const Dots = () => <span className={styles.loading}></span>

26
src/components/Tabs.tsx Normal file
View File

@ -0,0 +1,26 @@
interface TabsProps {
tabs: string[]
tab: number
setTab: React.Dispatch<React.SetStateAction<number>>
}
export const Tabs = ({ tabs, tab, setTab }: TabsProps) => {
return (
<div className='IBMSMSplitMainFullSideSec IBMSMSMFSSNav'>
{tabs.map((t, i) => {
return (
<button
key={t}
className={`btn btnMain IBMSMSMFSSNavBtn${
tab === i ? ' IBMSMSMFSSNavBtnActive' : ''
}`}
type='button'
onClick={() => setTab(i)}
>
{t}
</button>
)
})}
</div>
)
}

1020
src/components/Zap.tsx Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,157 @@
import { NDKKind } from '@nostr-dev-kit/ndk'
import { formatDate } from 'date-fns'
import { useDidMount, useNDKContext } from 'hooks'
import { useState } from 'react'
import { useParams, useLocation, Link } from 'react-router-dom'
import { getModPageRoute, getBlogPageRoute, getProfilePageRoute } from 'routes'
import { CommentEvent, UserProfile } from 'types'
import { hexToNpub } from 'utils'
import { Reactions } from './Reactions'
import { Zap } from './Zap'
import { nip19 } from 'nostr-tools'
import { CommentContent } from './CommentContent'
interface CommentProps {
comment: CommentEvent
}
export const Comment = ({ comment }: CommentProps) => {
const { naddr } = useParams()
const location = useLocation()
const { ndk } = useNDKContext()
const isMod = location.pathname.includes('/mod/')
const isBlog = location.pathname.includes('/blog/')
const baseUrl = naddr
? isMod
? getModPageRoute(naddr)
: isBlog
? getBlogPageRoute(naddr)
: undefined
: undefined
const [commentEvents, setCommentEvents] = useState<CommentEvent[]>([])
const [profile, setProfile] = useState<UserProfile>()
useDidMount(() => {
comment.event.author.fetchProfile().then((res) => setProfile(res))
ndk
.fetchEvents({
kinds: [NDKKind.Text, NDKKind.GenericReply],
'#e': [comment.event.id]
})
.then((ndkEventsSet) => {
setCommentEvents(
Array.from(ndkEventsSet).map((ndkEvent) => ({
event: ndkEvent
}))
)
})
})
const profileRoute = getProfilePageRoute(
nip19.nprofileEncode({
pubkey: comment.event.pubkey
})
)
return (
<div className='IBMSMSMBSSCL_Comment'>
<div className='IBMSMSMBSSCL_CommentTop'>
<div className='IBMSMSMBSSCL_CommentTopPPWrapper'>
<Link
className='IBMSMSMBSSCL_CommentTopPP'
to={profileRoute}
style={{
background: `url('${
profile?.image || ''
}') center / cover no-repeat`
}}
/>
</div>
<div className='IBMSMSMBSSCL_CommentTopDetailsWrapper'>
<div className='IBMSMSMBSSCL_CommentTopDetails'>
<Link className='IBMSMSMBSSCL_CTD_Name' to={profileRoute}>
{profile?.displayName || profile?.name || ''}{' '}
</Link>
<Link className='IBMSMSMBSSCL_CTD_Address' to={profileRoute}>
{hexToNpub(comment.event.pubkey)}
</Link>
</div>
{comment.event.created_at && (
<div className='IBMSMSMBSSCL_CommentActionsDetails'>
<a className='IBMSMSMBSSCL_CADTime'>
{formatDate(comment.event.created_at * 1000, 'hh:mm aa')}{' '}
</a>
<a className='IBMSMSMBSSCL_CADDate'>
{formatDate(comment.event.created_at * 1000, 'dd/MM/yyyy')}
</a>
</div>
)}
</div>
</div>
<div className='IBMSMSMBSSCL_CommentBottom'>
{comment.status && (
<p className='IBMSMSMBSSCL_CBTextStatus'>
<span className='IBMSMSMBSSCL_CBTextStatusSpan'>Status:</span>
{comment.status}
</p>
)}
<CommentContent content={comment.event.content} />
</div>
<div className='IBMSMSMBSSCL_CommentActions'>
<div className='IBMSMSMBSSCL_CommentActionsInside'>
<Reactions {...comment.event.rawEvent()} />
{/* <div
className='IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAERepost'
style={{ cursor: 'not-allowed' }}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 -64 640 640'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSCL_CAElementIcon'
>
<path d='M614.2 334.8C610.5 325.8 601.7 319.1 592 319.1H544V176C544 131.9 508.1 96 464 96h-128c-17.67 0-32 14.31-32 32s14.33 32 32 32h128C472.8 160 480 167.2 480 176v143.1h-48c-9.703 0-18.45 5.844-22.17 14.82s-1.656 19.29 5.203 26.16l80 80.02C499.7 445.7 505.9 448 512 448s12.28-2.344 16.97-7.031l80-80.02C615.8 354.1 617.9 343.8 614.2 334.8zM304 352h-128C167.2 352 160 344.8 160 336V192h48c9.703 0 18.45-5.844 22.17-14.82s1.656-19.29-5.203-26.16l-80-80.02C140.3 66.34 134.1 64 128 64S115.7 66.34 111 71.03l-80 80.02C24.17 157.9 22.11 168.2 25.83 177.2S38.3 192 48 192H96V336C96 380.1 131.9 416 176 416h128c17.67 0 32-14.31 32-32S321.7 352 304 352z'></path>
</svg>
<p className='IBMSMSMBSSCL_CAElementText'>0</p>
<div className='IBMSMSMBSSCL_CAElementLoadWrapper'>
<div className='IBMSMSMBSSCL_CAElementLoad'></div>
</div>
</div> */}
{typeof profile?.lud16 !== 'undefined' && profile.lud16 !== '' && (
<Zap {...comment.event.rawEvent()} />
)}
{comment.event.kind === NDKKind.GenericReply && (
<>
<Link
className='IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAEReplies'
to={baseUrl + comment.event.encode()}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='IBMSMSMBSSCL_CAElementIcon'
>
<path d='M256 32C114.6 32 .0272 125.1 .0272 240c0 49.63 21.35 94.98 56.97 130.7c-12.5 50.37-54.27 95.27-54.77 95.77c-2.25 2.25-2.875 5.734-1.5 8.734C1.979 478.2 4.75 480 8 480c66.25 0 115.1-31.76 140.6-51.39C181.2 440.9 217.6 448 256 448c141.4 0 255.1-93.13 255.1-208S397.4 32 256 32z'></path>
</svg>
<p className='IBMSMSMBSSCL_CAElementText'>
{commentEvents.length}
</p>
<p className='IBMSMSMBSSCL_CAElementText'>Replies</p>
</Link>
<Link
className='IBMSMSMBSSCL_CAElement IBMSMSMBSSCL_CAEReply'
to={baseUrl + comment.event.encode()}
>
<p className='IBMSMSMBSSCL_CAElementText'>Reply</p>
</Link>
</>
)}
</div>
</div>
</div>
)
}

View File

@ -0,0 +1,18 @@
import { useTextLimit } from 'hooks/useTextLimit'
interface CommentContentProps {
content: string
}
export const CommentContent = ({ content }: CommentContentProps) => {
const { text, isTextOverflowing, isExpanded, toggle } = useTextLimit(content)
return (
<>
<p className='IBMSMSMBSSCL_CBText'>{text}</p>
{isTextOverflowing && (
<div className='IBMSMSMBSSCL_CBExpand' onClick={toggle}>
<p>{isExpanded ? 'Hide' : 'View'} full post</p>
</div>
)}
</>
)
}

Some files were not shown because too many files have changed in this diff Show More