Compare commits

...

421 Commits

Author SHA1 Message Date
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
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 486f3b79a5
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
daniyal
d52fec5685 feat: display list of mods 2024-08-06 15:46:38 +05:00
daniyal
0c266dde6a feat: extract mods from relays and display in mods page 2024-07-29 11:26:26 +05:00
daniyal
252eab504f chore: quick fix 2024-07-25 21:14:58 +05:00
daniyal
0aa8b9a38f feat: add loading spinner while fetching mod details 2024-07-25 21:12:12 +05:00
daniyal
70db95778c chore: refactor code 2024-07-25 20:45:31 +05:00
daniyal
cd0a53a8c8 feat: implemented mod details page 2024-07-25 20:05:28 +05:00
s
ca4164e507 Merge pull request 'feat: submit mod' (#2) from mod-submit into master
Reviewed-on: #2
2024-07-24 10:17:11 +00:00
daniyal
2a503d6e4a feat: completed basic implementation for mod submission 2024-07-24 15:13:28 +05:00
daniyal
deaf0ddac9 chore: add state to modForm 2024-07-21 21:30:03 +05:00
s
fd93a6cf5d Merge pull request 'feat: implement nostr login' (#1) from nostr-login into master
Reviewed-on: #1
Reviewed-by: Otto <y@noreply.git.nostrdev.com>
2024-07-16 11:19:47 +00:00
163 changed files with 241955 additions and 2244 deletions

14
.env.example Normal file
View File

@ -0,0 +1,14 @@
# 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>
# A dedicated npub used for reporting mods, blogs, profile and etc.
VITE_REPORTING_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

View File

@ -0,0 +1,40 @@
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_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .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,40 @@
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_FALLBACK_MOD_IMAGE=${{ vars.VITE_FALLBACK_MOD_IMAGE }}" >> .env
echo "VITE_FALLBACK_GAME_IMAGE=${{ vars.VITE_FALLBACK_GAME_IMAGE }}" >> .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,84 @@
# 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_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
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

2
.gitignore vendored
View File

@ -22,3 +22,5 @@ dist-ssr
*.njsproj
*.sln
*.sw?
.env

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,54 +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"
/>
<link rel="stylesheet" href="assets/bootstrap/css/bootstrap.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="assets/img/Logo%20with%20circle.png"
/>
<link
rel="icon"
type="image/png"
sizes="935x934"
href="assets/img/Logo%20with%20circle.png"
/>
<!-- Links and Stylesheets -->
<link rel="stylesheet" href="/assets/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="/assets/fonts/fontawesome-all.min.css" />
<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>

1344
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,18 +10,50 @@
"preview": "vite preview"
},
"dependencies": {
"@nostr-dev-kit/ndk": "2.8.2",
"@getalby/lightning-tools": "5.0.3",
"@nostr-dev-kit/ndk": "2.10.0",
"@nostr-dev-kit/ndk-cache-dexie": "2.5.1",
"@reduxjs/toolkit": "2.2.6",
"@tiptap/core": "2.6.6",
"@tiptap/extension-link": "2.6.6",
"@tiptap/react": "2.6.6",
"@tiptap/starter-kit": "2.6.6",
"@types/react-helmet": "^6.1.11",
"axios": "1.7.3",
"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",
"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-helmet": "^6.1.0",
"react-redux": "9.1.2",
"react-router-dom": "^6.24.1"
"react-router-dom": "^6.24.1",
"react-toastify": "10.0.5",
"react-window": "1.8.10",
"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/uuid": "10.0.0",
"@typescript-eslint/eslint-plugin": "^7.13.1",
"@typescript-eslint/parser": "^7.13.1",
"@vitejs/plugin-react": "^4.3.1",
@ -30,6 +62,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>

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 692 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,803 @@
<?xml version="1.0" standalone="no"?>
<!--
Font Awesome Free 5.12.0 by @fontawesome - https://fontawesome.com
License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
-->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<metadata>
Created by FontForge 20190801 at Tue Dec 10 16:09:21 2019
By Robert Madole
Copyright (c) Font Awesome
</metadata>
<defs>
<font id="FontAwesome5Free-Regular" horiz-adv-x="512" >
<font-face
font-family="Font Awesome 5 Free Regular"
font-weight="400"
font-stretch="normal"
units-per-em="512"
panose-1="2 0 5 3 0 0 0 0 0 0"
ascent="448"
descent="-64"
bbox="-0.0663408 -64.0662 640.01 448.1"
underline-thickness="25"
underline-position="-50"
unicode-range="U+0020-F5C8"
/>
<missing-glyph />
<glyph glyph-name="heart" unicode="&#xf004;"
d="M458.4 383.7c75.2998 -63.4004 64.0996 -166.601 10.5996 -221.3l-175.4 -178.7c-10 -10.2002 -23.2998 -15.7998 -37.5996 -15.7998c-14.2002 0 -27.5996 5.69922 -37.5996 15.8994l-175.4 178.7c-53.5996 54.7002 -64.5996 157.9 10.5996 221.2
c57.8008 48.7002 147.101 41.2998 202.4 -15c55.2998 56.2998 144.6 63.5996 202.4 15zM434.8 196.2c36.2002 36.8994 43.7998 107.7 -7.2998 150.8c-38.7002 32.5996 -98.7002 27.9004 -136.5 -10.5996l-35 -35.7002l-35 35.7002
c-37.5996 38.2998 -97.5996 43.1992 -136.5 10.5c-51.2002 -43.1006 -43.7998 -113.5 -7.2998 -150.7l175.399 -178.7c2.40039 -2.40039 4.40039 -2.40039 6.80078 0z" />
<glyph glyph-name="star" unicode="&#xf005;" horiz-adv-x="576"
d="M528.1 276.5c26.2002 -3.7998 36.7002 -36.0996 17.7002 -54.5996l-105.7 -103l25 -145.5c4.5 -26.3008 -23.1992 -45.9004 -46.3994 -33.7002l-130.7 68.7002l-130.7 -68.7002c-23.2002 -12.2998 -50.8994 7.39941 -46.3994 33.7002l25 145.5l-105.7 103
c-19 18.5 -8.5 50.7998 17.7002 54.5996l146.1 21.2998l65.2998 132.4c11.7998 23.8994 45.7002 23.5996 57.4004 0l65.2998 -132.4zM388.6 135.7l100.601 98l-139 20.2002l-62.2002 126l-62.2002 -126l-139 -20.2002l100.601 -98l-23.7002 -138.4l124.3 65.2998
l124.3 -65.2998z" />
<glyph glyph-name="user" unicode="&#xf007;" horiz-adv-x="448"
d="M313.6 144c74.2002 0 134.4 -60.2002 134.4 -134.4v-25.5996c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v25.5996c0 74.2002 60.2002 134.4 134.4 134.4c28.7998 0 42.5 -16 89.5996 -16s60.9004 16 89.5996 16zM400 -16v25.5996
c0 47.6006 -38.7998 86.4004 -86.4004 86.4004c-14.6992 0 -37.8994 -16 -89.5996 -16c-51.2998 0 -75 16 -89.5996 16c-47.6006 0 -86.4004 -38.7998 -86.4004 -86.4004v-25.5996h352zM224 160c-79.5 0 -144 64.5 -144 144s64.5 144 144 144s144 -64.5 144 -144
s-64.5 -144 -144 -144zM224 400c-52.9004 0 -96 -43.0996 -96 -96s43.0996 -96 96 -96s96 43.0996 96 96s-43.0996 96 -96 96z" />
<glyph glyph-name="clock" unicode="&#xf017;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200zM317.8 96.4004l-84.8994 61.6992
c-3.10059 2.30078 -4.90039 5.90039 -4.90039 9.7002v164.2c0 6.59961 5.40039 12 12 12h32c6.59961 0 12 -5.40039 12 -12v-141.7l66.7998 -48.5996c5.40039 -3.90039 6.5 -11.4004 2.60059 -16.7998l-18.8008 -25.9004c-3.89941 -5.2998 -11.3994 -6.5 -16.7998 -2.59961z
" />
<glyph glyph-name="list-alt" unicode="&#xf022;"
d="M464 416c26.5098 0 48 -21.4902 48 -48v-352c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h416zM458 16c3.31152 0 6 2.68848 6 6v340c0 3.31152 -2.68848 6 -6 6h-404c-3.31152 0 -6 -2.68848 -6 -6v-340
c0 -3.31152 2.68848 -6 6 -6h404zM416 108v-24c0 -6.62695 -5.37305 -12 -12 -12h-200c-6.62695 0 -12 5.37305 -12 12v24c0 6.62695 5.37305 12 12 12h200c6.62695 0 12 -5.37305 12 -12zM416 204v-24c0 -6.62695 -5.37305 -12 -12 -12h-200c-6.62695 0 -12 5.37305 -12 12
v24c0 6.62695 5.37305 12 12 12h200c6.62695 0 12 -5.37305 12 -12zM416 300v-24c0 -6.62695 -5.37305 -12 -12 -12h-200c-6.62695 0 -12 5.37305 -12 12v24c0 6.62695 5.37305 12 12 12h200c6.62695 0 12 -5.37305 12 -12zM164 288c0 -19.8818 -16.1182 -36 -36 -36
s-36 16.1182 -36 36s16.1182 36 36 36s36 -16.1182 36 -36zM164 192c0 -19.8818 -16.1182 -36 -36 -36s-36 16.1182 -36 36s16.1182 36 36 36s36 -16.1182 36 -36zM164 96c0 -19.8818 -16.1182 -36 -36 -36s-36 16.1182 -36 36s16.1182 36 36 36s36 -16.1182 36 -36z" />
<glyph glyph-name="flag" unicode="&#xf024;"
d="M336.174 368c35.4668 0 73.0195 12.6914 108.922 28.1797c31.6406 13.6514 66.9043 -9.65723 66.9043 -44.1162v-239.919c0 -16.1953 -8.1543 -31.3057 -21.7129 -40.1631c-26.5762 -17.3643 -70.0693 -39.9814 -128.548 -39.9814c-68.6084 0 -112.781 32 -161.913 32
c-56.5674 0 -89.957 -11.2803 -127.826 -28.5566v-83.4434c0 -8.83691 -7.16309 -16 -16 -16h-16c-8.83691 0 -16 7.16309 -16 16v406.438c-14.3428 8.2998 -24 23.7979 -24 41.5615c0 27.5693 23.2422 49.71 51.2012 47.8965
c22.9658 -1.49023 41.8662 -19.4717 44.4805 -42.3379c0.177734 -1.52441 0.321289 -4.00781 0.321289 -5.54199c0 -4.30176 -1.10352 -11.1035 -2.46289 -15.1846c22.418 8.68555 49.4199 15.168 80.7207 15.168c68.6084 0 112.781 -32 161.913 -32zM464 112v240
c-31.5059 -14.6338 -84.5547 -32 -127.826 -32c-59.9111 0 -101.968 32 -161.913 32c-41.4365 0 -80.4766 -16.5879 -102.261 -32v-232c31.4473 14.5967 84.4648 24 127.826 24c59.9111 0 101.968 -32 161.913 -32c41.4365 0 80.4775 16.5879 102.261 32z" />
<glyph glyph-name="bookmark" unicode="&#xf02e;" horiz-adv-x="384"
d="M336 448c26.5098 0 48 -21.4902 48 -48v-464l-192 112l-192 -112v464c0 26.5098 21.4902 48 48 48h288zM336 19.5703v374.434c0 3.31348 -2.68555 5.99609 -6 5.99609h-276c-3.31152 0 -6 -2.68848 -6 -6v-374.43l144 84z" />
<glyph glyph-name="image" unicode="&#xf03e;"
d="M464 384c26.5098 0 48 -21.4902 48 -48v-288c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v288c0 26.5098 21.4902 48 48 48h416zM458 48c3.31152 0 6 2.68848 6 6v276c0 3.31152 -2.68848 6 -6 6h-404c-3.31152 0 -6 -2.68848 -6 -6v-276
c0 -3.31152 2.68848 -6 6 -6h404zM128 296c22.0908 0 40 -17.9092 40 -40s-17.9092 -40 -40 -40s-40 17.9092 -40 40s17.9092 40 40 40zM96 96v48l39.5137 39.5146c4.6875 4.68652 12.2852 4.68652 16.9717 0l39.5146 -39.5146l119.514 119.515
c4.6875 4.68652 12.2852 4.68652 16.9717 0l87.5146 -87.5146v-80h-320z" />
<glyph glyph-name="edit" unicode="&#xf044;" horiz-adv-x="576"
d="M402.3 103.1l32 32c5 5 13.7002 1.5 13.7002 -5.69922v-145.4c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h273.5c7.09961 0 10.7002 -8.59961 5.7002 -13.7002l-32 -32c-1.5 -1.5 -3.5 -2.2998 -5.7002 -2.2998h-241.5v-352h352
v113.5c0 2.09961 0.799805 4.09961 2.2998 5.59961zM558.9 304.9l-262.601 -262.601l-90.3994 -10c-26.2002 -2.89941 -48.5 19.2002 -45.6006 45.6006l10 90.3994l262.601 262.601c22.8994 22.8994 59.8994 22.8994 82.6992 0l43.2002 -43.2002
c22.9004 -22.9004 22.9004 -60 0.100586 -82.7998zM460.1 274l-58.0996 58.0996l-185.8 -185.899l-7.2998 -65.2998l65.2998 7.2998zM524.9 353.7l-43.2002 43.2002c-4.10059 4.09961 -10.7998 4.09961 -14.7998 0l-30.9004 -30.9004l58.0996 -58.0996l30.9004 30.8994
c4 4.2002 4 10.7998 -0.0996094 14.9004z" />
<glyph glyph-name="times-circle" unicode="&#xf057;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200zM357.8 254.2l-62.2002 -62.2002l62.2002 -62.2002
c4.7002 -4.7002 4.7002 -12.2998 0 -17l-22.5996 -22.5996c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-62.2002 62.2002l-62.2002 -62.2002c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-22.5996 22.5996c-4.7002 4.7002 -4.7002 12.2998 0 17l62.2002 62.2002l-62.2002 62.2002
c-4.7002 4.7002 -4.7002 12.2998 0 17l22.5996 22.5996c4.7002 4.7002 12.2998 4.7002 17 0l62.2002 -62.2002l62.2002 62.2002c4.7002 4.7002 12.2998 4.7002 17 0l22.5996 -22.5996c4.7002 -4.7002 4.7002 -12.2998 0 -17z" />
<glyph glyph-name="check-circle" unicode="&#xf058;"
d="M256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248zM256 392c-110.549 0 -200 -89.4678 -200 -200c0 -110.549 89.4678 -200 200 -200c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200z
M396.204 261.733c4.66699 -4.70508 4.63672 -12.3037 -0.0673828 -16.9717l-172.589 -171.204c-4.70508 -4.66797 -12.3027 -4.63672 -16.9697 0.0683594l-90.7812 91.5156c-4.66797 4.70605 -4.63672 12.3047 0.0683594 16.9717l22.7188 22.5361
c4.70508 4.66699 12.3027 4.63574 16.9697 -0.0693359l59.792 -60.2773l141.353 140.216c4.70508 4.66797 12.3027 4.6377 16.9697 -0.0673828z" />
<glyph glyph-name="question-circle" unicode="&#xf059;"
d="M256 440c136.957 0 248 -111.083 248 -248c0 -136.997 -111.043 -248 -248 -248s-248 111.003 -248 248c0 136.917 111.043 248 248 248zM256 -8c110.569 0 200 89.4697 200 200c0 110.529 -89.5088 200 -200 200c-110.528 0 -200 -89.5049 -200 -200
c0 -110.569 89.4678 -200 200 -200zM363.244 247.2c0 -67.0518 -72.4209 -68.084 -72.4209 -92.8633v-6.33691c0 -6.62695 -5.37305 -12 -12 -12h-45.6475c-6.62695 0 -12 5.37305 -12 12v8.65918c0 35.7451 27.1006 50.0342 47.5791 61.5156
c17.5615 9.84473 28.3242 16.541 28.3242 29.5791c0 17.2461 -21.999 28.6934 -39.7842 28.6934c-23.1885 0 -33.8936 -10.9775 -48.9424 -29.9697c-4.05664 -5.11914 -11.46 -6.07031 -16.666 -2.12402l-27.8232 21.0986
c-5.10742 3.87207 -6.25098 11.0654 -2.64453 16.3633c23.627 34.6934 53.7217 54.1846 100.575 54.1846c49.0713 0 101.45 -38.3037 101.45 -88.7998zM298 80c0 -23.1592 -18.8408 -42 -42 -42s-42 18.8408 -42 42s18.8408 42 42 42s42 -18.8408 42 -42z" />
<glyph glyph-name="eye" unicode="&#xf06e;" horiz-adv-x="576"
d="M288 304c0.0927734 0 0.244141 0.000976562 0.336914 0.000976562c61.6641 0 111.71 -50.0469 111.71 -111.711c0 -61.6631 -50.0459 -111.71 -111.71 -111.71s-111.71 50.0469 -111.71 111.71c0 8.71289 1.95898 22.5781 4.37305 30.9502
c6.93066 -3.94141 19.0273 -7.18457 27 -7.24023c30.9121 0 56 25.0879 56 56c-0.0556641 7.97266 -3.29883 20.0693 -7.24023 27c8.42383 2.62207 22.4189 4.8623 31.2402 5zM572.52 206.6c1.9209 -3.79883 3.47949 -10.3379 3.47949 -14.5947
s-1.55859 -10.7959 -3.47949 -14.5947c-54.1992 -105.771 -161.59 -177.41 -284.52 -177.41s-230.29 71.5898 -284.52 177.4c-1.9209 3.79883 -3.47949 10.3379 -3.47949 14.5947s1.55859 10.7959 3.47949 14.5947c54.1992 105.771 161.59 177.41 284.52 177.41
s230.29 -71.5898 284.52 -177.4zM288 48c98.6602 0 189.1 55 237.93 144c-48.8398 89 -139.27 144 -237.93 144s-189.09 -55 -237.93 -144c48.8398 -89 139.279 -144 237.93 -144z" />
<glyph glyph-name="eye-slash" unicode="&#xf070;" horiz-adv-x="640"
d="M634 -23c3.31738 -2.65137 6.00977 -8.25098 6.00977 -12.498c0 -3.10449 -1.57715 -7.58984 -3.51953 -10.0117l-10 -12.4902c-2.65234 -3.31152 -8.24707 -6 -12.4902 -6c-3.09961 0 -7.58008 1.57227 -10 3.50977l-598 467.49
c-3.31738 2.65137 -6.00977 8.25098 -6.00977 12.498c0 3.10449 1.57715 7.58984 3.51953 10.0117l10 12.4902c2.65234 3.31152 8.24707 6 12.4902 6c3.09961 0 7.58008 -1.57227 10 -3.50977zM296.79 301.53c6.33496 1.35059 16.7324 2.45801 23.21 2.46973
c60.4805 0 109.36 -47.9102 111.58 -107.85zM343.21 82.46c-6.33496 -1.34375 -16.7334 -2.44629 -23.21 -2.45996c-60.4697 0 -109.35 47.9102 -111.58 107.84zM320 336c-19.8799 0 -39.2803 -2.7998 -58.2197 -7.09961l-46.4102 36.29
c32.9199 11.8096 67.9297 18.8096 104.63 18.8096c122.93 0 230.29 -71.5898 284.57 -177.4c1.91992 -3.79883 3.47949 -10.3379 3.47949 -14.5947s-1.55957 -10.7959 -3.47949 -14.5947c-11.7197 -22.7598 -35.4189 -56.4092 -52.9004 -75.1104l-37.7402 29.5
c14.333 15.0156 34.0449 41.9854 44 60.2002c-48.8398 89 -139.279 144 -237.93 144zM320 48c19.8896 0 39.2803 2.7998 58.2197 7.08984l46.4102 -36.2803c-32.9199 -11.7598 -67.9297 -18.8096 -104.63 -18.8096c-122.92 0 -230.28 71.5898 -284.51 177.4
c-1.9209 3.79883 -3.47949 10.3379 -3.47949 14.5947s1.55859 10.7959 3.47949 14.5947c11.7168 22.7568 35.4111 56.4014 52.8896 75.1006l37.7402 -29.5c-14.3467 -15.0107 -34.0811 -41.9756 -44.0498 -60.1904c48.8496 -89 139.279 -144 237.93 -144z" />
<glyph glyph-name="calendar-alt" unicode="&#xf073;" horiz-adv-x="448"
d="M148 160h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12zM256 172c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40
c6.59961 0 12 -5.40039 12 -12v-40zM352 172c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM256 76c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40
c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM160 76c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM352 76c0 -6.59961 -5.40039 -12 -12 -12h-40
c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM448 336v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40
c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h48c26.5 0 48 -21.5 48 -48zM400 -10v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="comment" unicode="&#xf075;"
d="M256 416c141.4 0 256 -93.0996 256 -208s-114.6 -208 -256 -208c-32.7998 0 -64 5.2002 -92.9004 14.2998c-29.0996 -20.5996 -77.5996 -46.2998 -139.1 -46.2998c-9.59961 0 -18.2998 5.7002 -22.0996 14.5c-3.80078 8.7998 -2 19 4.59961 26
c0.5 0.400391 31.5 33.7998 46.4004 73.2002c-33 35.0996 -52.9004 78.7002 -52.9004 126.3c0 114.9 114.6 208 256 208zM256 48c114.7 0 208 71.7998 208 160s-93.2998 160 -208 160s-208 -71.7998 -208 -160c0 -42.2002 21.7002 -74.0996 39.7998 -93.4004
l20.6006 -21.7998l-10.6006 -28.0996c-5.5 -14.5 -12.5996 -28.1006 -19.8994 -40.2002c23.5996 7.59961 43.1992 18.9004 57.5 29l19.5 13.7998l22.6992 -7.2002c25.3008 -8 51.7002 -12.0996 78.4004 -12.0996z" />
<glyph glyph-name="folder" unicode="&#xf07b;"
d="M464 320c26.5098 0 48 -21.4902 48 -48v-224c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v288c0 26.5098 21.4902 48 48 48h146.74c8.49023 0 16.6299 -3.37012 22.6299 -9.37012l54.6299 -54.6299h192zM464 48v224h-198.62
c-8.49023 0 -16.6299 3.37012 -22.6299 9.37012l-54.6299 54.6299h-140.12v-288h416z" />
<glyph glyph-name="folder-open" unicode="&#xf07c;" horiz-adv-x="576"
d="M527.9 224c37.6992 0 60.6992 -41.5 40.6992 -73.4004l-79.8994 -128c-8.7998 -14.0996 -24.2002 -22.5996 -40.7002 -22.5996h-400c-26.5 0 -48 21.5 -48 48v288c0 26.5 21.5 48 48 48h160l64 -64h160c26.5 0 48 -21.5 48 -48v-48h47.9004zM48 330v-233.4l62.9004 104.2
c8.69922 14.4004 24.2998 23.2002 41.0996 23.2002h280v42c0 3.2998 -2.7002 6 -6 6h-173.9l-64 64h-134.1c-3.2998 0 -6 -2.7002 -6 -6zM448 48l80 128h-378.8l-77.2002 -128h376z" />
<glyph glyph-name="chart-bar" unicode="&#xf080;"
d="M396.8 96c-6.39941 0 -12.7998 6.40039 -12.7998 12.7998v230.4c0 6.39941 6.40039 12.7998 12.7998 12.7998h22.4004c6.39941 0 12.7998 -6.40039 12.7998 -12.7998v-230.4c0 -6.39941 -6.40039 -12.7998 -12.7998 -12.7998h-22.4004zM204.8 96
c-6.39941 0 -12.7998 6.40039 -12.7998 12.7998v198.4c0 6.39941 6.40039 12.7998 12.7998 12.7998h22.4004c6.39941 0 12.7998 -6.40039 12.7998 -12.7998v-198.4c0 -6.39941 -6.40039 -12.7998 -12.7998 -12.7998h-22.4004zM300.8 96
c-6.39941 0 -12.7998 6.40039 -12.7998 12.7998v134.4c0 6.39941 6.40039 12.7998 12.7998 12.7998h22.4004c6.39941 0 12.7998 -6.40039 12.7998 -12.7998v-134.4c0 -6.39941 -6.40039 -12.7998 -12.7998 -12.7998h-22.4004zM496 48c8.83984 0 16 -7.16016 16 -16v-16
c0 -8.83984 -7.16016 -16 -16 -16h-464c-17.6699 0 -32 14.3301 -32 32v336c0 8.83984 7.16016 16 16 16h16c8.83984 0 16 -7.16016 16 -16v-320h448zM108.8 96c-6.39941 0 -12.7998 6.40039 -12.7998 12.7998v70.4004c0 6.39941 6.40039 12.7998 12.7998 12.7998h22.4004
c6.39941 0 12.7998 -6.40039 12.7998 -12.7998v-70.4004c0 -6.39941 -6.40039 -12.7998 -12.7998 -12.7998h-22.4004z" />
<glyph glyph-name="comments" unicode="&#xf086;" horiz-adv-x="576"
d="M532 61.7998c15.2998 -30.7002 37.4004 -54.5 37.7998 -54.7998c6.2998 -6.7002 8 -16.5 4.40039 -25c-3.7002 -8.5 -12 -14 -21.2002 -14c-53.5996 0 -96.7002 20.2998 -125.2 38.7998c-19 -4.39941 -39 -6.7998 -59.7998 -6.7998
c-86.2002 0 -159.9 40.4004 -191.3 97.7998c-9.7002 1.2002 -19.2002 2.7998 -28.4004 4.90039c-28.5 -18.6006 -71.7002 -38.7998 -125.2 -38.7998c-9.19922 0 -17.5996 5.5 -21.1992 14c-3.7002 8.5 -1.90039 18.2998 4.39941 25
c0.400391 0.399414 22.4004 24.1992 37.7002 54.8994c-27.5 27.2002 -44 61.2002 -44 98.2002c0 88.4004 93.0996 160 208 160c86.2998 0 160.3 -40.5 191.8 -98.0996c99.7002 -11.8008 176.2 -77.9004 176.2 -157.9c0 -37.0996 -16.5 -71.0996 -44 -98.2002zM139.2 154.1
l19.7998 -4.5c16 -3.69922 32.5 -5.59961 49 -5.59961c86.7002 0 160 51.2998 160 112s-73.2998 112 -160 112s-160 -51.2998 -160 -112c0 -28.7002 16.2002 -50.5996 29.7002 -64l24.7998 -24.5l-15.5 -31.0996c-2.59961 -5.10059 -5.2998 -10.1006 -8 -14.8008
c14.5996 5.10059 29 12.3008 43.0996 21.4004zM498.3 96c13.5 13.4004 29.7002 35.2998 29.7002 64c0 49.2002 -48.2998 91.5 -112.7 106c0.299805 -3.2998 0.700195 -6.59961 0.700195 -10c0 -80.9004 -78 -147.5 -179.3 -158.3
c29.0996 -29.6006 77.2998 -49.7002 131.3 -49.7002c16.5 0 33 1.90039 49 5.59961l19.9004 4.60059l17.0996 -11.1006c14.0996 -9.09961 28.5 -16.2998 43.0996 -21.3994c-2.69922 4.7002 -5.39941 9.7002 -8 14.7998l-15.5 31.0996z" />
<glyph glyph-name="star-half" unicode="&#xf089;" horiz-adv-x="576"
d="M288 62.7002v-54.2998l-130.7 -68.6006c-23.3994 -12.2998 -50.8994 7.60059 -46.3994 33.7002l25 145.5l-105.7 103c-19 18.5 -8.5 50.7998 17.7002 54.5996l146.1 21.2002l65.2998 132.4c5.90039 11.8994 17.2998 17.7998 28.7002 17.7998v-68.0996l-62.2002 -126
l-139 -20.2002l100.601 -98l-23.7002 -138.4z" />
<glyph glyph-name="lemon" unicode="&#xf094;"
d="M484.112 420.111c28.1221 -28.123 35.9434 -68.0039 19.0215 -97.0547c-23.0576 -39.584 50.1436 -163.384 -82.3311 -295.86c-132.301 -132.298 -256.435 -59.3594 -295.857 -82.3291c-29.0459 -16.917 -68.9219 -9.11426 -97.0576 19.0205
c-28.1221 28.1221 -35.9434 68.0029 -19.0215 97.0547c23.0566 39.5859 -50.1436 163.386 82.3301 295.86c132.308 132.309 256.407 59.3496 295.862 82.332c29.0498 16.9219 68.9307 9.09863 97.0537 -19.0234zM461.707 347.217
c13.5166 23.2031 -27.7578 63.7314 -50.4883 50.4912c-66.6025 -38.7939 -165.646 45.5898 -286.081 -74.8457c-120.444 -120.445 -36.0449 -219.472 -74.8447 -286.08c-13.542 -23.2471 27.8145 -63.6953 50.4932 -50.4883
c66.6006 38.7949 165.636 -45.5996 286.076 74.8428c120.444 120.445 36.0449 219.472 74.8447 286.08zM291.846 338.481c1.37012 -10.96 -6.40332 -20.957 -17.3643 -22.3271c-54.8467 -6.85547 -135.779 -87.7871 -142.636 -142.636
c-1.37305 -10.9883 -11.3984 -18.7334 -22.3262 -17.3643c-10.9609 1.37012 -18.7344 11.3652 -17.3643 22.3262c9.16211 73.2852 104.167 168.215 177.364 177.364c10.9531 1.36816 20.9561 -6.40234 22.3262 -17.3633z" />
<glyph glyph-name="credit-card" unicode="&#xf09d;" horiz-adv-x="576"
d="M527.9 416c26.5996 0 48.0996 -21.5 48.0996 -48v-352c0 -26.5 -21.5 -48 -48.0996 -48h-479.801c-26.5996 0 -48.0996 21.5 -48.0996 48v352c0 26.5 21.5 48 48.0996 48h479.801zM54.0996 368c-3.2998 0 -6 -2.7002 -6 -6v-42h479.801v42c0 3.2998 -2.7002 6 -6 6
h-467.801zM521.9 16c3.2998 0 6 2.7002 6 6v170h-479.801v-170c0 -3.2998 2.7002 -6 6 -6h467.801zM192 116v-40c0 -6.59961 -5.40039 -12 -12 -12h-72c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h72c6.59961 0 12 -5.40039 12 -12zM384 116v-40
c0 -6.59961 -5.40039 -12 -12 -12h-136c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h136c6.59961 0 12 -5.40039 12 -12z" />
<glyph glyph-name="hdd" unicode="&#xf0a0;" horiz-adv-x="576"
d="M567.403 212.358c5.59668 -8.04688 8.59668 -17.6113 8.59668 -27.4121v-136.946c0 -26.5098 -21.4902 -48 -48 -48h-480c-26.5098 0 -48 21.4902 -48 48v136.946c0 8.30957 3.85156 20.5898 8.59668 27.4121l105.08 151.053
c7.90625 11.3652 25.5596 20.5889 39.4033 20.5889h0.000976562h269.838h0.000976562c13.8438 0 31.4971 -9.22363 39.4033 -20.5889zM153.081 336l-77.9131 -112h425.664l-77.9131 112h-269.838zM528 48v128h-480v-128h480zM496 112c0 -17.6729 -14.3271 -32 -32 -32
s-32 14.3271 -32 32s14.3271 32 32 32s32 -14.3271 32 -32zM400 112c0 -17.6729 -14.3271 -32 -32 -32s-32 14.3271 -32 32s14.3271 32 32 32s32 -14.3271 32 -32z" />
<glyph glyph-name="hand-point-right" unicode="&#xf0a4;"
d="M428.8 310.4c45.0996 0 83.2002 -38.1016 83.2002 -83.2002c0 -45.6162 -37.7646 -83.2002 -83.2002 -83.2002h-35.6475c-1.41602 -6.36719 -4.96875 -16.252 -7.92969 -22.0645c2.50586 -22.0059 -3.50293 -44.9775 -15.9844 -62.791
c-1.14062 -52.4863 -37.3984 -91.1445 -99.9404 -91.1445h-21.2988c-60.0635 0 -98.5117 40 -127.2 40h-2.67871c-5.74707 -4.95215 -13.5361 -8 -22.1201 -8h-64c-17.6729 0 -32 12.8936 -32 28.7998v230.4c0 15.9062 14.3271 28.7998 32 28.7998h64.001
c8.58398 0 16.373 -3.04785 22.1201 -8h2.67871c6.96387 0 14.8623 6.19336 30.1816 23.6689l0.128906 0.148438l0.130859 0.145508c8.85645 9.93652 18.1162 20.8398 25.8506 33.2529c18.7051 30.2471 30.3936 78.7842 75.707 78.7842c56.9277 0 92 -35.2861 92 -83.2002
v-0.0839844c0 -6.21777 -0.974609 -16.2148 -2.17578 -22.3154h86.1768zM428.8 192c18.9756 0 35.2002 16.2246 35.2002 35.2002c0 18.7002 -16.7754 35.2002 -35.2002 35.2002h-158.399c0 17.3242 26.3994 35.1992 26.3994 70.3994c0 26.4004 -20.625 35.2002 -44 35.2002
c-8.79395 0 -20.4443 -32.7119 -34.9258 -56.0996c-9.07422 -14.5752 -19.5244 -27.2256 -30.7988 -39.875c-16.1094 -18.374 -33.8359 -36.6328 -59.0752 -39.5967v-176.753c42.79 -3.7627 74.5088 -39.6758 120 -39.6758h21.2988
c40.5244 0 57.124 22.1973 50.6006 61.3252c14.6113 8.00098 24.1514 33.9785 12.9248 53.625c19.3652 18.2246 17.7871 46.3809 4.9502 61.0498h91.0254zM88 64c0 13.2549 -10.7451 24 -24 24s-24 -10.7451 -24 -24s10.7451 -24 24 -24s24 10.7451 24 24z" />
<glyph glyph-name="hand-point-left" unicode="&#xf0a5;"
d="M0 227.2c0 45.0986 38.1006 83.2002 83.2002 83.2002h86.1758c-1.3623 6.91016 -2.17578 14.374 -2.17578 22.3994c0 47.9141 35.0723 83.2002 92 83.2002c45.3135 0 57.002 -48.5371 75.7061 -78.7852c7.73438 -12.4121 16.9951 -23.3154 25.8506 -33.2529
l0.130859 -0.145508l0.128906 -0.148438c15.3213 -17.4746 23.2197 -23.668 30.1836 -23.668h2.67871c5.74707 4.95215 13.5361 8 22.1201 8h64c17.6729 0 32 -12.8936 32 -28.7998v-230.4c0 -15.9062 -14.3271 -28.7998 -32 -28.7998h-64
c-8.58398 0 -16.373 3.04785 -22.1201 8h-2.67871c-28.6885 0 -67.1367 -40 -127.2 -40h-21.2988c-62.542 0 -98.8008 38.6582 -99.9404 91.1445c-12.4814 17.8135 -18.4922 40.7852 -15.9844 62.791c-2.96094 5.8125 -6.51367 15.6973 -7.92969 22.0645h-35.6465
c-45.4355 0 -83.2002 37.584 -83.2002 83.2002zM48 227.2c0 -18.9756 16.2246 -35.2002 35.2002 -35.2002h91.0244c-12.8369 -14.6689 -14.415 -42.8252 4.9502 -61.0498c-11.2256 -19.6465 -1.68652 -45.624 12.9248 -53.625
c-6.52246 -39.1279 10.0771 -61.3252 50.6016 -61.3252h21.2988c45.4912 0 77.21 35.9131 120 39.6768v176.752c-25.2393 2.96289 -42.9658 21.2227 -59.0752 39.5967c-11.2744 12.6494 -21.7246 25.2998 -30.7988 39.875
c-14.4814 23.3877 -26.1318 56.0996 -34.9258 56.0996c-23.375 0 -44 -8.7998 -44 -35.2002c0 -35.2002 26.3994 -53.0752 26.3994 -70.3994h-158.399c-18.4248 0 -35.2002 -16.5 -35.2002 -35.2002zM448 88c-13.2549 0 -24 -10.7451 -24 -24s10.7451 -24 24 -24
s24 10.7451 24 24s-10.7451 24 -24 24z" />
<glyph glyph-name="hand-point-up" unicode="&#xf0a6;" horiz-adv-x="448"
d="M105.6 364.8c0 45.0996 38.1016 83.2002 83.2002 83.2002c45.6162 0 83.2002 -37.7646 83.2002 -83.2002v-35.6465c6.36719 -1.41602 16.252 -4.96875 22.0645 -7.92969c22.0059 2.50684 44.9775 -3.50293 62.791 -15.9844
c52.4863 -1.14062 91.1445 -37.3984 91.1445 -99.9404v-21.2988c0 -60.0635 -40 -98.5117 -40 -127.2v-2.67871c4.95215 -5.74707 8 -13.5361 8 -22.1201v-64c0 -17.6729 -12.8936 -32 -28.7998 -32h-230.4c-15.9062 0 -28.7998 14.3271 -28.7998 32v64
c0 8.58398 3.04785 16.373 8 22.1201v2.67871c0 6.96387 -6.19336 14.8623 -23.6689 30.1816l-0.148438 0.128906l-0.145508 0.130859c-9.93652 8.85645 -20.8398 18.1162 -33.2529 25.8506c-30.2471 18.7051 -78.7842 30.3936 -78.7842 75.707
c0 56.9277 35.2861 92 83.2002 92h0.0839844c6.21777 0 16.2148 -0.974609 22.3154 -2.17578v86.1768zM224 364.8c0 18.9756 -16.2246 35.2002 -35.2002 35.2002c-18.7002 0 -35.2002 -16.7754 -35.2002 -35.2002v-158.399c-17.3242 0 -35.1992 26.3994 -70.3994 26.3994
c-26.4004 0 -35.2002 -20.625 -35.2002 -44c0 -8.79395 32.7119 -20.4443 56.0996 -34.9258c14.5752 -9.07422 27.2256 -19.5244 39.875 -30.7988c18.374 -16.1094 36.6328 -33.8359 39.5967 -59.0752h176.753c3.7627 42.79 39.6758 74.5088 39.6758 120v21.2988
c0 40.5244 -22.1973 57.124 -61.3252 50.6006c-8.00098 14.6113 -33.9785 24.1514 -53.625 12.9248c-18.2246 19.3652 -46.3809 17.7871 -61.0498 4.9502v91.0254zM352 24c-13.2549 0 -24 -10.7451 -24 -24s10.7451 -24 24 -24s24 10.7451 24 24s-10.7451 24 -24 24z" />
<glyph glyph-name="hand-point-down" unicode="&#xf0a7;" horiz-adv-x="448"
d="M188.8 -64c-45.0986 0 -83.2002 38.1006 -83.2002 83.2002v86.1758c-6.91016 -1.3623 -14.374 -2.17578 -22.3994 -2.17578c-47.9141 0 -83.2002 35.0723 -83.2002 92c0 45.3135 48.5371 57.002 78.7852 75.707c12.4121 7.73438 23.3154 16.9951 33.2529 25.8506
l0.145508 0.130859l0.148438 0.128906c17.4746 15.3213 23.668 23.2197 23.668 30.1836v2.67871c-4.95215 5.74707 -8 13.5361 -8 22.1201v64c0 17.6729 12.8936 32 28.7998 32h230.4c15.9062 0 28.7998 -14.3271 28.7998 -32v-64.001
c0 -8.58398 -3.04785 -16.373 -8 -22.1201v-2.67871c0 -28.6885 40 -67.1367 40 -127.2v-21.2988c0 -62.542 -38.6582 -98.8008 -91.1445 -99.9404c-17.8135 -12.4814 -40.7852 -18.4922 -62.791 -15.9844c-5.8125 -2.96094 -15.6973 -6.51367 -22.0645 -7.92969v-35.6465
c0 -45.4355 -37.584 -83.2002 -83.2002 -83.2002zM188.8 -16c18.9756 0 35.2002 16.2246 35.2002 35.2002v91.0244c14.6689 -12.8369 42.8252 -14.415 61.0498 4.9502c19.6465 -11.2256 45.624 -1.68652 53.625 12.9248c39.1279 -6.52246 61.3252 10.0771 61.3252 50.6016
v21.2988c0 45.4912 -35.9131 77.21 -39.6768 120h-176.752c-2.96289 -25.2393 -21.2227 -42.9658 -39.5967 -59.0752c-12.6494 -11.2744 -25.2998 -21.7246 -39.875 -30.7988c-23.3877 -14.4814 -56.0996 -26.1318 -56.0996 -34.9258c0 -23.375 8.7998 -44 35.2002 -44
c35.2002 0 53.0752 26.3994 70.3994 26.3994v-158.399c0 -18.4248 16.5 -35.2002 35.2002 -35.2002zM328 384c0 -13.2549 10.7451 -24 24 -24s24 10.7451 24 24s-10.7451 24 -24 24s-24 -10.7451 -24 -24z" />
<glyph glyph-name="copy" unicode="&#xf0c5;" horiz-adv-x="448"
d="M433.941 382.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-268.118c0 -26.5098 -21.4902 -48 -48 -48h-80v-48c0 -26.5098 -21.4902 -48 -48 -48h-224c-26.5098 0 -48 21.4902 -48 48v320c0 26.5098 21.4902 48 48 48h80v48c0 26.5098 21.4902 48 48 48
h172.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM266 -16c3.31152 0 6 2.68848 6 6v42h-96c-26.5098 0 -48 21.4902 -48 48v224h-74c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h212zM394 80c3.31152 0 6 2.68848 6 6v202h-88
c-13.2549 0 -24 10.7451 -24 24v88h-106c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h212zM400 336v9.63184v0.000976562c0 1.37207 -0.787109 3.27246 -1.75684 4.24219l-48.3682 48.3682c-1.12598 1.125 -2.65234 1.75684 -4.24316 1.75684h-9.63184
v-64h64z" />
<glyph glyph-name="save" unicode="&#xf0c7;" horiz-adv-x="448"
d="M433.941 318.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-268.118c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h268.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM272 368h-128v-80h128v80
zM394 16c3.31152 0 6 2.68848 6 6v259.632v0.000976562c0 1.37207 -0.787109 3.27246 -1.75684 4.24219l-78.2432 78.2432v-100.118c0 -13.2549 -10.7451 -24 -24 -24h-176c-13.2549 0 -24 10.7451 -24 24v104h-42c-3.31152 0 -6 -2.68848 -6 -6v-340
c0 -3.31152 2.68848 -6 6 -6h340zM224 216c48.5234 0 88 -39.4766 88 -88s-39.4766 -88 -88 -88s-88 39.4766 -88 88s39.4766 88 88 88zM224 88c22.0557 0 40 17.9443 40 40s-17.9443 40 -40 40s-40 -17.9443 -40 -40s17.9443 -40 40 -40z" />
<glyph glyph-name="square" unicode="&#xf0c8;" horiz-adv-x="448"
d="M400 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h352zM394 16c3.2998 0 6 2.7002 6 6v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340z" />
<glyph glyph-name="envelope" unicode="&#xf0e0;"
d="M464 384c26.5098 0 48 -21.4902 48 -48v-288c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v288c0 26.5098 21.4902 48 48 48h416zM464 336h-416v-40.8047c22.4248 -18.2627 58.1797 -46.6602 134.587 -106.49
c16.834 -13.2422 50.2051 -45.0762 73.4131 -44.7012c23.2119 -0.371094 56.5723 31.4541 73.4131 44.7012c76.4189 59.8389 112.165 88.2305 134.587 106.49v40.8047zM48 48h416v185.601c-22.915 -18.252 -55.4189 -43.8691 -104.947 -82.6523
c-22.5439 -17.748 -60.3359 -55.1787 -103.053 -54.9473c-42.9277 -0.231445 -81.2051 37.75 -103.062 54.9551c-49.5293 38.7842 -82.0244 64.3945 -104.938 82.6455v-185.602z" />
<glyph glyph-name="lightbulb" unicode="&#xf0eb;" horiz-adv-x="352"
d="M176 368c8.83984 0 16 -7.16016 16 -16s-7.16016 -16 -16 -16c-35.2803 0 -64 -28.7002 -64 -64c0 -8.83984 -7.16016 -16 -16 -16s-16 7.16016 -16 16c0 52.9404 43.0596 96 96 96zM96.0596 -11.1699l-0.0400391 43.1797h159.961l-0.0507812 -43.1797
c-0.00976562 -3.13965 -0.939453 -6.21973 -2.67969 -8.83984l-24.5098 -36.8398c-2.95996 -4.45996 -7.95996 -7.14062 -13.3203 -7.14062h-78.8496c-5.35059 0 -10.3506 2.68066 -13.3203 7.14062l-24.5098 36.8398c-1.75 2.62012 -2.68066 5.68945 -2.68066 8.83984z
M176 448c97.2002 0 176 -78.7998 176 -176c0 -44.3701 -16.4502 -84.8496 -43.5498 -115.79c-16.6406 -18.9795 -42.7402 -58.79 -52.4199 -92.1602v-0.0498047h-48v0.0996094c0.00390625 4.04199 0.999023 10.4482 2.21973 14.3008
c5.67969 17.9893 22.9902 64.8496 62.0996 109.46c20.4102 23.29 31.6504 53.1699 31.6504 84.1396c0 70.5801 -57.4199 128 -128 128c-68.2803 0 -128.15 -54.3604 -127.95 -128c0.0898438 -30.9902 11.0703 -60.71 31.6104 -84.1396
c39.3496 -44.9004 56.5801 -91.8604 62.1699 -109.67c1.42969 -4.56055 2.13965 -9.30078 2.15039 -14.0703v-0.120117h-48v0.0595703c-9.68066 33.3604 -35.7803 73.1709 -52.4209 92.1602c-27.1094 30.9307 -43.5596 71.4102 -43.5596 115.78
c0 93.0303 73.7197 176 176 176z" />
<glyph glyph-name="bell" unicode="&#xf0f3;" horiz-adv-x="448"
d="M439.39 85.71c6 -6.44043 8.66016 -14.1602 8.61035 -21.71c-0.0996094 -16.4004 -12.9805 -32 -32.0996 -32h-383.801c-19.1191 0 -31.9893 15.5996 -32.0996 32c-0.0498047 7.5498 2.61035 15.2598 8.61035 21.71c19.3193 20.7598 55.4697 51.9902 55.4697 154.29
c0 77.7002 54.4795 139.9 127.939 155.16v20.8398c0 17.6699 14.3203 32 31.9805 32s31.9805 -14.3301 31.9805 -32v-20.8398c73.46 -15.2598 127.939 -77.46 127.939 -155.16c0 -102.3 36.1504 -133.53 55.4697 -154.29zM67.5303 80h312.939
c-21.2197 27.96 -44.4199 74.3203 -44.5293 159.42c0 0.200195 0.0595703 0.379883 0.0595703 0.580078c0 61.8604 -50.1396 112 -112 112s-112 -50.1396 -112 -112c0 -0.200195 0.0595703 -0.379883 0.0595703 -0.580078
c-0.109375 -85.0898 -23.3096 -131.45 -44.5293 -159.42zM224 -64c-35.3203 0 -63.9697 28.6504 -63.9697 64h127.939c0 -35.3496 -28.6494 -64 -63.9697 -64z" />
<glyph glyph-name="hospital" unicode="&#xf0f8;" horiz-adv-x="448"
d="M128 204v40c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-40c0 -6.62695 -5.37305 -12 -12 -12h-40c-6.62695 0 -12 5.37305 -12 12zM268 192c-6.62695 0 -12 5.37305 -12 12v40c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-40
c0 -6.62695 -5.37305 -12 -12 -12h-40zM192 108c0 -6.62695 -5.37305 -12 -12 -12h-40c-6.62695 0 -12 5.37305 -12 12v40c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-40zM268 96c-6.62695 0 -12 5.37305 -12 12v40c0 6.62695 5.37305 12 12 12h40
c6.62695 0 12 -5.37305 12 -12v-40c0 -6.62695 -5.37305 -12 -12 -12h-40zM448 -28v-36h-448v36c0 6.62695 5.37305 12 12 12h19.5v378.965c0 11.6172 10.7451 21.0352 24 21.0352h88.5v40c0 13.2549 10.7451 24 24 24h112c13.2549 0 24 -10.7451 24 -24v-40h88.5
c13.2549 0 24 -9.41797 24 -21.0352v-378.965h19.5c6.62695 0 12 -5.37305 12 -12zM79.5 -15h112.5v67c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-67h112.5v351h-64.5v-24c0 -13.2549 -10.7451 -24 -24 -24h-112c-13.2549 0 -24 10.7451 -24 24v24
h-64.5v-351zM266 384h-26v26c0 3.31152 -2.68848 6 -6 6h-20c-3.31152 0 -6 -2.68848 -6 -6v-26h-26c-3.31152 0 -6 -2.68848 -6 -6v-20c0 -3.31152 2.68848 -6 6 -6h26v-26c0 -3.31152 2.68848 -6 6 -6h20c3.31152 0 6 2.68848 6 6v26h26c3.31152 0 6 2.68848 6 6v20
c0 3.31152 -2.68848 6 -6 6z" />
<glyph glyph-name="plus-square" unicode="&#xf0fe;" horiz-adv-x="448"
d="M352 208v-32c0 -6.59961 -5.40039 -12 -12 -12h-88v-88c0 -6.59961 -5.40039 -12 -12 -12h-32c-6.59961 0 -12 5.40039 -12 12v88h-88c-6.59961 0 -12 5.40039 -12 12v32c0 6.59961 5.40039 12 12 12h88v88c0 6.59961 5.40039 12 12 12h32c6.59961 0 12 -5.40039 12 -12
v-88h88c6.59961 0 12 -5.40039 12 -12zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340
c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="circle" unicode="&#xf111;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200z" />
<glyph glyph-name="smile" unicode="&#xf118;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM332 135.4c8.5 10.1992 23.7002 11.5 33.7998 3.09961c10.2002 -8.5 11.6006 -23.5996 3.10059 -33.7998
c-30 -36 -74.1006 -56.6006 -120.9 -56.6006s-90.9004 20.6006 -120.9 56.6006c-8.39941 10.2002 -7.09961 25.2998 3.10059 33.7998c10.0996 8.40039 25.2998 7.09961 33.7998 -3.09961c20.7998 -25.1006 51.5 -39.4004 84 -39.4004s63.2002 14.4004 84 39.4004z" />
<glyph glyph-name="frown" unicode="&#xf119;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM248 144c40.2002 0 78 -17.7002 103.8 -48.5996c8.40039 -10.2002 7.10059 -25.3008 -3.09961 -33.8008
c-10.7002 -8.7998 -25.7002 -6.59961 -33.7998 3.10059c-16.6006 20 -41 31.3994 -66.9004 31.3994s-50.2998 -11.5 -66.9004 -31.3994c-8.5 -10.2002 -23.5996 -11.5 -33.7998 -3.10059c-10.2002 8.5 -11.5996 23.6006 -3.09961 33.8008
c25.7998 30.8994 63.5996 48.5996 103.8 48.5996z" />
<glyph glyph-name="meh" unicode="&#xf11a;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM336 128c13.2002 0 24 -10.7998 24 -24s-10.7998 -24 -24 -24h-176c-13.2002 0 -24 10.7998 -24 24s10.7998 24 24 24h176z
" />
<glyph glyph-name="keyboard" unicode="&#xf11c;" horiz-adv-x="576"
d="M528 384c26.5098 0 48 -21.4902 48 -48v-288c0 -26.5098 -21.4902 -48 -48 -48h-480c-26.5098 0 -48 21.4902 -48 48v288c0 26.5098 21.4902 48 48 48h480zM536 48v288c0 4.41113 -3.58887 8 -8 8h-480c-4.41113 0 -8 -3.58887 -8 -8v-288c0 -4.41113 3.58887 -8 8 -8
h480c4.41113 0 8 3.58887 8 8zM170 178c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM266 178c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28
c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM362 178c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM458 178c0 -6.62695 -5.37305 -12 -12 -12h-28
c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM122 96c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM506 96
c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM122 260c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28
c6.62695 0 12 -5.37305 12 -12v-28zM218 260c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM314 260c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28
c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM410 260c0 -6.62695 -5.37305 -12 -12 -12h-28c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM506 260c0 -6.62695 -5.37305 -12 -12 -12h-28
c-6.62695 0 -12 5.37305 -12 12v28c0 6.62695 5.37305 12 12 12h28c6.62695 0 12 -5.37305 12 -12v-28zM408 102c0 -6.62695 -5.37305 -12 -12 -12h-216c-6.62695 0 -12 5.37305 -12 12v16c0 6.62695 5.37305 12 12 12h216c6.62695 0 12 -5.37305 12 -12v-16z" />
<glyph glyph-name="calendar" unicode="&#xf133;" horiz-adv-x="448"
d="M400 384c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12
v-52h48zM394 -16c3.2998 0 6 2.7002 6 6v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340z" />
<glyph glyph-name="play-circle" unicode="&#xf144;"
d="M371.7 210c16.3994 -9.2002 16.3994 -32.9004 0 -42l-176 -101c-15.9004 -8.7998 -35.7002 2.59961 -35.7002 21v208c0 18.5 19.9004 29.7998 35.7002 21zM504 192c0 -137 -111 -248 -248 -248s-248 111 -248 248s111 248 248 248s248 -111 248 -248zM56 192
c0 -110.5 89.5 -200 200 -200s200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200z" />
<glyph glyph-name="minus-square" unicode="&#xf146;" horiz-adv-x="448"
d="M108 164c-6.59961 0 -12 5.40039 -12 12v32c0 6.59961 5.40039 12 12 12h232c6.59961 0 12 -5.40039 12 -12v-32c0 -6.59961 -5.40039 -12 -12 -12h-232zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h352
c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="check-square" unicode="&#xf14a;" horiz-adv-x="448"
d="M400 416c26.5098 0 48 -21.4902 48 -48v-352c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h352zM400 16v352h-352v-352h352zM364.136 257.724l-172.589 -171.204
c-4.70508 -4.66699 -12.3027 -4.63672 -16.9697 0.0683594l-90.7812 91.5156c-4.66699 4.70508 -4.63672 12.3037 0.0693359 16.9717l22.7188 22.5361c4.70508 4.66699 12.3027 4.63672 16.9697 -0.0693359l59.792 -60.2773l141.353 140.217
c4.70508 4.66699 12.3027 4.63672 16.9697 -0.0683594l22.5361 -22.7178c4.66699 -4.70605 4.63672 -12.3047 -0.0683594 -16.9717z" />
<glyph glyph-name="share-square" unicode="&#xf14d;" horiz-adv-x="576"
d="M561.938 289.94c18.75 -18.7402 18.75 -49.1406 0 -67.8809l-143.998 -144c-29.9727 -29.9727 -81.9404 -9.05273 -81.9404 33.9404v53.7998c-101.266 -7.83691 -99.625 -31.6406 -84.1104 -78.7598c14.2285 -43.0889 -33.4736 -79.248 -71.0195 -55.7402
c-51.6924 32.3057 -84.8701 83.0635 -84.8701 144.76c0 39.3408 12.2197 72.7402 36.3301 99.3008c19.8398 21.8398 47.7402 38.4697 82.9102 49.4199c36.7295 11.4395 78.3096 16.1094 120.76 17.9893v57.1982c0 42.9355 51.9258 63.9541 81.9404 33.9404zM384 112l144 144
l-144 144v-104.09c-110.86 -0.90332 -240 -10.5166 -240 -119.851c0 -52.1396 32.79 -85.6094 62.3096 -104.06c-39.8174 120.65 48.999 141.918 177.69 143.84v-103.84zM408.74 27.5068c6.14844 1.75684 15.5449 5.92383 20.9736 9.30273
c7.97656 4.95215 18.2861 -0.825195 18.2861 -10.2139v-42.5957c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h132c6.62695 0 12 -5.37305 12 -12v-4.48633c0 -4.91699 -2.9873 -9.36914 -7.56934 -11.1514
c-13.7021 -5.33105 -26.3955 -11.5371 -38.0498 -18.585c-1.59668 -0.974609 -4.41016 -1.77051 -6.28027 -1.77734h-86.1006c-3.31152 0 -6 -2.68848 -6 -6v-340c0 -3.31152 2.68848 -6 6 -6h340c3.31152 0 6 2.68848 6 6v25.9658c0 5.37012 3.5791 10.0596 8.74023 11.541
z" />
<glyph glyph-name="compass" unicode="&#xf14e;" horiz-adv-x="496"
d="M347.94 318.14c16.6592 7.61035 33.8096 -9.54004 26.1992 -26.1992l-65.9697 -144.341c-2.73047 -5.97363 -9.7959 -13.0391 -15.7695 -15.7695l-144.341 -65.9697c-16.6592 -7.61035 -33.8096 9.5498 -26.1992 26.1992l65.9697 144.341
c2.73047 5.97363 9.7959 13.0391 15.7695 15.7695zM270.58 169.42c12.4697 12.4697 12.4697 32.6904 0 45.1602s-32.6904 12.4697 -45.1602 0s-12.4697 -32.6904 0 -45.1602s32.6904 -12.4697 45.1602 0zM248 440c136.97 0 248 -111.03 248 -248s-111.03 -248 -248 -248
s-248 111.03 -248 248s111.03 248 248 248zM248 -8c110.28 0 200 89.7197 200 200s-89.7197 200 -200 200s-200 -89.7197 -200 -200s89.7197 -200 200 -200z" />
<glyph glyph-name="caret-square-down" unicode="&#xf150;" horiz-adv-x="448"
d="M125.1 240h197.801c10.6992 0 16.0996 -13 8.5 -20.5l-98.9004 -98.2998c-4.7002 -4.7002 -12.2002 -4.7002 -16.9004 0l-98.8994 98.2998c-7.7002 7.5 -2.2998 20.5 8.39941 20.5zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="caret-square-up" unicode="&#xf151;" horiz-adv-x="448"
d="M322.9 144h-197.801c-10.6992 0 -16.0996 13 -8.5 20.5l98.9004 98.2998c4.7002 4.7002 12.2002 4.7002 16.9004 0l98.8994 -98.2998c7.7002 -7.5 2.2998 -20.5 -8.39941 -20.5zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="caret-square-right" unicode="&#xf152;" horiz-adv-x="448"
d="M176 93.0996v197.801c0 10.6992 13 16.0996 20.5 8.5l98.2998 -98.9004c4.7002 -4.7002 4.7002 -12.2002 0 -16.9004l-98.2998 -98.8994c-7.5 -7.7002 -20.5 -2.2998 -20.5 8.39941zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="file" unicode="&#xf15b;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416z" />
<glyph glyph-name="file-alt" unicode="&#xf15c;" horiz-adv-x="384"
d="M288 200v-28c0 -6.59961 -5.40039 -12 -12 -12h-168c-6.59961 0 -12 5.40039 -12 12v28c0 6.59961 5.40039 12 12 12h168c6.59961 0 12 -5.40039 12 -12zM276 128c6.59961 0 12 -5.40039 12 -12v-28c0 -6.59961 -5.40039 -12 -12 -12h-168c-6.59961 0 -12 5.40039 -12 12
v28c0 6.59961 5.40039 12 12 12h168zM384 316.1v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996l83.9004 -83.9004c9 -8.90039 14.0996 -21.2002 14.0996 -33.9004z
M256 396.1v-76.0996h76.0996zM336 -16v288h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416h288z" />
<glyph glyph-name="thumbs-up" unicode="&#xf164;"
d="M466.27 161.31c4.6748 -22.6465 0.864258 -44.5371 -8.98926 -62.9893c2.95898 -23.8682 -4.02148 -48.5654 -17.3398 -66.9902c-0.954102 -55.9072 -35.8232 -95.3301 -112.94 -95.3301c-7 0 -15 0.00976562 -22.2197 0.00976562
c-102.742 0 -133.293 38.9395 -177.803 39.9404c-3.56934 -13.7764 -16.085 -23.9502 -30.9775 -23.9502h-64c-17.6729 0 -32 14.3271 -32 32v240c0 17.6729 14.3271 32 32 32h98.7598c19.1455 16.9531 46.0137 60.6533 68.7598 83.4004
c13.667 13.667 10.1533 108.6 71.7607 108.6c57.5801 0 95.2695 -31.9355 95.2695 -104.73c0 -18.4092 -3.92969 -33.7295 -8.84961 -46.5391h36.4795c48.6025 0 85.8203 -41.5654 85.8203 -85.5801c0 -19.1504 -4.95996 -34.9902 -13.7305 -49.8408zM404.52 107.48
c21.5811 20.3838 18.6992 51.0645 5.21094 65.6191c9.44922 0 22.3594 18.9102 22.2695 37.8105c-0.0898438 18.9102 -16.71 37.8203 -37.8203 37.8203h-103.989c0 37.8193 28.3594 55.3691 28.3594 94.5391c0 23.75 0 56.7305 -47.2695 56.7305
c-18.9102 -18.9102 -9.45996 -66.1797 -37.8203 -94.54c-26.5596 -26.5703 -66.1797 -97.46 -94.54 -97.46h-10.9199v-186.17c53.6113 0 100.001 -37.8203 171.64 -37.8203h37.8203c35.5117 0 60.8203 17.1201 53.1201 65.9004
c15.2002 8.16016 26.5 36.4395 13.9395 57.5703zM88 16c0 13.2549 -10.7451 24 -24 24s-24 -10.7451 -24 -24s10.7451 -24 24 -24s24 10.7451 24 24z" />
<glyph glyph-name="thumbs-down" unicode="&#xf165;"
d="M466.27 222.69c8.77051 -14.8506 13.7305 -30.6904 13.7305 -49.8408c0 -44.0146 -37.2178 -85.5801 -85.8203 -85.5801h-36.4795c4.91992 -12.8096 8.84961 -28.1299 8.84961 -46.5391c0 -72.7949 -37.6895 -104.73 -95.2695 -104.73
c-61.6074 0 -58.0938 94.9326 -71.7607 108.6c-22.7461 22.7471 -49.6133 66.4473 -68.7598 83.4004h-7.05176c-5.5332 -9.56152 -15.8662 -16 -27.708 -16h-64c-17.6729 0 -32 14.3271 -32 32v240c0 17.6729 14.3271 32 32 32h64c8.11328 0 15.5146 -3.02539 21.1553 -8
h10.8447c40.9971 0 73.1953 39.9902 176.78 39.9902c7.21973 0 15.2197 0.00976562 22.2197 0.00976562c77.1172 0 111.986 -39.4229 112.94 -95.3301c13.3184 -18.4248 20.2979 -43.1221 17.3398 -66.9902c9.85352 -18.4521 13.6641 -40.3428 8.98926 -62.9893zM64 152
c13.2549 0 24 10.7451 24 24s-10.7451 24 -24 24s-24 -10.7451 -24 -24s10.7451 -24 24 -24zM394.18 135.27c21.1104 0 37.7305 18.9102 37.8203 37.8203c0.0898438 18.9004 -12.8203 37.8105 -22.2695 37.8105c13.4883 14.5547 16.3701 45.2354 -5.21094 65.6191
c12.5605 21.1309 1.26074 49.4102 -13.9395 57.5703c7.7002 48.7803 -17.6084 65.9004 -53.1201 65.9004h-37.8203c-71.6387 0 -118.028 -37.8203 -171.64 -37.8203v-186.17h10.9199c28.3604 0 67.9805 -70.8896 94.54 -97.46
c28.3604 -28.3604 18.9102 -75.6299 37.8203 -94.54c47.2695 0 47.2695 32.9805 47.2695 56.7305c0 39.1699 -28.3594 56.7197 -28.3594 94.5391h103.989z" />
<glyph glyph-name="sun" unicode="&#xf185;"
d="M494.2 226.1c11.2002 -7.59961 17.7998 -20.0996 17.8994 -33.6992c0 -13.4004 -6.69922 -26 -17.7998 -33.5l-59.7998 -40.5l13.7002 -71c2.5 -13.2002 -1.60059 -26.8008 -11.1006 -36.3008s-22.8994 -13.7998 -36.2998 -11.0996l-70.8994 13.7002l-40.4004 -59.9004
c-7.5 -11.0996 -20.0996 -17.7998 -33.5 -17.7998s-26 6.7002 -33.5 17.9004l-40.4004 59.8994l-70.7998 -13.7002c-13.3994 -2.59961 -26.7998 1.60059 -36.2998 11.1006s-13.7002 23.0996 -11.0996 36.2998l13.6992 71l-59.7998 40.5
c-11.0996 7.5 -17.7998 20 -17.7998 33.5s6.59961 26 17.7998 33.5996l59.7998 40.5l-13.6992 71c-2.60059 13.2002 1.59961 26.7002 11.0996 36.3008c9.5 9.59961 23 13.6992 36.2998 11.1992l70.7998 -13.6992l40.4004 59.8994c15.0996 22.2998 51.9004 22.2998 67 0
l40.4004 -59.8994l70.8994 13.6992c13 2.60059 26.6006 -1.59961 36.2002 -11.0996c9.5 -9.59961 13.7002 -23.2002 11.0996 -36.4004l-13.6992 -71zM381.3 140.5l76.7998 52.0996l-76.7998 52l17.6006 91.1006l-91 -17.6006l-51.9004 76.9004l-51.7998 -76.7998
l-91 17.5996l17.5996 -91.2002l-76.7998 -52l76.7998 -52l-17.5996 -91.1992l90.8994 17.5996l51.9004 -77l51.9004 76.9004l91 -17.6006zM256 296c57.2998 0 104 -46.7002 104 -104s-46.7002 -104 -104 -104s-104 46.7002 -104 104s46.7002 104 104 104zM256 136
c30.9004 0 56 25.0996 56 56s-25.0996 56 -56 56s-56 -25.0996 -56 -56s25.0996 -56 56 -56z" />
<glyph glyph-name="moon" unicode="&#xf186;"
d="M279.135 -64c-141.424 0 -256 114.64 -256 256c0 141.425 114.641 256 256 256c13.0068 -0.00195312 33.9443 -1.91797 46.7354 -4.27734c44.0205 -8.13086 53.7666 -66.8691 15.0215 -88.9189c-41.374 -23.5439 -67.4336 -67.4121 -67.4336 -115.836
c0 -83.5234 75.9238 -146.475 158.272 -130.792c43.6904 8.32129 74.5186 -42.5693 46.248 -77.4004c-47.8613 -58.9717 -120.088 -94.7754 -198.844 -94.7754zM279.135 400c-114.875 0 -208 -93.125 -208 -208s93.125 -208 208 -208
c65.2314 0 123.439 30.0361 161.575 77.0244c-111.611 -21.2568 -215.252 64.0957 -215.252 177.943c0 67.5127 36.9326 126.392 91.6934 157.555c-12.3271 2.27637 -25.0312 3.47754 -38.0166 3.47754z" />
<glyph glyph-name="caret-square-left" unicode="&#xf191;" horiz-adv-x="448"
d="M272 290.9v-197.801c0 -10.6992 -13 -16.0996 -20.5 -8.5l-98.2998 98.9004c-4.7002 4.7002 -4.7002 12.2002 0 16.9004l98.2998 98.8994c7.5 7.7002 20.5 2.2998 20.5 -8.39941zM448 368v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352
c0 26.5 21.5 48 48 48h352c26.5 0 48 -21.5 48 -48zM400 22v340c0 3.2998 -2.7002 6 -6 6h-340c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="dot-circle" unicode="&#xf192;"
d="M256 392c-110.549 0 -200 -89.4678 -200 -200c0 -110.549 89.4678 -200 200 -200c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200zM256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248z
M256 272c44.1826 0 80 -35.8174 80 -80s-35.8174 -80 -80 -80s-80 35.8174 -80 80s35.8174 80 80 80z" />
<glyph glyph-name="building" unicode="&#xf1ad;" horiz-adv-x="448"
d="M128 300v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12zM268 288c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40
c0 -6.59961 -5.40039 -12 -12 -12h-40zM140 192c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-40zM268 192c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40
c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-40zM192 108c0 -6.59961 -5.40039 -12 -12 -12h-40c-6.59961 0 -12 5.40039 -12 12v40c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40zM268 96c-6.59961 0 -12 5.40039 -12 12v40
c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-40zM448 -28v-36h-448v36c0 6.59961 5.40039 12 12 12h19.5v440c0 13.2998 10.7002 24 24 24h337c13.2998 0 24 -10.7002 24 -24v-440h19.5
c6.59961 0 12 -5.40039 12 -12zM79.5 -15h112.5v67c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-67h112.5v414l-288.5 1z" />
<glyph glyph-name="file-pdf" unicode="&#xf1c1;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM298.2 127.7c10.5 -10.5 8 -38.7002 -17.5 -38.7002c-14.7998 0 -36.9004 6.7998 -55.7998 17c-21.6006 -3.59961 -46 -12.7002 -68.4004 -20.0996c-50.0996 -86.4004 -79.4004 -47 -76.0996 -31.2002
c4 20 31 35.8994 51 46.2002c10.5 18.3994 25.3994 50.5 35.3994 74.3994c-7.39941 28.6006 -11.3994 51 -7 67.1006c4.7998 17.6992 38.4004 20.2998 42.6006 -5.90039c4.69922 -15.4004 -1.5 -39.9004 -5.40039 -56c8.09961 -21.2998 19.5996 -35.7998 36.7998 -46.2998
c17.4004 2.2002 52.2002 5.5 64.4004 -6.5zM100.1 49.9004c0 -0.700195 11.4004 4.69922 30.4004 35c-5.90039 -5.5 -25.2998 -21.3008 -30.4004 -35zM181.7 240.5c-2.5 0 -2.60059 -26.9004 1.7998 -40.7998c4.90039 8.7002 5.59961 40.7998 -1.7998 40.7998zM157.3 103.9
c15.9004 6.09961 34 14.8994 54.7998 19.1992c-11.1992 8.30078 -21.7998 20.4004 -30.0996 35.5c-6.7002 -17.6992 -15 -37.7998 -24.7002 -54.6992zM288.9 108.9c3.59961 2.39941 -2.2002 10.3994 -37.3008 7.7998c32.3008 -13.7998 37.3008 -7.7998 37.3008 -7.7998z" />
<glyph glyph-name="file-word" unicode="&#xf1c2;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM268.1 192v0.200195h15.8008c7.7998 0 13.5 -7.2998 11.5996 -14.9004c-4.2998 -17 -13.7002 -54.0996 -34.5 -136c-1.2998 -5.39941 -6.09961 -9.09961 -11.5996 -9.09961h-24.7002
c-5.5 0 -10.2998 3.7998 -11.6006 9.09961c-5.2998 20.9004 -17.7998 71 -17.8994 71.4004l-2.90039 17.2998c-0.5 -5.2998 -1.5 -11.0996 -3 -17.2998l-17.8994 -71.4004c-1.30078 -5.39941 -6.10059 -9.09961 -11.6006 -9.09961h-25.2002
c-5.59961 0 -10.3994 3.7002 -11.6992 9.09961c-6.5 26.5 -25.2002 103.4 -33.2002 136c-1.7998 7.5 3.89941 14.7998 11.7002 14.7998h16.7998c5.7998 0 10.7002 -4.09961 11.7998 -9.69922c5 -25.7002 18.4004 -93.8008 19.0996 -99
c0.300781 -1.7002 0.400391 -3.10059 0.5 -4.2002c0.800781 7.5 0.400391 4.7002 24.8008 103.7c1.39941 5.2998 6.19922 9.09961 11.6992 9.09961h13.3008c5.59961 0 10.3994 -3.7998 11.6992 -9.2002c23.9004 -99.7002 22.8008 -94.3994 23.6006 -99.5
c0.299805 -1.7002 0.5 -3.09961 0.700195 -4.2998c0.599609 8.09961 0.399414 5.7998 21 103.5c1.09961 5.5 6 9.5 11.6992 9.5z" />
<glyph glyph-name="file-excel" unicode="&#xf1c3;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM260 224c9.2002 0 15 -10 10.2998 -18c-16 -27.5 -45.5996 -76.9004 -46.2998 -78l46.4004 -78c4.59961 -8 -1.10059 -18 -10.4004 -18h-28.7998c-4.40039 0 -8.5 2.40039 -10.6006 6.2998
c-22.6992 41.7998 -13.6992 27.5 -28.5996 57.7002c-5.59961 -12.7002 -6.90039 -17.7002 -28.5996 -57.7002c-2.10059 -3.89941 -6.10059 -6.2998 -10.5 -6.2998h-28.9004c-9.2998 0 -15.0996 10 -10.4004 18l46.3008 78l-46.3008 78c-4.59961 8 1.10059 18 10.4004 18
h28.9004c4.39941 0 8.5 -2.40039 10.5996 -6.2998c21.7002 -40.4004 14.7002 -28.6006 28.5996 -57.7002c6.40039 15.2998 10.6006 24.5996 28.6006 57.7002c2.09961 3.89941 6.09961 6.2998 10.5 6.2998h28.7998z" />
<glyph glyph-name="file-powerpoint" unicode="&#xf1c4;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM120 44v168c0 6.59961 5.40039 12 12 12h69.2002c36.7002 0 62.7998 -27 62.7998 -66.2998c0 -74.2998 -68.7002 -66.5 -95.5 -66.5v-47.2002c0 -6.59961 -5.40039 -12 -12 -12h-24.5c-6.59961 0 -12 5.40039 -12 12z
M168.5 131.4h23c7.90039 0 13.9004 2.39941 18.0996 7.19922c8.5 9.80078 8.40039 28.5 0.100586 37.8008c-4.10059 4.59961 -9.90039 7 -17.4004 7h-23.8994v-52h0.0996094z" />
<glyph glyph-name="file-image" unicode="&#xf1c5;" horiz-adv-x="384"
d="M369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM332.1 320l-76.0996 76.0996v-76.0996h76.0996zM48 -16h288v288
h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416zM80 32v64l39.5 39.5c4.7002 4.7002 12.2998 4.7002 17 0l39.5 -39.5l87.5 87.5c4.7002 4.7002 12.2998 4.7002 17 0l23.5 -23.5v-128h-224zM128 272c26.5 0 48 -21.5 48 -48s-21.5 -48 -48 -48s-48 21.5 -48 48
s21.5 48 48 48z" />
<glyph glyph-name="file-archive" unicode="&#xf1c6;" horiz-adv-x="384"
d="M128.3 288h32v-32h-32v32zM192.3 384v-32h-32v32h32zM128.3 352h32v-32h-32v32zM192.3 320v-32h-32v32h32zM369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1
c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM256 396.1v-76.0996h76.0996zM336 -16v288h-104c-13.2998 0 -24 10.7002 -24 24v104h-48.2998v-16h-32v16h-79.7002v-416h288zM194.2 182.3l17.2998 -87.7002c6.40039 -32.3994 -18.4004 -62.5996 -51.5 -62.5996
c-33.2002 0 -58 30.4004 -51.4004 62.9004l19.7002 97.0996v32h32v-32h22.1006c5.7998 0 10.6992 -4.09961 11.7998 -9.7002zM160.3 57.9004c17.9004 0 32.4004 12.0996 32.4004 27c0 14.8994 -14.5 27 -32.4004 27c-17.8994 0 -32.3994 -12.1006 -32.3994 -27
c0 -14.9004 14.5 -27 32.3994 -27zM192.3 256v-32h-32v32h32z" />
<glyph glyph-name="file-audio" unicode="&#xf1c7;" horiz-adv-x="384"
d="M369.941 350.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-332.118c0 -26.5098 -21.4902 -48 -48 -48h-288c-26.5098 0 -48 21.4902 -48 48v416c0 26.5098 21.4902 48 48 48h204.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM332.118 320
l-76.1182 76.1182v-76.1182h76.1182zM48 -16h288v288h-104c-13.2549 0 -24 10.7451 -24 24v104h-160v-416zM192 60.0244c0 -10.6914 -12.9258 -16.0459 -20.4854 -8.48535l-35.5146 35.9746h-28c-6.62695 0 -12 5.37305 -12 12v56c0 6.62695 5.37305 12 12 12h28
l35.5146 36.9473c7.56055 7.56055 20.4854 2.20605 20.4854 -8.48535v-135.951zM233.201 107.154c9.05078 9.29688 9.05957 24.1328 0.000976562 33.4385c-22.1494 22.752 12.2344 56.2461 34.3945 33.4814c27.1982 -27.9404 27.2119 -72.4443 0.000976562 -100.401
c-21.793 -22.3857 -56.9463 10.3154 -34.3965 33.4814z" />
<glyph glyph-name="file-video" unicode="&#xf1c8;" horiz-adv-x="384"
d="M369.941 350.059c7.75977 -7.75977 14.0586 -22.9658 14.0586 -33.9404v-332.118c0 -26.5098 -21.4902 -48 -48 -48h-288c-26.5098 0 -48 21.4902 -48 48v416c0 26.5098 21.4902 48 48 48h204.118c10.9746 0 26.1807 -6.29883 33.9404 -14.0586zM332.118 320
l-76.1182 76.1182v-76.1182h76.1182zM48 -16h288v288h-104c-13.2549 0 -24 10.7451 -24 24v104h-160v-416zM276.687 195.303c10.0049 10.0049 27.3135 2.99707 27.3135 -11.3135v-111.976c0 -14.2939 -17.2959 -21.332 -27.3135 -11.3135l-52.6865 52.6738v-37.374
c0 -11.0459 -8.9541 -20 -20 -20h-104c-11.0459 0 -20 8.9541 -20 20v104c0 11.0459 8.9541 20 20 20h104c11.0459 0 20 -8.9541 20 -20v-37.374z" />
<glyph glyph-name="file-code" unicode="&#xf1c9;" horiz-adv-x="384"
d="M149.9 98.9004c3.5 -3.30078 3.69922 -8.90039 0.399414 -12.4004l-17.3994 -18.5996c-1.60059 -1.80078 -4 -2.80078 -6.40039 -2.80078c-2.2002 0 -4.40039 0.900391 -6 2.40039l-57.7002 54.0996c-3.7002 3.40039 -3.7002 9.30078 0 12.8008l57.7002 54.0996
c3.40039 3.2998 9 3.2002 12.4004 -0.400391l17.3994 -18.5996l0.200195 -0.200195c3.2002 -3.59961 2.7998 -9.2002 -0.799805 -12.3994l-32.7998 -28.9004l32.7998 -28.9004zM369.9 350.1c9 -9 14.0996 -21.2998 14.0996 -34v-332.1c0 -26.5 -21.5 -48 -48 -48h-288
c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48.0996h204.1c12.7002 0 24.9004 -5.09961 33.9004 -14.0996zM256 396.1v-76.0996h76.0996zM336 -16v288h-104c-13.2998 0 -24 10.7002 -24 24v104h-160v-416h288zM209.6 234l24.4004 -7
c4.7002 -1.2998 7.40039 -6.2002 6 -10.9004l-54.7002 -188.199c-1.2998 -4.60059 -6.2002 -7.40039 -10.8994 -6l-24.4004 7.09961c-4.7002 1.2998 -7.40039 6.2002 -6 10.9004l54.7002 188.1c1.39941 4.7002 6.2002 7.40039 10.8994 6zM234.1 157.1
c-3.5 3.30078 -3.69922 8.90039 -0.399414 12.4004l17.3994 18.5996c3.30078 3.60059 8.90039 3.7002 12.4004 0.400391l57.7002 -54.0996c3.7002 -3.40039 3.7002 -9.30078 0 -12.8008l-57.7002 -54.0996c-3.5 -3.2998 -9.09961 -3.09961 -12.4004 0.400391
l-17.3994 18.5996l-0.200195 0.200195c-3.2002 3.59961 -2.7998 9.2002 0.799805 12.3994l32.7998 28.9004l-32.7998 28.9004z" />
<glyph glyph-name="life-ring" unicode="&#xf1cd;"
d="M256 -56c-136.967 0 -248 111.033 -248 248s111.033 248 248 248s248 -111.033 248 -248s-111.033 -248 -248 -248zM152.602 20.7197c63.2178 -38.3184 143.579 -38.3184 206.797 0l-53.4111 53.4111c-31.8467 -13.5215 -68.168 -13.5059 -99.9746 0zM336 192
c0 44.1123 -35.8877 80 -80 80s-80 -35.8877 -80 -80s35.8877 -80 80 -80s80 35.8877 80 80zM427.28 88.6016c38.3184 63.2178 38.3184 143.579 0 206.797l-53.4111 -53.4111c13.5215 -31.8467 13.5049 -68.168 0 -99.9746zM359.397 363.28
c-63.2168 38.3184 -143.578 38.3184 -206.796 0l53.4111 -53.4111c31.8457 13.5215 68.167 13.5049 99.9736 0zM84.7197 295.398c-38.3184 -63.2178 -38.3184 -143.579 0 -206.797l53.4111 53.4111c-13.5215 31.8467 -13.5059 68.168 0 99.9746z" />
<glyph glyph-name="paper-plane" unicode="&#xf1d8;"
d="M440 441.5c34.5996 19.9004 77.5996 -8.7998 71.5 -48.9004l-59.4004 -387.199c-2.2998 -14.5 -11.0996 -27.3008 -23.8994 -34.5c-7.2998 -4.10059 -15.4004 -6.2002 -23.6006 -6.2002c-6.19922 0 -12.3994 1.2002 -18.2998 3.59961l-111.899 46.2002l-43.8008 -59.0996
c-27.3994 -36.9004 -86.5996 -17.8008 -86.5996 28.5996v84.4004l-114.3 47.2998c-36.7998 15.0996 -40.1006 66 -5.7002 85.8994zM192 -16l36.5996 49.5l-36.5996 15.0996v-64.5996zM404.6 12.7002l59.4004 387.3l-416 -240l107.8 -44.5996l211.5 184.3
c14.2002 12.2998 34.4004 -5.7002 23.7002 -21.2002l-140.2 -202.3z" />
<glyph glyph-name="futbol" unicode="&#xf1e3;" horiz-adv-x="496"
d="M483.8 268.6c42.2998 -130.199 -29 -270.1 -159.2 -312.399c-25.5 -8.2998 -51.2998 -12.2002 -76.6992 -12.2002c-104.5 0 -201.7 66.5996 -235.7 171.4c-42.2998 130.199 29 270.1 159.2 312.399c25.5 8.2998 51.2998 12.2002 76.6992 12.2002
c104.5 0 201.7 -66.5996 235.7 -171.4zM409.3 74.9004c6.10059 8.39941 12.1006 16.8994 16.7998 26.1992c14.3008 28.1006 21.5 58.5 21.7002 89.2002l-38.8994 36.4004l-71.1006 -22.1006l-24.3994 -75.1992l43.6992 -60.9004zM409.3 310.3
c-24.5 33.4004 -58.7002 58.4004 -97.8994 71.4004l-47.4004 -26.2002v-73.7998l64.2002 -46.5l70.7002 22zM184.9 381.6c-39.9004 -13.2998 -73.5 -38.5 -97.8008 -71.8994l10.1006 -52.5l70.5996 -22l64.2002 46.5v73.7998zM139 68.5l43.5 61.7002l-24.2998 74.2998
l-71.1006 22.2002l-39 -36.4004c0.5 -55.7002 23.4004 -95.2002 37.8008 -115.3zM187.2 1.5c64.0996 -20.4004 115.5 -1.7998 121.7 0l22.3994 48.0996l-44.2998 61.7002h-78.5996l-43.6006 -61.7002z" />
<glyph glyph-name="newspaper" unicode="&#xf1ea;" horiz-adv-x="576"
d="M552 384c13.2549 0 24 -10.7451 24 -24v-336c0 -13.2549 -10.7451 -24 -24 -24h-496c-30.9277 0 -56 25.0723 -56 56v272c0 13.2549 10.7451 24 24 24h42.752c6.60547 18.623 24.3896 32 45.248 32h440zM48 56c0 -4.41113 3.58887 -8 8 -8s8 3.58887 8 8v248h-16v-248z
M528 48v288h-416v-280c0 -2.7168 -0.204102 -5.38574 -0.578125 -8h416.578zM172 168c-6.62695 0 -12 5.37305 -12 12v96c0 6.62695 5.37305 12 12 12h136c6.62695 0 12 -5.37305 12 -12v-96c0 -6.62695 -5.37305 -12 -12 -12h-136zM200 248v-40h80v40h-80zM160 108v24
c0 6.62695 5.37305 12 12 12h136c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-136c-6.62695 0 -12 5.37305 -12 12zM352 108v24c0 6.62695 5.37305 12 12 12h104c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-104
c-6.62695 0 -12 5.37305 -12 12zM352 252v24c0 6.62695 5.37305 12 12 12h104c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-104c-6.62695 0 -12 5.37305 -12 12zM352 180v24c0 6.62695 5.37305 12 12 12h104c6.62695 0 12 -5.37305 12 -12v-24
c0 -6.62695 -5.37305 -12 -12 -12h-104c-6.62695 0 -12 5.37305 -12 12z" />
<glyph glyph-name="bell-slash" unicode="&#xf1f6;" horiz-adv-x="640"
d="M633.99 -23.0195c6.91016 -5.52051 8.01953 -15.5908 2.5 -22.4902l-10 -12.4902c-5.53027 -6.88965 -15.5898 -8.00977 -22.4902 -2.49023l-598 467.51c-6.90039 5.52051 -8.01953 15.5908 -2.49023 22.4902l10 12.4902
c5.52051 6.90039 15.5898 8.00977 22.4902 2.49023zM163.53 80h182.84l61.3994 -48h-279.659c-19.1201 0 -31.9902 15.5996 -32.1006 32c-0.0498047 7.5498 2.61035 15.2598 8.61035 21.71c18.3701 19.7402 51.5703 49.6904 54.8398 140.42l45.4697 -35.5498
c-6.91992 -54.7803 -24.6895 -88.5498 -41.3994 -110.58zM320 352c-23.3496 0 -45 -7.17969 -62.9404 -19.4004l-38.1699 29.8408c19.6807 15.7793 43.1104 27.3096 69.1299 32.7197v20.8398c0 17.6699 14.3203 32 31.9805 32s31.9805 -14.3301 31.9805 -32v-20.8398
c73.46 -15.2598 127.939 -77.46 127.939 -155.16c0 -41.3604 6.03027 -70.7197 14.3398 -92.8496l-59.5293 46.54c-1.63086 13.96 -2.77051 28.8896 -2.79004 45.7295c0 0.200195 0.0595703 0.379883 0.0595703 0.580078c0 61.8604 -50.1396 112 -112 112zM320 -64
c-35.3203 0 -63.9697 28.6504 -63.9697 64h127.939c0 -35.3496 -28.6494 -64 -63.9697 -64z" />
<glyph glyph-name="copyright" unicode="&#xf1f9;"
d="M256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248zM256 -8c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200c-110.549 0 -200 -89.4688 -200 -200c0 -110.549 89.4678 -200 200 -200z
M363.351 93.0645c-9.61328 -9.71289 -45.5293 -41.3965 -104.064 -41.3965c-82.4297 0 -140.484 61.4248 -140.484 141.567c0 79.1514 60.2754 139.4 139.763 139.4c55.5303 0 88.7373 -26.6201 97.5928 -34.7783c2.13379 -1.96289 3.86523 -5.9082 3.86523 -8.80762
c0 -1.95508 -0.864258 -4.87402 -1.92969 -6.51465l-18.1543 -28.1133c-3.8418 -5.9502 -11.9668 -7.28223 -17.499 -2.9209c-8.5957 6.77637 -31.8145 22.5381 -61.708 22.5381c-48.3037 0 -77.916 -35.3301 -77.916 -80.082c0 -41.5889 26.8877 -83.6924 78.2764 -83.6924
c32.6572 0 56.8428 19.0391 65.7266 27.2256c5.26953 4.85645 13.5957 4.03906 17.8193 -1.73828l19.8652 -27.1699c1.28613 -1.74512 2.33008 -4.91992 2.33008 -7.08789c0 -2.72363 -1.56055 -6.5 -3.48242 -8.42969z" />
<glyph glyph-name="closed-captioning" unicode="&#xf20a;"
d="M464 384c26.5 0 48 -21.5 48 -48v-288c0 -26.5 -21.5 -48 -48 -48h-416c-26.5 0 -48 21.5 -48 48v288c0 26.5 21.5 48 48 48h416zM458 48c3.2998 0 6 2.7002 6 6v276c0 3.2998 -2.7002 6 -6 6h-404c-3.2998 0 -6 -2.7002 -6 -6v-276c0 -3.2998 2.7002 -6 6 -6h404z
M246.9 133.7c1.69922 -2.40039 1.5 -5.60059 -0.5 -7.7002c-53.6006 -56.7998 -172.801 -32.0996 -172.801 67.9004c0 97.2998 121.7 119.5 172.5 70.0996c2.10059 -2 2.5 -3.2002 1 -5.7002l-17.5 -30.5c-1.89941 -3.09961 -6.19922 -4 -9.09961 -1.7002
c-40.7998 32 -94.5996 14.9004 -94.5996 -31.1992c0 -48 51 -70.5 92.1992 -32.6006c2.80078 2.5 7.10059 2.10059 9.2002 -0.899414zM437.3 133.7c1.7002 -2.40039 1.5 -5.60059 -0.5 -7.7002c-53.5996 -56.9004 -172.8 -32.0996 -172.8 67.9004
c0 97.2998 121.7 119.5 172.5 70.0996c2.09961 -2 2.5 -3.2002 1 -5.7002l-17.5 -30.5c-1.90039 -3.09961 -6.2002 -4 -9.09961 -1.7002c-40.8008 32 -94.6006 14.9004 -94.6006 -31.1992c0 -48 51 -70.5 92.2002 -32.6006c2.7998 2.5 7.09961 2.10059 9.2002 -0.899414z
" />
<glyph glyph-name="object-group" unicode="&#xf247;"
d="M500 320h-12v-256h12c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v12h-320v-12c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h12v256h-12
c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h72c6.62695 0 12 -5.37305 12 -12v-12h320v12c0 6.62695 5.37305 12 12 12h72c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12zM448 384v-32h32v32h-32zM32 384v-32h32v32h-32zM64 0v32
h-32v-32h32zM480 0v32h-32v-32h32zM440 64v256h-12c-6.62695 0 -12 5.37305 -12 12v12h-320v-12c0 -6.62695 -5.37305 -12 -12 -12h-12v-256h12c6.62695 0 12 -5.37305 12 -12v-12h320v12c0 6.62695 5.37305 12 12 12h12zM404 256c6.62695 0 12 -5.37207 12 -12v-168
c0 -6.62793 -5.37305 -12 -12 -12h-200c-6.62695 0 -12 5.37207 -12 12v52h-84c-6.62695 0 -12 5.37207 -12 12v168c0 6.62793 5.37305 12 12 12h200c6.62695 0 12 -5.37207 12 -12v-52h84zM136 280v-112h144v112h-144zM376 104v112h-56v-76
c0 -6.62793 -5.37305 -12 -12 -12h-76v-24h144z" />
<glyph glyph-name="object-ungroup" unicode="&#xf248;" horiz-adv-x="576"
d="M564 224h-12v-160h12c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v12h-224v-12c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h12v24h-88v-12
c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h12v160h-12c-6.62695 0 -12 5.37305 -12 12v72c0 6.62695 5.37305 12 12 12h72c6.62695 0 12 -5.37305 12 -12v-12h224v12c0 6.62695 5.37305 12 12 12h72
c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-12v-24h88v12c0 6.62695 5.37305 12 12 12h72c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12zM352 384v-32h32v32h-32zM352 128v-32h32v32h-32zM64 96v32h-32v-32h32zM64 352v32
h-32v-32h32zM96 136h224v12c0 6.62695 5.37305 12 12 12h12v160h-12c-6.62695 0 -12 5.37305 -12 12v12h-224v-12c0 -6.62695 -5.37305 -12 -12 -12h-12v-160h12c6.62695 0 12 -5.37305 12 -12v-12zM224 0v32h-32v-32h32zM504 64v160h-12c-6.62695 0 -12 5.37305 -12 12v12
h-88v-88h12c6.62695 0 12 -5.37305 12 -12v-72c0 -6.62695 -5.37305 -12 -12 -12h-72c-6.62695 0 -12 5.37305 -12 12v12h-88v-24h12c6.62695 0 12 -5.37305 12 -12v-12h224v12c0 6.62695 5.37305 12 12 12h12zM544 0v32h-32v-32h32zM544 256v32h-32v-32h32z" />
<glyph glyph-name="sticky-note" unicode="&#xf249;" horiz-adv-x="448"
d="M448 99.8936c0 -10.9746 -6.29883 -26.1797 -14.0586 -33.9404l-83.8828 -83.8818c-7.75977 -7.76074 -22.9658 -14.0596 -33.9404 -14.0596h-268.118c-26.5098 0 -48 21.4902 -48 48v351.988c0 26.5098 21.4902 48 48 48h352c26.5098 0 48 -21.4902 48 -48v-268.106z
M320 19.8936l76.1182 76.1182h-76.1182v-76.1182zM400 368h-352v-351.988h224v104c0 13.2549 10.7451 24 24 24h104v223.988z" />
<glyph glyph-name="clone" unicode="&#xf24d;"
d="M464 448c26.5098 0 48 -21.4902 48 -48v-320c0 -26.5098 -21.4902 -48 -48 -48h-48v-48c0 -26.5098 -21.4902 -48 -48 -48h-320c-26.5098 0 -48 21.4902 -48 48v320c0 26.5098 21.4902 48 48 48h48v48c0 26.5098 21.4902 48 48 48h320zM362 -16c3.31152 0 6 2.68848 6 6
v42h-224c-26.5098 0 -48 21.4902 -48 48v224h-42c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h308zM458 80c3.31152 0 6 2.68848 6 6v308c0 3.31152 -2.68848 6 -6 6h-308c-3.31152 0 -6 -2.68848 -6 -6v-308c0 -3.31152 2.68848 -6 6 -6h308z" />
<glyph glyph-name="hourglass" unicode="&#xf254;" horiz-adv-x="384"
d="M368 400c0 -80.0996 -31.8984 -165.619 -97.1797 -208c64.9912 -42.1934 97.1797 -127.436 97.1797 -208h4c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-360c-6.62695 0 -12 5.37305 -12 12v24c0 6.62695 5.37305 12 12 12h4
c0 80.0996 31.8994 165.619 97.1797 208c-64.9912 42.1934 -97.1797 127.436 -97.1797 208h-4c-6.62695 0 -12 5.37305 -12 12v24c0 6.62695 5.37305 12 12 12h360c6.62695 0 12 -5.37305 12 -12v-24c0 -6.62695 -5.37305 -12 -12 -12h-4zM64 400
c0 -101.621 57.3066 -184 128 -184s128 82.3799 128 184h-256zM320 -16c0 101.62 -57.3076 184 -128 184s-128 -82.3799 -128 -184h256z" />
<glyph glyph-name="hand-rock" unicode="&#xf255;"
d="M408.864 368.948c48.8213 20.751 103.136 -15.0723 103.136 -67.9111v-114.443c0 -15.3955 -3.08887 -30.3906 -9.18262 -44.5674l-42.835 -99.6562c-4.99707 -11.625 -3.98242 -18.8574 -3.98242 -42.3701c0 -17.6729 -14.3271 -32 -32 -32h-252
c-17.6729 0 -32 14.3271 -32 32c0 27.3301 1.1416 29.2012 -3.11035 32.9033l-97.71 85.0811c-24.8994 21.6797 -39.1797 52.8926 -39.1797 85.6338v56.9531c0 47.4277 44.8457 82.0215 91.0459 71.1807c1.96094 55.751 63.5107 87.8262 110.671 60.8057
c29.1895 31.0713 78.8604 31.4473 108.334 -0.0214844c32.7051 18.6846 76.4121 10.3096 98.8135 -23.5879zM464 186.594v114.445c0 34.29 -52 33.8232 -52 0.676758c0 -8.83594 -7.16309 -16 -16 -16h-7c-8.83691 0 -16 7.16406 -16 16v26.751
c0 34.457 -52 33.707 -52 0.676758v-27.4287c0 -8.83594 -7.16309 -16 -16 -16h-7c-8.83691 0 -16 7.16406 -16 16v40.4658c0 34.3525 -52 33.8115 -52 0.677734v-41.1436c0 -8.83594 -7.16406 -16 -16 -16h-7c-8.83594 0 -16 7.16406 -16 16v26.751
c0 34.4023 -52 33.7744 -52 0.676758v-116.571c0 -8.83203 -7.16797 -16 -16 -16c-3.30664 0 -8.01367 1.7627 -10.5068 3.93359l-7 6.09473c-3.03223 2.64062 -5.49316 8.04688 -5.49316 12.0674v0v41.2275c0 34.2148 -52 33.8857 -52 0.677734v-56.9531
c0 -18.8555 8.27441 -36.874 22.7002 -49.4365l97.71 -85.0801c12.4502 -10.8398 19.5898 -26.4463 19.5898 -42.8164v-10.2861h220v7.07617c0 13.21 2.65332 26.0791 7.88281 38.25l42.835 99.6553c2.91602 6.75391 5.28223 18.207 5.28223 25.5635v0.0488281z" />
<glyph glyph-name="hand-paper" unicode="&#xf256;" horiz-adv-x="448"
d="M372.57 335.359c39.9062 5.63281 75.4297 -25.7393 75.4297 -66.3594v-131.564c-0.00195312 -12.7666 -2.33008 -33.2246 -5.19531 -45.666l-30.1836 -130.958c-3.34668 -14.5234 -16.2783 -24.8125 -31.1816 -24.8125h-222.897
c-9.10352 0 -20.7793 6.01758 -26.0615 13.4316l-119.97 168.415c-21.2441 29.8203 -14.8047 71.3574 14.5498 93.1533c18.7754 13.9395 42.1309 16.2979 62.083 8.87109v126.13c0 44.0547 41.125 75.5439 82.4053 64.9834c23.8926 48.1963 92.3535 50.2471 117.982 0.74707
c42.5186 11.1445 83.0391 -21.9346 83.0391 -65.5469v-10.8242zM399.997 137.437l-0.00195312 131.563c0 24.9492 -36.5703 25.5508 -36.5703 -0.691406v-76.3086c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v154.184
c0 25.501 -36.5703 26.3633 -36.5703 0.691406v-154.875c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v188.309c0 25.501 -36.5703 26.3545 -36.5703 0.691406v-189c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16
v153.309c0 25.501 -36.5713 26.3359 -36.5713 0.691406v-206.494c0 -15.5703 -20.0352 -21.9092 -29.0303 -9.2832l-27.1279 38.0791c-14.3711 20.1709 -43.833 -2.33496 -29.3945 -22.6045l115.196 -161.697h201.92l27.3252 118.551
c2.63086 11.417 3.96484 23.1553 3.96484 34.8857z" />
<glyph glyph-name="hand-scissors" unicode="&#xf257;"
d="M256 -32c-44.9561 0 -77.3428 43.2627 -64.0244 85.8535c-21.6484 13.71 -34.0156 38.7617 -30.3408 65.0068h-87.6348c-40.8037 0 -74 32.8105 -74 73.1406c0 40.3291 33.1963 73.1396 74 73.1396l94 -9.14062l-78.8496 18.6787
c-38.3076 14.7422 -57.04 57.4707 -41.9424 95.1123c15.0303 37.4736 57.7549 55.7803 95.6416 41.2012l144.929 -55.7568c24.9551 30.5566 57.8086 43.9932 92.2178 24.7324l97.999 -54.8525c20.9746 -11.7393 34.0049 -33.8457 34.0049 -57.6904v-205.702
c0 -30.7422 -21.4404 -57.5576 -51.7979 -64.5537l-118.999 -27.4268c-4.97168 -1.14648 -10.0889 -1.72949 -15.2031 -1.72949zM256 16.0127l70 -0.000976562c1.23633 0 3.21777 0.225586 4.42285 0.501953l119.001 27.4277
c8.58203 1.97754 14.5762 9.29102 14.5762 17.7812v205.701c0 6.4873 -3.62109 12.542 -9.44922 15.8047l-98 54.8545c-8.13965 4.55566 -18.668 2.61914 -24.4873 -4.50781l-21.7646 -26.6475c-2.65039 -3.24512 -8.20215 -5.87891 -12.3926 -5.87891
c-1.64062 0 -4.21484 0.477539 -5.74609 1.06738l-166.549 64.0908c-32.6543 12.5664 -50.7744 -34.5771 -19.2227 -46.7168l155.357 -59.7852c5.66016 -2.17773 10.2539 -8.86816 10.2539 -14.9326v0v-11.6328c0 -8.83691 -7.16309 -16 -16 -16h-182
c-34.375 0 -34.4297 -50.2803 0 -50.2803h182c8.83691 0 16 -7.16309 16 -16v-6.85645c0 -8.83691 -7.16309 -16 -16 -16h-28c-25.1221 0 -25.1592 -36.5674 0 -36.5674h28c8.83691 0 16 -7.16211 16 -16v-6.85547c0 -8.83691 -7.16309 -16 -16 -16
c-25.1201 0 -25.1602 -36.5674 0 -36.5674z" />
<glyph glyph-name="hand-lizard" unicode="&#xf258;" horiz-adv-x="576"
d="M556.686 157.458c12.6357 -19.4863 19.3145 -42.0615 19.3145 -65.2871v-124.171h-224v71.582l-99.751 38.7871c-2.7832 1.08203 -5.70996 1.63086 -8.69727 1.63086h-131.552c-30.8789 0 -56 25.1211 -56 56c0 48.5234 39.4766 88 88 88h113.709l18.333 48h-196.042
c-44.1123 0 -80 35.8877 -80 80v8c0 30.8779 25.1211 56 56 56h293.917c24.5 0 47.084 -12.2725 60.4111 -32.8291zM528 16v76.1709v0.0478516c0 11.7461 -5.19141 29.2734 -11.5879 39.124l-146.358 225.715c-4.44336 6.85254 -11.9707 10.9424 -20.1367 10.9424h-293.917
c-4.41113 0 -8 -3.58887 -8 -8v-8c0 -17.6445 14.3555 -32 32 -32h213.471c25.2021 0 42.626 -25.293 33.6299 -48.8457l-24.5518 -64.2812c-7.05371 -18.4658 -25.0732 -30.873 -44.8398 -30.873h-113.709c-22.0557 0 -40 -17.9443 -40 -40c0 -4.41113 3.58887 -8 8 -8
h131.552h0.0517578c7.44141 0 19.1074 -2.19238 26.041 -4.89355l99.752 -38.7881c18.5898 -7.22852 30.6035 -24.7881 30.6035 -44.7363v-23.582h128z" />
<glyph glyph-name="hand-spock" unicode="&#xf259;"
d="M501.03 331.824c6.05762 -9.77832 10.9746 -27.0498 10.9746 -38.5518c0 -4.80664 -0.915039 -12.499 -2.04297 -17.1709l-57.623 -241.963c-12.748 -54.1729 -68.2627 -98.1387 -123.915 -98.1387h-0.345703h-107.455h-0.224609
c-33.8135 0 -81.2148 18.834 -105.807 42.041l-91.3652 85.9766c-12.8213 12.0469 -23.2266 36.1016 -23.2266 53.6943c0 16.1299 8.97266 38.7529 20.0273 50.499c5.31836 5.66406 29.875 29.3926 68.1152 21.8477l-24.3594 82.1973
c-1.68164 5.66406 -3.0459 15.0576 -3.0459 20.9668c0 37.5938 30.417 70.502 67.8955 73.4551c-0.204102 2.03125 -0.369141 5.33691 -0.369141 7.37891c0 31.627 24.8594 63.6895 55.4902 71.5684c43.248 10.9785 80.5645 -17.7012 89.6602 -53.0723l13.6836 -53.207
l4.64648 22.6602c6.76074 32.417 39.123 58.8115 72.2373 58.916c8.73438 0 56.625 -3.26953 70.7383 -54.0801c15.0664 0.710938 46.9199 -3.50977 66.3105 -35.0176zM463.271 287.219c7.86914 32.9844 -42.1211 45.2695 -50.0859 11.9219l-24.8008 -104.146
c-4.38867 -18.4141 -31.7783 -11.8926 -28.0557 6.2168l28.5479 139.166c7.39844 36.0703 -43.3076 45.0703 -50.1182 11.9629l-31.791 -154.971c-3.54883 -17.3086 -28.2832 -18.0469 -32.7109 -0.804688l-47.3262 184.035
c-8.43359 32.8105 -58.3691 20.2676 -49.8652 -12.8359l42.4414 -165.039c4.81641 -18.7207 -23.3711 -26.9121 -28.9648 -8.00781l-31.3438 105.779c-9.6875 32.6465 -59.1191 18.2578 -49.3867 -14.625l36.0137 -121.539
c5.61816 -18.9521 10.1777 -50.377 10.1777 -70.1436v-0.00878906c0 -6.54297 -8.05664 -10.9355 -13.4824 -5.82617l-51.123 48.1074c-24.7852 23.4082 -60.0527 -14.1875 -35.2793 -37.4902l91.3691 -85.9805c16.9629 -16.0068 49.6592 -28.998 72.9824 -28.998h0.154297
h107.455h0.216797c34.7402 0 69.3936 27.4443 77.3525 61.2598z" />
<glyph glyph-name="hand-pointer" unicode="&#xf25a;" horiz-adv-x="448"
d="M358.182 268.639c43.1934 16.6348 89.8184 -15.7949 89.8184 -62.6387v-84c-0.000976562 -4.25 -0.775391 -11.0615 -1.72754 -15.2041l-27.4297 -118.999c-6.98242 -30.2969 -33.7549 -51.7969 -64.5566 -51.7969h-178.286c-21.2588 0 -41.3682 10.4102 -53.791 27.8457
l-109.699 154.001c-21.2432 29.8193 -14.8047 71.3574 14.5498 93.1523c18.8115 13.9658 42.1748 16.2822 62.083 8.87207v161.129c0 36.9443 29.7363 67 66.2861 67s66.2861 -30.0557 66.2861 -67v-73.6338c20.4131 2.85742 41.4678 -3.94238 56.5947 -19.6289
c27.1934 12.8467 60.3799 5.66992 79.8721 -19.0986zM80.9854 168.303c-14.4004 20.2119 -43.8008 -2.38281 -29.3945 -22.6055l109.712 -154c3.43457 -4.81934 8.92871 -7.69727 14.6973 -7.69727h178.285c8.49219 0 15.8037 5.99414 17.7822 14.5762l27.4297 119.001
c0.333008 1.44629 0.501953 2.93457 0.501953 4.42285v84c0 25.1602 -36.5713 25.1211 -36.5713 0c0 -8.83594 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16406 -16 16v21c0 25.1602 -36.5713 25.1201 -36.5713 0v-21c0 -8.83594 -7.16309 -16 -16 -16h-6.85938
c-8.83691 0 -16 7.16406 -16 16v35c0 25.1602 -36.5703 25.1201 -36.5703 0v-35c0 -8.83594 -7.16309 -16 -16 -16h-6.85742c-8.83691 0 -16 7.16406 -16 16v175c0 25.1602 -36.5713 25.1201 -36.5713 0v-241.493c0 -15.5703 -20.0352 -21.9092 -29.0303 -9.2832z
M176.143 48v96c0 8.83691 6.26855 16 14 16h6c7.73242 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26758 -16 -14 -16h-6c-7.73242 0 -14 7.16309 -14 16zM251.571 48v96c0 8.83691 6.26758 16 14 16h6c7.73145 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26855 -16 -14 -16h-6
c-7.73242 0 -14 7.16309 -14 16zM327 48v96c0 8.83691 6.26758 16 14 16h6c7.73242 0 14 -7.16309 14 -16v-96c0 -8.83691 -6.26758 -16 -14 -16h-6c-7.73242 0 -14 7.16309 -14 16z" />
<glyph glyph-name="hand-peace" unicode="&#xf25b;" horiz-adv-x="448"
d="M362.146 256.024c42.5908 13.3184 85.8535 -19.0684 85.8535 -64.0244l-0.0117188 -70.001c-0.000976562 -4.25 -0.775391 -11.0615 -1.72949 -15.2031l-27.4268 -118.999c-6.99707 -30.3564 -33.8105 -51.7969 -64.5547 -51.7969h-205.702
c-23.8447 0 -45.9502 13.0303 -57.6904 34.0059l-54.8525 97.999c-19.2607 34.4092 -5.82422 67.2617 24.7324 92.2178l-55.7568 144.928c-14.5791 37.8867 3.72754 80.6113 41.2012 95.6416c37.6406 15.0977 80.3691 -3.63477 95.1123 -41.9424l18.6787 -78.8496
l-9.14062 94c0 40.8037 32.8096 74 73.1396 74s73.1406 -33.1963 73.1406 -74v-87.6348c26.2451 3.6748 51.2959 -8.69238 65.0068 -30.3408zM399.987 122l-0.000976562 70c0 25.1602 -36.5674 25.1201 -36.5674 0c0 -8.83691 -7.16309 -16 -16 -16h-6.85547
c-8.83789 0 -16 7.16309 -16 16v28c0 25.1592 -36.5674 25.1221 -36.5674 0v-28c0 -8.83691 -7.16309 -16 -16 -16h-6.85645c-8.83691 0 -16 7.16309 -16 16v182c0 34.4297 -50.2803 34.375 -50.2803 0v-182c0 -8.83691 -7.16309 -16 -16 -16h-11.6328v0
c-6.06445 0 -12.7549 4.59375 -14.9326 10.2539l-59.7842 155.357c-12.1396 31.5518 -59.2842 13.4326 -46.7168 -19.2227l64.0898 -166.549c0.589844 -1.53125 1.06738 -4.10547 1.06738 -5.74609c0 -4.19043 -2.63379 -9.74219 -5.87891 -12.3926l-26.6475 -21.7646
c-7.12695 -5.81934 -9.06445 -16.3467 -4.50781 -24.4873l54.8535 -98c3.26367 -5.82812 9.31934 -9.44922 15.8057 -9.44922h205.701c8.49121 0 15.8037 5.99414 17.7812 14.5762l27.4277 119.001c0.333008 1.44629 0.501953 2.93457 0.501953 4.42285z" />
<glyph glyph-name="registered" unicode="&#xf25d;"
d="M256 440c136.967 0 248 -111.033 248 -248s-111.033 -248 -248 -248s-248 111.033 -248 248s111.033 248 248 248zM256 -8c110.549 0 200 89.4678 200 200c0 110.549 -89.4678 200 -200 200c-110.549 0 -200 -89.4688 -200 -200c0 -110.549 89.4678 -200 200 -200z
M366.442 73.791c4.40332 -7.99219 -1.37012 -17.791 -10.5107 -17.791h-42.8096h-0.0126953c-3.97559 0 -8.71582 2.84961 -10.5801 6.36035l-47.5156 89.3027h-31.958v-83.6631c0 -6.61719 -5.38281 -12 -12 -12h-38.5674c-6.61719 0 -12 5.38281 -12 12v248.304
c0 6.61719 5.38281 12 12 12h78.667c71.251 0 101.498 -32.749 101.498 -85.252c0 -31.6123 -15.2148 -59.2969 -39.4824 -73.1758c3.02148 -4.61719 0.225586 0.199219 53.2715 -96.085zM256.933 208.094c20.9131 0 32.4307 11.5186 32.4316 32.4316
c0 19.5752 -6.5127 31.709 -38.9297 31.709h-27.377v-64.1406h33.875z" />
<glyph glyph-name="calendar-plus" unicode="&#xf271;" horiz-adv-x="448"
d="M336 156v-24c0 -6.59961 -5.40039 -12 -12 -12h-76v-76c0 -6.59961 -5.40039 -12 -12 -12h-24c-6.59961 0 -12 5.40039 -12 12v76h-76c-6.59961 0 -12 5.40039 -12 12v24c0 6.59961 5.40039 12 12 12h76v76c0 6.59961 5.40039 12 12 12h24c6.59961 0 12 -5.40039 12 -12
v-76h76c6.59961 0 12 -5.40039 12 -12zM448 336v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40
c6.59961 0 12 -5.40039 12 -12v-52h48c26.5 0 48 -21.5 48 -48zM400 -10v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="calendar-minus" unicode="&#xf272;" horiz-adv-x="448"
d="M124 120c-6.59961 0 -12 5.40039 -12 12v24c0 6.59961 5.40039 12 12 12h200c6.59961 0 12 -5.40039 12 -12v-24c0 -6.59961 -5.40039 -12 -12 -12h-200zM448 336v-352c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52
c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h48c26.5 0 48 -21.5 48 -48zM400 -10v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="calendar-times" unicode="&#xf273;" horiz-adv-x="448"
d="M311.7 73.2998l-17 -17c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-53.7002 53.7998l-53.7002 -53.6992c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-17 17c-4.7002 4.69922 -4.7002 12.2998 0 17l53.7002 53.6992l-53.7002 53.7002c-4.7002 4.7002 -4.7002 12.2998 0 17
l17 17c4.7002 4.7002 12.2998 4.7002 17 0l53.7002 -53.7002l53.7002 53.7002c4.7002 4.7002 12.2998 4.7002 17 0l17 -17c4.7002 -4.7002 4.7002 -12.2998 0 -17l-53.7998 -53.7998l53.6992 -53.7002c4.80078 -4.7002 4.80078 -12.2998 0.100586 -17zM448 336v-352
c0 -26.5 -21.5 -48 -48 -48h-352c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h48v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h128v52c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-52h48c26.5 0 48 -21.5 48 -48zM400 -10
v298h-352v-298c0 -3.2998 2.7002 -6 6 -6h340c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="calendar-check" unicode="&#xf274;" horiz-adv-x="448"
d="M400 384c26.5098 0 48 -21.4902 48 -48v-352c0 -26.5098 -21.4902 -48 -48 -48h-352c-26.5098 0 -48 21.4902 -48 48v352c0 26.5098 21.4902 48 48 48h48v52c0 6.62695 5.37305 12 12 12h40c6.62695 0 12 -5.37305 12 -12v-52h128v52c0 6.62695 5.37305 12 12 12h40
c6.62695 0 12 -5.37305 12 -12v-52h48zM394 -16c3.31152 0 6 2.68848 6 6v298h-352v-298c0 -3.31152 2.68848 -6 6 -6h340zM341.151 184.65l-142.31 -141.169c-4.70508 -4.66699 -12.3027 -4.6377 -16.9707 0.0673828l-75.0908 75.6992
c-4.66699 4.70508 -4.6377 12.3027 0.0673828 16.9707l22.7197 22.5361c4.70508 4.66699 12.3027 4.63672 16.9697 -0.0693359l44.1035 -44.4609l111.072 110.182c4.70508 4.66699 12.3027 4.63672 16.9707 -0.0683594l22.5361 -22.7178
c4.66699 -4.70508 4.63672 -12.3027 -0.0683594 -16.9697z" />
<glyph glyph-name="map" unicode="&#xf279;" horiz-adv-x="576"
d="M560.02 416c8.4502 0 15.9805 -6.83008 15.9805 -16.0195v-346.32c0 -11.9609 -9.01367 -25.2705 -20.1201 -29.71l-151.83 -52.8105c-5.32617 -1.7334 -14.1953 -3.13965 -19.7969 -3.13965c-5.7373 0 -14.8105 1.47363 -20.2529 3.29004l-172 60.71l-170.05 -62.8398
c-1.99023 -0.790039 -4 -1.16016 -5.95996 -1.16016c-8.45996 0 -15.9902 6.83008 -15.9902 16.0195v346.32c0.00292969 11.959 9.0166 25.2686 20.1201 29.71l151.83 52.8105c6.43945 2.08984 13.1201 3.13965 19.8096 3.13965
c5.73242 -0.00195312 14.8008 -1.47168 20.2402 -3.28027l172 -60.7197h0.00976562l170.05 62.8398c1.98047 0.790039 4 1.16016 5.95996 1.16016zM224 357.58v-285.97l128 -45.1904v285.97zM48 29.9502l127.36 47.0801l0.639648 0.229492v286.2l-128 -44.5303v-288.979z
M528 65.0801v288.97l-127.36 -47.0693l-0.639648 -0.240234v-286.19z" />
<glyph glyph-name="comment-alt" unicode="&#xf27a;"
d="M448 448c35.2998 0 64 -28.7002 64 -64v-288c0 -35.2998 -28.7002 -64 -64 -64h-144l-124.9 -93.5996c-2.19922 -1.7002 -4.69922 -2.40039 -7.09961 -2.40039c-6.2002 0 -12 4.90039 -12 12v84h-96c-35.2998 0 -64 28.7002 -64 64v288c0 35.2998 28.7002 64 64 64h384z
M464 96v288c0 8.7998 -7.2002 16 -16 16h-384c-8.7998 0 -16 -7.2002 -16 -16v-288c0 -8.7998 7.2002 -16 16 -16h144v-60l67.2002 50.4004l12.7998 9.59961h160c8.7998 0 16 7.2002 16 16z" />
<glyph glyph-name="pause-circle" unicode="&#xf28b;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200zM352 272v-160c0 -8.7998 -7.2002 -16 -16 -16h-48
c-8.7998 0 -16 7.2002 -16 16v160c0 8.7998 7.2002 16 16 16h48c8.7998 0 16 -7.2002 16 -16zM240 272v-160c0 -8.7998 -7.2002 -16 -16 -16h-48c-8.7998 0 -16 7.2002 -16 16v160c0 8.7998 7.2002 16 16 16h48c8.7998 0 16 -7.2002 16 -16z" />
<glyph glyph-name="stop-circle" unicode="&#xf28d;"
d="M504 192c0 -137 -111 -248 -248 -248s-248 111 -248 248s111 248 248 248s248 -111 248 -248zM56 192c0 -110.5 89.5 -200 200 -200s200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200zM352 272v-160c0 -8.7998 -7.2002 -16 -16 -16h-160
c-8.7998 0 -16 7.2002 -16 16v160c0 8.7998 7.2002 16 16 16h160c8.7998 0 16 -7.2002 16 -16z" />
<glyph glyph-name="handshake" unicode="&#xf2b5;" horiz-adv-x="640"
d="M519.2 320.1h120.8v-255.699h-64c-17.5 0 -31.7998 14.1992 -31.9004 31.6992h-57.8994c-1.7998 -8.19922 -5.2998 -16.0996 -10.9004 -23l-26.2002 -32.2998c-15.7998 -19.3994 -41.8994 -25.5 -64 -16.7998c-13.5 -16.5996 -30.5996 -24 -48.7998 -24
c-15.0996 0 -28.5996 5.09961 -41.0996 15.9004c-31.7998 -21.9004 -74.7002 -21.3008 -105.601 3.7998l-84.5996 76.3994h-9.09961c-0.100586 -17.5 -14.3008 -31.6992 -31.9004 -31.6992h-64v255.699h118l47.5996 47.6006c10.5 10.3994 24.8008 16.2998 39.6006 16.2998
h226.8v0c12.7812 0 30.5225 -7.30273 39.5996 -16.2998zM48 96.4004c8.7998 0 16 7.09961 16 16c0 8.7998 -7.2002 16 -16 16s-16 -7.2002 -16 -16c0 -8.80078 7.2002 -16 16 -16zM438 103.3c2.7002 3.40039 2.2002 8.5 -1.2002 11.2998l-108.2 87.8008l-8.19922 -7.5
c-40.3008 -36.8008 -86.7002 -11.8008 -101.5 4.39941c-26.7002 29 -25 74.4004 4.39941 101.3l38.7002 35.5h-56.7002c-2 -0.799805 -3.7002 -1.5 -5.7002 -2.2998l-61.6992 -61.5996h-41.9004v-128.101h27.7002l97.2998 -88
c16.0996 -13.0996 41.4004 -10.5 55.2998 6.60059l15.6006 19.2002l36.7998 -31.5c3 -2.40039 12 -4.90039 18 2.39941l30 36.5l23.8994 -19.3994c3.5 -2.80078 8.5 -2.2002 11.3008 1.19922zM544 144.1v128h-44.7002l-61.7002 61.6006
c-1.39941 1.5 -3.39941 2.2998 -5.5 2.2998l-83.6992 -0.200195c-10 0 -19.6006 -3.7002 -27 -10.5l-65.6006 -60.0996c-9.7002 -8.7998 -10.5 -24 -1.2002 -33.9004c8.90039 -9.39941 25.1006 -8.7002 34.6006 0l55.2002 50.6006c6.5 5.89941 16.5996 5.5 22.5996 -1
l10.9004 -11.7002c6 -6.5 5.5 -16.6006 -1 -22.6006l-12.5 -11.3994l102.699 -83.4004c2.80078 -2.2998 5.40039 -4.89941 7.7002 -7.7002h69.2002zM592 96.4004c8.7998 0 16 7.09961 16 16c0 8.7998 -7.2002 16 -16 16s-16 -7.2002 -16 -16c0 -8.80078 7.2002 -16 16 -16z
" />
<glyph glyph-name="envelope-open" unicode="&#xf2b6;"
d="M494.586 283.484c9.6123 -7.94824 17.4141 -24.5205 17.4141 -36.9932v-262.491c0 -26.5098 -21.4902 -48 -48 -48h-416c-26.5098 0 -48 21.4902 -48 48v262.515c0 12.5166 7.84668 29.1279 17.5146 37.0771c4.08008 3.35449 110.688 89.0996 135.15 108.549
c22.6992 18.1426 60.1299 55.8594 103.335 55.8594c43.4365 0 81.2314 -38.1914 103.335 -55.8594c23.5283 -18.707 130.554 -104.773 135.251 -108.656zM464 -10v253.632v0.00488281c0 1.5791 -0.996094 3.66602 -2.22363 4.6582
c-15.8633 12.8232 -108.793 87.5752 -132.366 106.316c-17.5527 14.0195 -49.7168 45.3887 -73.4102 45.3887c-23.6016 0 -55.2451 -30.8799 -73.4102 -45.3887c-23.5713 -18.7393 -116.494 -93.4795 -132.364 -106.293
c-1.40918 -1.13965 -2.22559 -2.85254 -2.22559 -4.66504v-253.653c0 -3.31152 2.68848 -6 6 -6h404c3.31152 0 6 2.68848 6 6zM432.009 177.704c4.24902 -5.15918 3.46484 -12.7949 -1.74512 -16.9814c-28.9746 -23.2822 -59.2734 -47.5967 -70.9287 -56.8623
c-22.6992 -18.1436 -60.1299 -55.8604 -103.335 -55.8604c-43.4521 0 -81.2871 38.2373 -103.335 55.8604c-11.2793 8.9668 -41.7441 33.4131 -70.9268 56.8643c-5.20996 4.1875 -5.99316 11.8223 -1.74512 16.9814l15.2578 18.5283
c4.17773 5.07227 11.6572 5.84277 16.7793 1.72559c28.6182 -23.001 58.5654 -47.0352 70.5596 -56.5713c17.5527 -14.0195 49.7168 -45.3887 73.4102 -45.3887c23.6016 0 55.2461 30.8799 73.4102 45.3887c11.9941 9.53516 41.9434 33.5703 70.5625 56.5684
c5.12207 4.11621 12.6016 3.3457 16.7783 -1.72656z" />
<glyph glyph-name="address-book" unicode="&#xf2b9;" horiz-adv-x="448"
d="M436 288h-20v-64h20c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-20v-64h20c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12h-20v-48c0 -26.5 -21.5 -48 -48 -48h-320c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48
h320c26.5 0 48 -21.5 48 -48v-48h20c6.59961 0 12 -5.40039 12 -12v-40c0 -6.59961 -5.40039 -12 -12 -12zM368 -16v416h-320v-416h320zM208 192c-35.2998 0 -64 28.7002 -64 64s28.7002 64 64 64s64 -28.7002 64 -64s-28.7002 -64 -64 -64zM118.4 64
c-12.4004 0 -22.4004 8.59961 -22.4004 19.2002v19.2002c0 31.7998 30.0996 57.5996 67.2002 57.5996c11.3994 0 17.8994 -8 44.7998 -8c26.0996 0 34 8 44.7998 8c37.1006 0 67.2002 -25.7998 67.2002 -57.5996v-19.2002c0 -10.6006 -10 -19.2002 -22.4004 -19.2002
h-179.199z" />
<glyph glyph-name="address-card" unicode="&#xf2bb;" horiz-adv-x="576"
d="M528 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-480c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h480zM528 16v352h-480v-352h480zM208 192c-35.2998 0 -64 28.7002 -64 64s28.7002 64 64 64s64 -28.7002 64 -64s-28.7002 -64 -64 -64z
M118.4 64c-12.4004 0 -22.4004 8.59961 -22.4004 19.2002v19.2002c0 31.7998 30.0996 57.5996 67.2002 57.5996c11.3994 0 17.8994 -8 44.7998 -8c26.0996 0 34 8 44.7998 8c37.1006 0 67.2002 -25.7998 67.2002 -57.5996v-19.2002
c0 -10.6006 -10 -19.2002 -22.4004 -19.2002h-179.199zM360 128c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112zM360 192c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112
c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112zM360 256c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112z" />
<glyph glyph-name="user-circle" unicode="&#xf2bd;" horiz-adv-x="496"
d="M248 344c53 0 96 -43 96 -96s-43 -96 -96 -96s-96 43 -96 96s43 96 96 96zM248 200c26.5 0 48 21.5 48 48s-21.5 48 -48 48s-48 -21.5 -48 -48s21.5 -48 48 -48zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8
c49.7002 0 95.0996 18.2998 130.1 48.4004c-14.8994 23 -40.3994 38.5 -69.5996 39.5c-20.7998 -6.5 -40.5996 -9.60059 -60.5 -9.60059s-39.7002 3.2002 -60.5 9.60059c-29.2002 -0.900391 -54.7002 -16.5 -69.5996 -39.5c35 -30.1006 80.3994 -48.4004 130.1 -48.4004z
M410.7 76.0996c23.3994 32.7002 37.2998 72.7002 37.2998 115.9c0 110.3 -89.7002 200 -200 200s-200 -89.7002 -200 -200c0 -43.2002 13.9004 -83.2002 37.2998 -115.9c24.5 31.4004 62.2002 51.9004 105.101 51.9004c10.1992 0 26.0996 -9.59961 57.5996 -9.59961
c31.5996 0 47.4004 9.59961 57.5996 9.59961c43 0 80.7002 -20.5 105.101 -51.9004z" />
<glyph glyph-name="id-badge" unicode="&#xf2c1;" horiz-adv-x="384"
d="M336 448c26.5 0 48 -21.5 48 -48v-416c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v416c0 26.5 21.5 48 48 48h288zM336 -16v416h-288v-416h288zM144 336c-8.7998 0 -16 7.2002 -16 16s7.2002 16 16 16h96c8.7998 0 16 -7.2002 16 -16s-7.2002 -16 -16 -16
h-96zM192 160c-35.2998 0 -64 28.7002 -64 64s28.7002 64 64 64s64 -28.7002 64 -64s-28.7002 -64 -64 -64zM102.4 32c-12.4004 0 -22.4004 8.59961 -22.4004 19.2002v19.2002c0 31.7998 30.0996 57.5996 67.2002 57.5996c11.3994 0 17.8994 -8 44.7998 -8
c26.0996 0 34 8 44.7998 8c37.1006 0 67.2002 -25.7998 67.2002 -57.5996v-19.2002c0 -10.6006 -10 -19.2002 -22.4004 -19.2002h-179.199z" />
<glyph glyph-name="id-card" unicode="&#xf2c2;" horiz-adv-x="576"
d="M528 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-480c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h480zM528 16v288h-480v-288h32.7998c-1 4.5 -0.799805 -3.59961 -0.799805 22.4004c0 31.7998 30.0996 57.5996 67.2002 57.5996
c11.3994 0 17.8994 -8 44.7998 -8c26.0996 0 34 8 44.7998 8c37.1006 0 67.2002 -25.7998 67.2002 -57.5996c0 -26 0.0996094 -17.9004 -0.799805 -22.4004h224.8zM360 96c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112c4.40039 0 8 -3.59961 8 -8v-16
c0 -4.40039 -3.59961 -8 -8 -8h-112zM360 160c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112zM360 224c-4.40039 0 -8 3.59961 -8 8v16c0 4.40039 3.59961 8 8 8h112
c4.40039 0 8 -3.59961 8 -8v-16c0 -4.40039 -3.59961 -8 -8 -8h-112zM192 128c-35.2998 0 -64 28.7002 -64 64s28.7002 64 64 64s64 -28.7002 64 -64s-28.7002 -64 -64 -64z" />
<glyph glyph-name="window-maximize" unicode="&#xf2d0;"
d="M464 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-416c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h416zM464 22v234h-416v-234c0 -3.2998 2.7002 -6 6 -6h404c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="window-minimize" unicode="&#xf2d1;"
d="M480 -32h-448c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32h448c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32z" />
<glyph glyph-name="window-restore" unicode="&#xf2d2;"
d="M464 448c26.5 0 48 -21.5 48 -48v-320c0 -26.5 -21.5 -48 -48 -48h-48v-48c0 -26.5 -21.5 -48 -48 -48h-320c-26.5 0 -48 21.5 -48 48v320c0 26.5 21.5 48 48 48h48v48c0 26.5 21.5 48 48 48h320zM368 -16v208h-320v-208h320zM464 80v320h-320v-48h224
c26.5 0 48 -21.5 48 -48v-224h48z" />
<glyph glyph-name="snowflake" unicode="&#xf2dc;" horiz-adv-x="448"
d="M440.1 92.7998c7.60059 -4.39941 10.1006 -14.2002 5.5 -21.7002l-7.89941 -13.8994c-4.40039 -7.7002 -14 -10.2998 -21.5 -5.90039l-39.2002 23l9.09961 -34.7002c2.30078 -8.5 -2.69922 -17.2998 -11.0996 -19.5996l-15.2002 -4.09961
c-8.39941 -2.30078 -17.0996 2.7998 -19.2998 11.2998l-21.2998 81l-71.9004 42.2002v-84.5l58.2998 -59.3008c6.10059 -6.19922 6.10059 -16.3994 0 -22.5996l-11.0996 -11.2998c-6.09961 -6.2002 -16.0996 -6.2002 -22.2002 0l-24.8994 25.3994v-46.0996
c0 -8.7998 -7 -16 -15.7002 -16h-15.7002c-8.7002 0 -15.7002 7.2002 -15.7002 16v45.9004l-24.8994 -25.4004c-6.10059 -6.2002 -16.1006 -6.2002 -22.2002 0l-11.1006 11.2998c-6.09961 6.2002 -6.09961 16.4004 0 22.6006l58.3008 59.2998v84.5l-71.9004 -42.2002
l-21.2998 -81c-2.2998 -8.5 -10.9004 -13.5996 -19.2998 -11.2998l-15.2002 4.09961c-8.40039 2.2998 -13.2998 11.1006 -11.1006 19.6006l9.10059 34.6992l-39.2002 -23c-7.5 -4.39941 -17.2002 -1.7998 -21.5 5.90039l-7.90039 13.9004
c-4.2998 7.69922 -1.69922 17.5 5.80078 21.8994l39.1992 23l-34.0996 9.2998c-8.40039 2.30078 -13.2998 11.1006 -11.0996 19.6006l4.09961 15.5c2.2998 8.5 10.9004 13.5996 19.2998 11.2998l79.7002 -21.7002l71.9004 42.2002l-71.9004 42.2002l-79.7002 -21.7002
c-8.39941 -2.2998 -17.0996 2.7998 -19.2998 11.2998l-4.09961 15.5c-2.30078 8.5 2.69922 17.2998 11.0996 19.6006l34.0996 9.09961l-39.1992 23c-7.60059 4.5 -10.1006 14.2002 -5.80078 21.9004l7.90039 13.8994c4.40039 7.7002 14 10.2998 21.5 5.90039l39.2002 -23
l-9.10059 34.7002c-2.2998 8.5 2.7002 17.2998 11.1006 19.5996l15.2002 4.09961c8.39941 2.30078 17.0996 -2.7998 19.2998 -11.2998l21.2998 -81l71.9004 -42.2002v84.5l-58.3008 59.3008c-6.09961 6.19922 -6.09961 16.3994 0 22.5996l11.5 11.2998
c6.10059 6.2002 16.1006 6.2002 22.2002 0l24.9004 -25.3994v46.0996c0 8.7998 7 16 15.7002 16h15.6992c8.7002 0 15.7002 -7.2002 15.7002 -16v-45.9004l24.9004 25.4004c6.09961 6.2002 16.0996 6.2002 22.2002 0l11.0996 -11.2998
c6.09961 -6.2002 6.09961 -16.4004 0 -22.6006l-58.2998 -59.2998v-84.5l71.8994 42.2002l21.3008 81c2.2998 8.5 10.8994 13.5996 19.2998 11.2998l15.2002 -4.09961c8.39941 -2.2998 13.2998 -11.1006 11.0996 -19.6006l-9.09961 -34.6992l39.1992 23
c7.5 4.39941 17.2002 1.7998 21.5 -5.90039l7.90039 -13.9004c4.2998 -7.69922 1.7002 -17.5 -5.7998 -21.8994l-39.2002 -23l34.0996 -9.2998c8.40039 -2.30078 13.3008 -11.1006 11.1006 -19.6006l-4.10059 -15.5c-2.2998 -8.5 -10.8994 -13.5996 -19.2998 -11.2998
l-79.7002 21.7002l-71.8994 -42.2002l71.7998 -42.2002l79.7002 21.7002c8.39941 2.2998 17.0996 -2.7998 19.2998 -11.2998l4.09961 -15.5c2.30078 -8.5 -2.69922 -17.2998 -11.0996 -19.6006l-34.0996 -9.2998z" />
<glyph glyph-name="trash-alt" unicode="&#xf2ed;" horiz-adv-x="448"
d="M268 32c-6.62402 0 -12 5.37598 -12 12v216c0 6.62402 5.37598 12 12 12h24c6.62402 0 12 -5.37598 12 -12v-216c0 -6.62402 -5.37598 -12 -12 -12h-24zM432 368c8.83203 0 16 -7.16797 16 -16v-16c0 -8.83203 -7.16797 -16 -16 -16h-16v-336
c0 -26.4961 -21.5039 -48 -48 -48h-288c-26.4961 0 -48 21.5039 -48 48v336h-16c-8.83203 0 -16 7.16797 -16 16v16c0 8.83203 7.16797 16 16 16h82.4102l34.0195 56.7002c7.71875 12.8613 26.1572 23.2998 41.1572 23.2998h0.00292969h100.82h0.0224609
c15 0 33.4385 -10.4385 41.1572 -23.2998l34 -56.7002h82.4102zM171.84 397.09l-17.4502 -29.0898h139.221l-17.46 29.0898c-0.96582 1.60645 -3.26953 2.91016 -5.14355 2.91016h-0.00683594h-94h-0.0166016c-1.87402 0 -4.17871 -1.30371 -5.14355 -2.91016zM368 -16v336
h-288v-336h288zM156 32c-6.62402 0 -12 5.37598 -12 12v216c0 6.62402 5.37598 12 12 12h24c6.62402 0 12 -5.37598 12 -12v-216c0 -6.62402 -5.37598 -12 -12 -12h-24z" />
<glyph glyph-name="images" unicode="&#xf302;" horiz-adv-x="576"
d="M480 32v-16c0 -26.5098 -21.4902 -48 -48 -48h-384c-26.5098 0 -48 21.4902 -48 48v256c0 26.5098 21.4902 48 48 48h16v-48h-10c-3.31152 0 -6 -2.68848 -6 -6v-244c0 -3.31152 2.68848 -6 6 -6h372c3.31152 0 6 2.68848 6 6v10h48zM522 368h-372
c-3.31152 0 -6 -2.68848 -6 -6v-244c0 -3.31152 2.68848 -6 6 -6h372c3.31152 0 6 2.68848 6 6v244c0 3.31152 -2.68848 6 -6 6zM528 416c26.5098 0 48 -21.4902 48 -48v-256c0 -26.5098 -21.4902 -48 -48 -48h-384c-26.5098 0 -48 21.4902 -48 48v256
c0 26.5098 21.4902 48 48 48h384zM264 304c0 -22.0908 -17.9092 -40 -40 -40s-40 17.9092 -40 40s17.9092 40 40 40s40 -17.9092 40 -40zM192 208l39.5146 39.5146c4.68652 4.68652 12.2842 4.68652 16.9717 0l39.5137 -39.5146l103.515 103.515
c4.68652 4.68652 12.2842 4.68652 16.9717 0l71.5137 -71.5146v-80h-288v48z" />
<glyph glyph-name="clipboard" unicode="&#xf328;" horiz-adv-x="384"
d="M336 384c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-288c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h80c0 35.2998 28.7002 64 64 64s64 -28.7002 64 -64h80zM192 408c-13.2998 0 -24 -10.7002 -24 -24s10.7002 -24 24 -24s24 10.7002 24 24
s-10.7002 24 -24 24zM336 -10v340c0 3.2998 -2.7002 6 -6 6h-42v-36c0 -6.59961 -5.40039 -12 -12 -12h-168c-6.59961 0 -12 5.40039 -12 12v36h-42c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h276c3.2998 0 6 2.7002 6 6z" />
<glyph glyph-name="arrow-alt-circle-down" unicode="&#xf358;"
d="M256 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM256 -8c110.5 0 200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200zM224 308c0 6.59961 5.40039 12 12 12h40c6.59961 0 12 -5.40039 12 -12v-116
h67c10.7002 0 16.0996 -12.9004 8.5 -20.5l-99 -99c-4.7002 -4.7002 -12.2998 -4.7002 -17 0l-99 99c-7.5 7.59961 -2.2002 20.5 8.5 20.5h67v116z" />
<glyph glyph-name="arrow-alt-circle-left" unicode="&#xf359;"
d="M8 192c0 137 111 248 248 248s248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248zM456 192c0 110.5 -89.5 200 -200 200s-200 -89.5 -200 -200s89.5 -200 200 -200s200 89.5 200 200zM384 212v-40c0 -6.59961 -5.40039 -12 -12 -12h-116v-67
c0 -10.7002 -12.9004 -16 -20.5 -8.5l-99 99c-4.7002 4.7002 -4.7002 12.2998 0 17l99 99c7.59961 7.59961 20.5 2.2002 20.5 -8.5v-67h116c6.59961 0 12 -5.40039 12 -12z" />
<glyph glyph-name="arrow-alt-circle-right" unicode="&#xf35a;"
d="M504 192c0 -137 -111 -248 -248 -248s-248 111 -248 248s111 248 248 248s248 -111 248 -248zM56 192c0 -110.5 89.5 -200 200 -200s200 89.5 200 200s-89.5 200 -200 200s-200 -89.5 -200 -200zM128 172v40c0 6.59961 5.40039 12 12 12h116v67
c0 10.7002 12.9004 16 20.5 8.5l99 -99c4.7002 -4.7002 4.7002 -12.2998 0 -17l-99 -99c-7.59961 -7.59961 -20.5 -2.2002 -20.5 8.5v67h-116c-6.59961 0 -12 5.40039 -12 12z" />
<glyph glyph-name="arrow-alt-circle-up" unicode="&#xf35b;"
d="M256 -56c-137 0 -248 111 -248 248s111 248 248 248s248 -111 248 -248s-111 -248 -248 -248zM256 392c-110.5 0 -200 -89.5 -200 -200s89.5 -200 200 -200s200 89.5 200 200s-89.5 200 -200 200zM276 64h-40c-6.59961 0 -12 5.40039 -12 12v116h-67
c-10.7002 0 -16 12.9004 -8.5 20.5l99 99c4.7002 4.7002 12.2998 4.7002 17 0l99 -99c7.59961 -7.59961 2.2002 -20.5 -8.5 -20.5h-67v-116c0 -6.59961 -5.40039 -12 -12 -12z" />
<glyph glyph-name="gem" unicode="&#xf3a5;" horiz-adv-x="576"
d="M464 448c4.09961 0 7.7998 -2 10.0996 -5.40039l99.9004 -147.199c2.90039 -4.40039 2.59961 -10.1006 -0.700195 -14.2002l-276 -340.8c-4.7998 -5.90039 -13.7998 -5.90039 -18.5996 0l-276 340.8c-3.2998 4 -3.60059 9.7998 -0.700195 14.2002l100 147.199
c2.2002 3.40039 6 5.40039 10 5.40039h352zM444.7 400h-56.7998l51.6992 -96h68.4004zM242.6 400l-51.5996 -96h194l-51.7002 96h-90.7002zM131.3 400l-63.2998 -96h68.4004l51.6992 96h-56.7998zM88.2998 256l119.7 -160l-68.2998 160h-51.4004zM191.2 256l96.7998 -243.3
l96.7998 243.3h-193.6zM368 96l119.6 160h-51.3994z" />
<glyph glyph-name="money-bill-alt" unicode="&#xf3d1;" horiz-adv-x="640"
d="M320 304c53.0195 0 96 -50.1396 96 -112c0 -61.8701 -43 -112 -96 -112c-53.0195 0 -96 50.1504 -96 112c0 61.8604 42.9805 112 96 112zM360 136v16c0 4.41992 -3.58008 8 -8 8h-16v88c0 4.41992 -3.58008 8 -8 8h-13.5801h-0.000976562
c-4.01074 0 -9.97266 -1.80566 -13.3086 -4.03027l-15.3301 -10.2197c-1.96777 -1.30957 -3.56445 -4.29004 -3.56445 -6.65332c0 -1.33691 0.601562 -3.32422 1.34375 -4.43652l8.88086 -13.3105c1.30859 -1.9668 4.29004 -3.56445 6.65332 -3.56445
c1.33691 0 3.32422 0.602539 4.43652 1.34473l0.469727 0.310547v-55.4404h-16c-4.41992 0 -8 -3.58008 -8 -8v-16c0 -4.41992 3.58008 -8 8 -8h64c4.41992 0 8 3.58008 8 8zM608 384c17.6699 0 32 -14.3301 32 -32v-320c0 -17.6699 -14.3301 -32 -32 -32h-576
c-17.6699 0 -32 14.3301 -32 32v320c0 17.6699 14.3301 32 32 32h576zM592 112v160c-35.3496 0 -64 28.6504 -64 64h-416c0 -35.3496 -28.6504 -64 -64 -64v-160c35.3496 0 64 -28.6504 64 -64h416c0 35.3496 28.6504 64 64 64z" />
<glyph glyph-name="window-close" unicode="&#xf410;"
d="M464 416c26.5 0 48 -21.5 48 -48v-352c0 -26.5 -21.5 -48 -48 -48h-416c-26.5 0 -48 21.5 -48 48v352c0 26.5 21.5 48 48 48h416zM464 22v340c0 3.2998 -2.7002 6 -6 6h-404c-3.2998 0 -6 -2.7002 -6 -6v-340c0 -3.2998 2.7002 -6 6 -6h404c3.2998 0 6 2.7002 6 6z
M356.5 253.4l-61.4004 -61.4004l61.4004 -61.4004c4.59961 -4.59961 4.59961 -12.0996 0 -16.7998l-22.2998 -22.2998c-4.60059 -4.59961 -12.1006 -4.59961 -16.7998 0l-61.4004 61.4004l-61.4004 -61.4004c-4.59961 -4.59961 -12.0996 -4.59961 -16.7998 0
l-22.2998 22.2998c-4.59961 4.60059 -4.59961 12.1006 0 16.7998l61.4004 61.4004l-61.4004 61.4004c-4.59961 4.59961 -4.59961 12.0996 0 16.7998l22.2998 22.2998c4.60059 4.59961 12.1006 4.59961 16.7998 0l61.4004 -61.4004l61.4004 61.4004
c4.59961 4.59961 12.0996 4.59961 16.7998 0l22.2998 -22.2998c4.7002 -4.60059 4.7002 -12.1006 0 -16.7998z" />
<glyph glyph-name="comment-dots" unicode="&#xf4ad;"
d="M144 240c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM256 240c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM368 240c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32
s-32 14.2998 -32 32s14.2998 32 32 32zM256 416c141.4 0 256 -93.0996 256 -208s-114.6 -208 -256 -208c-32.7998 0 -64 5.2002 -92.9004 14.2998c-29.0996 -20.5996 -77.5996 -46.2998 -139.1 -46.2998c-9.59961 0 -18.2998 5.7002 -22.0996 14.5
c-3.80078 8.7998 -2 19 4.59961 26c0.5 0.400391 31.5 33.7998 46.4004 73.2002c-33 35.0996 -52.9004 78.7002 -52.9004 126.3c0 114.9 114.6 208 256 208zM256 48c114.7 0 208 71.7998 208 160s-93.2998 160 -208 160s-208 -71.7998 -208 -160
c0 -42.2002 21.7002 -74.0996 39.7998 -93.4004l20.6006 -21.7998l-10.6006 -28.0996c-5.5 -14.5 -12.5996 -28.1006 -19.8994 -40.2002c23.5996 7.59961 43.1992 18.9004 57.5 29l19.5 13.7998l22.6992 -7.2002c25.3008 -8 51.7002 -12.0996 78.4004 -12.0996z" />
<glyph glyph-name="smile-wink" unicode="&#xf4da;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM365.8 138.4c10.2002 -8.5 11.6006 -23.6006 3.10059 -33.8008
c-30 -36 -74.1006 -56.5996 -120.9 -56.5996s-90.9004 20.5996 -120.9 56.5996c-8.39941 10.2002 -7.09961 25.3008 3.10059 33.8008c10.0996 8.39941 25.2998 7.09961 33.7998 -3.10059c20.7998 -25.0996 51.5 -39.3994 84 -39.3994s63.2002 14.3994 84 39.3994
c8.5 10.2002 23.5996 11.6006 33.7998 3.10059zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 268c25.7002 0 55.9004 -16.9004 59.7002 -42.0996c1.7998 -11.1006 -11.2998 -18.2002 -19.7998 -10.8008l-9.5 8.5
c-14.8008 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5c-8.30078 -7.39941 -21.5 -0.399414 -19.8008 10.8008c4 25.1992 34.2002 42.0996 59.9004 42.0996z" />
<glyph glyph-name="angry" unicode="&#xf556;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM248 136c33.5996 0 65.2002 -14.7998 86.7998 -40.5996
c8.40039 -10.2002 7.10059 -25.3008 -3.09961 -33.8008c-10.6006 -8.89941 -25.7002 -6.69922 -33.7998 3c-24.8008 29.7002 -75 29.7002 -99.8008 0c-8.5 -10.1992 -23.5996 -11.5 -33.7998 -3s-11.5996 23.6006 -3.09961 33.8008
c21.5996 25.7998 53.2002 40.5996 86.7998 40.5996zM200 208c0 -17.7002 -14.2998 -32.0996 -32 -32.0996s-32 14.2998 -32 32c0 6.19922 2.2002 11.6992 5.2998 16.5996l-28.2002 8.5c-12.6992 3.7998 -19.8994 17.2002 -16.0996 29.9004
c3.7998 12.6992 17.0996 20 29.9004 16.0996l80 -24c12.6992 -3.7998 19.8994 -17.2002 16.0996 -29.9004c-3.09961 -10.3994 -12.7002 -17.0996 -23 -17.0996zM399 262.9c3.7998 -12.7002 -3.40039 -26.1006 -16.0996 -29.8008l-28.2002 -8.5
c3.09961 -4.89941 5.2998 -10.3994 5.2998 -16.5996c0 -17.7002 -14.2998 -32 -32 -32s-32 14.2998 -32 32c-10.2998 0 -19.9004 6.7002 -23 17.0996c-3.7998 12.7002 3.40039 26.1006 16.0996 29.9004l80 24c12.8008 3.7998 26.1006 -3.40039 29.9004 -16.0996z" />
<glyph glyph-name="dizzy" unicode="&#xf567;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM214.2 209.9
c-7.90039 -7.90039 -20.5 -7.90039 -28.4004 -0.200195l-17.7998 17.7998l-17.7998 -17.7998c-7.7998 -7.7998 -20.5 -7.7998 -28.2998 0c-7.80078 7.7998 -7.80078 20.5 0 28.2998l17.8994 17.9004l-17.8994 17.8994c-7.80078 7.7998 -7.80078 20.5 0 28.2998
c7.7998 7.80078 20.5 7.80078 28.2998 0l17.7998 -17.7998l17.9004 17.9004c7.7998 7.7998 20.5 7.7998 28.2998 0s7.7998 -20.5 0 -28.2998l-17.9004 -17.9004l17.9004 -17.7998c7.7998 -7.7998 7.7998 -20.5 0 -28.2998zM374.2 302.1
c7.7002 -7.7998 7.7002 -20.3994 0 -28.1992l-17.9004 -17.9004l17.7998 -18c7.80078 -7.7998 7.80078 -20.5 0 -28.2998c-7.7998 -7.7998 -20.5 -7.7998 -28.2998 0l-17.7998 17.7998l-17.7998 -17.7998c-7.7998 -7.7998 -20.5 -7.7998 -28.2998 0
c-7.80078 7.7998 -7.80078 20.5 0 28.2998l17.8994 17.9004l-17.8994 17.8994c-7.80078 7.7998 -7.80078 20.5 0 28.2998c7.7998 7.80078 20.5 7.80078 28.2998 0l17.7998 -17.7998l17.9004 17.7998c7.7998 7.80078 20.5 7.80078 28.2998 0zM248 176
c35.2998 0 64 -28.7002 64 -64s-28.7002 -64 -64 -64s-64 28.7002 -64 64s28.7002 64 64 64z" />
<glyph glyph-name="flushed" unicode="&#xf579;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM344 304c44.2002 0 80 -35.7998 80 -80s-35.7998 -80 -80 -80
s-80 35.7998 -80 80s35.7998 80 80 80zM344 176c26.5 0 48 21.5 48 48s-21.5 48 -48 48s-48 -21.5 -48 -48s21.5 -48 48 -48zM344 248c13.2998 0 24 -10.7002 24 -24s-10.7002 -24 -24 -24s-24 10.7002 -24 24s10.7002 24 24 24zM232 224c0 -44.2002 -35.7998 -80 -80 -80
s-80 35.7998 -80 80s35.7998 80 80 80s80 -35.7998 80 -80zM152 176c26.5 0 48 21.5 48 48s-21.5 48 -48 48s-48 -21.5 -48 -48s21.5 -48 48 -48zM152 248c13.2998 0 24 -10.7002 24 -24s-10.7002 -24 -24 -24s-24 10.7002 -24 24s10.7002 24 24 24zM312 104
c13.2002 0 24 -10.7998 24 -24s-10.7998 -24 -24 -24h-128c-13.2002 0 -24 10.7998 -24 24s10.7998 24 24 24h128z" />
<glyph glyph-name="frown-open" unicode="&#xf57a;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM200 240c0 -17.7002 -14.2998 -32 -32 -32s-32 14.2998 -32 32
s14.2998 32 32 32s32 -14.2998 32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM248 160c35.5996 0 88.7998 -21.2998 95.7998 -61.2002c2 -11.7998 -9.09961 -21.5996 -20.5 -18.0996
c-31.2002 9.59961 -59.3994 15.2998 -75.2998 15.2998s-44.0996 -5.7002 -75.2998 -15.2998c-11.5 -3.40039 -22.5 6.2998 -20.5 18.0996c7 39.9004 60.2002 61.2002 95.7998 61.2002z" />
<glyph glyph-name="grimace" unicode="&#xf57f;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM344 192c26.5 0 48 -21.5 48 -48v-32c0 -26.5 -21.5 -48 -48 -48h-192c-26.5 0 -48 21.5 -48 48v32c0 26.5 21.5 48 48 48
h192zM176 96v24h-40v-8c0 -8.7998 7.2002 -16 16 -16h24zM176 136v24h-24c-8.7998 0 -16 -7.2002 -16 -16v-8h40zM240 96v24h-48v-24h48zM240 136v24h-48v-24h48zM304 96v24h-48v-24h48zM304 136v24h-48v-24h48zM360 112v8h-40v-24h24c8.7998 0 16 7.2002 16 16zM360 136v8
c0 8.7998 -7.2002 16 -16 16h-24v-24h40z" />
<glyph glyph-name="grin" unicode="&#xf580;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008
c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.9004 -123.3 80c-1.7002 9.90039 7.7998 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32z" />
<glyph glyph-name="grin-alt" unicode="&#xf581;" horiz-adv-x="496"
d="M200.3 200c-7.5 -11.4004 -24.5996 -12 -32.7002 0c-12.3994 18.7002 -15.1992 37.2998 -15.6992 56c0.599609 18.7002 3.2998 37.2998 15.6992 56c7.60059 11.4004 24.7002 12 32.7002 0c12.4004 -18.7002 15.2002 -37.2998 15.7002 -56
c-0.599609 -18.7002 -3.2998 -37.2998 -15.7002 -56zM328.3 200c-7.5 -11.4004 -24.5996 -12 -32.7002 0c-12.3994 18.7002 -15.1992 37.2998 -15.6992 56c0.599609 18.7002 3.2998 37.2998 15.6992 56c7.60059 11.4004 24.7002 12 32.7002 0
c12.4004 -18.7002 15.2002 -37.2998 15.7002 -56c-0.599609 -18.7002 -3.2998 -37.2998 -15.7002 -56zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200
s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.8008 -123.3 80c-1.7002 10 7.7998 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006
s79.7002 4.7998 105.6 13.1006z" />
<glyph glyph-name="grin-beam" unicode="&#xf582;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008
c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.9004 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM117.7 216.3c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998
c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996
l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002zM277.7 216.3c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998
c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002z" />
<glyph glyph-name="grin-beam-sweat" unicode="&#xf583;" horiz-adv-x="496"
d="M440 288c-29.5 0 -53.2998 26.2998 -53.2998 58.7002c0 25 31.7002 75.5 46.2002 97.2998c3.5 5.2998 10.5996 5.2998 14.1992 0c14.5 -21.7998 46.2002 -72.2998 46.2002 -97.2998c0 -32.4004 -23.7998 -58.7002 -53.2998 -58.7002zM248 48
c-51.9004 0 -115.3 32.9004 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996s79.7002 4.7998 105.6 13.0996c10 3.2002 19.4004 -5.39941 17.7002 -15.2998c-8 -47.0996 -71.3994 -80 -123.3 -80zM378.3 216.3
c-3.09961 -0.899414 -7.2002 0.100586 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998
c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998zM483.6 269.2c8 -24.2998 12.4004 -50.2002 12.4004 -77.2002c0 -137 -111 -248 -248 -248s-248 111 -248 248s111 248 248 248
c45.7002 0 88.4004 -12.5996 125.2 -34.2002c-10.9004 -21.5996 -15.5 -36.2002 -17.2002 -45.7002c-31.2002 20.1006 -68.2002 31.9004 -108 31.9004c-110.3 0 -200 -89.7002 -200 -200s89.7002 -200 200 -200s200 89.7002 200 200
c0 22.5 -3.90039 44.0996 -10.7998 64.2998c0.399414 0 21.7998 -2.7998 46.3994 12.9004zM168 258.6c-12.2998 0 -23.7998 -7.7998 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998
c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996z" />
<glyph glyph-name="grin-hearts" unicode="&#xf584;" horiz-adv-x="496"
d="M353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.8008 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM200.8 192.3
l-70.2002 18.1006c-20.3994 5.2998 -31.8994 27 -24.1992 47.1992c6.69922 17.7002 26.6992 26.7002 44.8994 22l7.10059 -1.89941l2 7.09961c5.09961 18.1006 22.8994 30.9004 41.5 27.9004c21.3994 -3.40039 34.3994 -24.2002 28.7998 -44.5l-19.4004 -69.9004
c-1.2998 -4.5 -6 -7.2002 -10.5 -6zM389.6 257.6c7.7002 -20.1992 -3.7998 -41.7998 -24.1992 -47.0996l-70.2002 -18.2002c-4.60059 -1.2002 -9.2998 1.5 -10.5 6l-19.4004 69.9004c-5.59961 20.2998 7.40039 41.0996 28.7998 44.5c18.7002 3 36.5 -9.7998 41.5 -27.9004
l2 -7.09961l7.10059 1.89941c18.2002 4.7002 38.2002 -4.39941 44.8994 -22zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200
s89.7002 -200 200 -200z" />
<glyph glyph-name="grin-squint" unicode="&#xf585;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008
c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.9004 -123.3 80c-1.7002 9.90039 7.7998 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM118.9 184.2c-3.80078 4.39941 -3.90039 11 -0.100586 15.5l33.6006 40.2998
l-33.6006 40.2998c-3.7002 4.5 -3.7002 11 0.100586 15.5c3.89941 4.40039 10.1992 5.5 15.2998 2.5l80 -48c3.59961 -2.2002 5.7998 -6.09961 5.7998 -10.2998s-2.2002 -8.09961 -5.7998 -10.2998l-80 -48c-5.40039 -3.2002 -11.7002 -1.7002 -15.2998 2.5zM361.8 181.7
l-80 48c-3.59961 2.2002 -5.7998 6.09961 -5.7998 10.2998s2.2002 8.09961 5.7998 10.2998l80 48c5.10059 2.90039 11.5 1.90039 15.2998 -2.5c3.80078 -4.5 3.90039 -11 0.100586 -15.5l-33.6006 -40.2998l33.6006 -40.2998c3.7002 -4.5 3.7002 -11 -0.100586 -15.5
c-3.59961 -4.2002 -9.89941 -5.7002 -15.2998 -2.5z" />
<glyph glyph-name="grin-squint-tears" unicode="&#xf586;"
d="M117.1 63.9004c6.30078 0.899414 11.7002 -4.5 10.9004 -10.9004c-3.7002 -25.7998 -13.7002 -84 -30.5996 -100.9c-22 -21.8994 -57.9004 -21.5 -80.3008 0.900391c-22.3994 22.4004 -22.7998 58.4004 -0.899414 80.2998
c16.8994 16.9004 75.0996 26.9004 100.899 30.6006zM75.9004 105.6c-19.6006 -3.89941 -35.1006 -8.09961 -47.3008 -12.1992c-39.2998 90.5996 -22.0996 199.899 52 274c48.5 48.3994 111.9 72.5996 175.4 72.5996c38.9004 0 77.7998 -9.2002 113.2 -27.4004
c-4 -12.1992 -8.2002 -28 -12 -48.2998c-30.4004 17.9004 -65 27.7002 -101.2 27.7002c-53.4004 0 -103.6 -20.7998 -141.4 -58.5996c-61.5996 -61.5 -74.2998 -153.4 -38.6992 -227.801zM428.2 293.2c20.2998 3.89941 36.2002 8 48.5 12
c47.8994 -93.2002 32.8994 -210.5 -45.2002 -288.601c-48.5 -48.3994 -111.9 -72.5996 -175.4 -72.5996c-33.6992 0 -67.2998 7 -98.6992 20.5996c4.19922 12.2002 8.2998 27.7002 12.1992 47.2002c26.6006 -12.7998 55.9004 -19.7998 86.4004 -19.7998
c53.4004 0 103.6 20.7998 141.4 58.5996c65.6992 65.7002 75.7998 166 30.7998 242.601zM394.9 320.1c-6.30078 -0.899414 -11.7002 4.5 -10.9004 10.9004c3.7002 25.7998 13.7002 84 30.5996 100.9c22 21.8994 57.9004 21.5 80.3008 -0.900391
c22.3994 -22.4004 22.7998 -58.4004 0.899414 -80.2998c-16.8994 -16.9004 -75.0996 -26.9004 -100.899 -30.6006zM207.9 211.8c3 -3 4.19922 -7.2998 3.19922 -11.5l-22.5996 -90.5c-1.40039 -5.39941 -6.2002 -9.09961 -11.7002 -9.09961h-0.899414
c-5.80078 0.5 -10.5 5.09961 -11 10.8994l-4.80078 52.3008l-52.2998 4.7998c-5.7998 0.5 -10.3994 5.2002 -10.8994 11c-0.400391 5.89941 3.39941 11.2002 9.09961 12.5996l90.5 22.7002c4.2002 1 8.40039 -0.200195 11.4004 -3.2002zM247.6 236.9
c-0.0996094 0 -6.39941 -1.80078 -11.3994 3.19922c-3 3 -4.2002 7.30078 -3.2002 11.4004l22.5996 90.5c1.40039 5.7002 7 9.2002 12.6006 9.09961c5.7998 -0.5 10.5 -5.09961 11 -10.8994l4.7998 -52.2998l52.2998 -4.80078c5.7998 -0.5 10.4004 -5.19922 10.9004 -11
c0.399414 -5.89941 -3.40039 -11.1992 -9.10059 -12.5996zM299.6 148.4c29.1006 29.0996 53 59.5996 65.3008 83.7998c4.89941 9.2998 17.5996 9.89941 23.3994 1.7002c27.7002 -38.9004 6.10059 -106.9 -30.5996 -143.7s-104.8 -58.2998 -143.7 -30.6006
c-8.2998 5.90039 -7.5 18.6006 1.7002 23.4004c24.2002 12.5 54.7998 36.2998 83.8994 65.4004z" />
<glyph glyph-name="grin-stars" unicode="&#xf587;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM353.6 143.4c10 3.09961 19.3008 -5.5 17.7002 -15.3008
c-8 -47.0996 -71.2998 -80 -123.3 -80s-115.4 32.8008 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.3008c26 -8.30078 64.3994 -13.1006 105.6 -13.1006s79.7002 4.7998 105.6 13.1006zM125.7 200.9l6.09961 34.8994l-25.3994 24.6006
c-4.60059 4.59961 -1.90039 12.2998 4.2998 13.1992l34.8994 5l15.5 31.6006c2.90039 5.7998 11 5.7998 13.9004 0l15.5 -31.6006l34.9004 -5c6.19922 -1 8.7998 -8.69922 4.2998 -13.1992l-25.4004 -24.6006l6 -34.8994c1 -6.2002 -5.39941 -11 -11 -7.90039
l-31.2998 16.2998l-31.2998 -16.2998c-5.60059 -3.09961 -12 1.7002 -11 7.90039zM385.4 273.6c6.19922 -1 8.89941 -8.59961 4.39941 -13.1992l-25.3994 -24.6006l6 -34.8994c1 -6.2002 -5.40039 -11 -11 -7.90039l-31.3008 16.2998l-31.2998 -16.2998
c-5.59961 -3.09961 -12 1.7002 -11 7.90039l6 34.8994l-25.3994 24.6006c-4.60059 4.59961 -1.90039 12.2998 4.2998 13.1992l34.8994 5l15.5 31.6006c2.90039 5.7998 11 5.7998 13.9004 0l15.5 -31.6006z" />
<glyph glyph-name="grin-tears" unicode="&#xf588;" horiz-adv-x="640"
d="M117.1 191.9c6.30078 0.899414 11.7002 -4.5 10.9004 -10.9004c-3.7002 -25.7998 -13.7002 -84 -30.5996 -100.9c-22 -21.8994 -57.9004 -21.5 -80.3008 0.900391c-22.3994 22.4004 -22.7998 58.4004 -0.899414 80.2998c16.8994 16.9004 75.0996 26.9004 100.899 30.6006
zM623.8 161.3c21.9004 -21.8994 21.5 -57.8994 -0.799805 -80.2002c-22.4004 -22.3994 -58.4004 -22.7998 -80.2998 -0.899414c-16.9004 16.8994 -26.9004 75.0996 -30.6006 100.899c-0.899414 6.30078 4.5 11.7002 10.8008 10.8008
c25.7998 -3.7002 84 -13.7002 100.899 -30.6006zM497.2 99.5996c12.3994 -37.2998 25.0996 -43.7998 28.2998 -46.5c-44.5996 -65.7998 -120 -109.1 -205.5 -109.1s-160.9 43.2998 -205.5 109.1c3.09961 2.60059 15.7998 9.10059 28.2998 46.5
c33.4004 -63.8994 100.3 -107.6 177.2 -107.6s143.8 43.7002 177.2 107.6zM122.7 223.5c-2.40039 0.299805 -5 2.5 -49.5 -6.90039c12.3994 125.4 118.1 223.4 246.8 223.4s234.4 -98 246.8 -223.5c-44.2998 9.40039 -47.3994 7.2002 -49.5 7
c-15.2002 95.2998 -97.7998 168.5 -197.3 168.5s-182.1 -73.2002 -197.3 -168.5zM320 48c-51.9004 0 -115.3 32.9004 -123.3 80c-1.7002 10 7.89941 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996s79.7002 4.7998 105.6 13.0996
c10 3.2002 19.4004 -5.39941 17.7002 -15.2998c-8 -47.0996 -71.3994 -80 -123.3 -80zM450.3 216.3c-3.09961 -0.899414 -7.2002 0.100586 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996l-9.5 -17
c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998zM240 258.6
c-12.2998 0 -23.7998 -7.7998 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004
c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996z" />
<glyph glyph-name="grin-tongue" unicode="&#xf589;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM312 40h0.0996094v43.7998l-17.6992 8.7998c-15.1006 7.60059 -31.5 -1.69922 -34.9004 -16.5l-2.7998 -12.0996c-2.10059 -9.2002 -15.2002 -9.2002 -17.2998 0
l-2.80078 12.0996c-3.39941 14.8008 -19.8994 24 -34.8994 16.5l-17.7002 -8.7998v-42.7998c0 -35.2002 28 -64.5 63.0996 -65c35.8008 -0.5 64.9004 28.4004 64.9004 64zM340.2 14.7002c64 33.3994 107.8 100.3 107.8 177.3c0 110.3 -89.7002 200 -200 200
s-200 -89.7002 -200 -200c0 -77 43.7998 -143.9 107.8 -177.3c-2.2002 8.09961 -3.7998 16.5 -3.7998 25.2998v43.5c-14.2002 12.4004 -24.4004 27.5 -27.2998 44.5c-1.7002 10 7.7998 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996
s79.7002 4.7998 105.6 13.0996c10 3.2002 19.4004 -5.39941 17.7002 -15.2998c-2.89941 -17 -13.0996 -32.0996 -27.2998 -44.5v-43.5c0 -8.7998 -1.59961 -17.2002 -3.7998 -25.2998zM168 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32
s14.2998 32 32 32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32z" />
<glyph glyph-name="grin-tongue-squint" unicode="&#xf58a;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM312 40h0.0996094v43.7998l-17.6992 8.7998c-15.1006 7.60059 -31.5 -1.69922 -34.9004 -16.5l-2.7998 -12.0996c-2.10059 -9.2002 -15.2002 -9.2002 -17.2998 0
l-2.80078 12.0996c-3.39941 14.8008 -19.8994 24 -34.8994 16.5l-17.7002 -8.7998v-42.7998c0 -35.2002 28 -64.5 63.0996 -65c35.8008 -0.5 64.9004 28.4004 64.9004 64zM340.2 14.7002c64 33.3994 107.8 100.3 107.8 177.3c0 110.3 -89.7002 200 -200 200
s-200 -89.7002 -200 -200c0 -77 43.7998 -143.9 107.8 -177.3c-2.2002 8.09961 -3.7998 16.5 -3.7998 25.2998v43.5c-14.2002 12.4004 -24.4004 27.5 -27.2998 44.5c-1.7002 10 7.7998 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996
s79.7002 4.7998 105.6 13.0996c10 3.2002 19.4004 -5.39941 17.7002 -15.2998c-2.89941 -17 -13.0996 -32.0996 -27.2998 -44.5v-43.5c0 -8.7998 -1.59961 -17.2002 -3.7998 -25.2998zM377.1 295.8c3.80078 -4.39941 3.90039 -11 0.100586 -15.5l-33.6006 -40.2998
l33.6006 -40.2998c3.7002 -4.5 3.7002 -11 -0.100586 -15.5c-3.59961 -4.2002 -9.89941 -5.7002 -15.2998 -2.5l-80 48c-3.59961 2.2002 -5.7998 6.09961 -5.7998 10.2998s2.2002 8.09961 5.7998 10.2998l80 48c5 3 11.5 1.90039 15.2998 -2.5zM214.2 250.3
c3.59961 -2.2002 5.7998 -6.09961 5.7998 -10.2998s-2.2002 -8.09961 -5.7998 -10.2998l-80 -48c-5.40039 -3.2002 -11.7002 -1.7002 -15.2998 2.5c-3.80078 4.5 -3.90039 11 -0.100586 15.5l33.6006 40.2998l-33.6006 40.2998c-3.7002 4.5 -3.7002 11 0.100586 15.5
c3.89941 4.5 10.2998 5.5 15.2998 2.5z" />
<glyph glyph-name="grin-tongue-wink" unicode="&#xf58b;" horiz-adv-x="496"
d="M152 268c25.7002 0 55.9004 -16.9004 59.7998 -42.0996c0.799805 -5 -1.7002 -10 -6.09961 -12.4004c-5.7002 -3.09961 -11.2002 -0.599609 -13.7002 1.59961l-9.5 8.5c-14.7998 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5
c-3.7998 -3.39941 -9.2998 -4 -13.7002 -1.59961c-4.39941 2.40039 -6.89941 7.40039 -6.09961 12.4004c3.89941 25.1992 34.0996 42.0996 59.7998 42.0996zM328 320c44.2002 0 80 -35.7998 80 -80s-35.7998 -80 -80 -80s-80 35.7998 -80 80s35.7998 80 80 80zM328 192
c26.5 0 48 21.5 48 48s-21.5 48 -48 48s-48 -21.5 -48 -48s21.5 -48 48 -48zM328 264c13.2998 0 24 -10.7002 24 -24s-10.7002 -24 -24 -24s-24 10.7002 -24 24s10.7002 24 24 24zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248z
M312 40h0.0996094v43.7998l-17.6992 8.7998c-15.1006 7.60059 -31.5 -1.69922 -34.9004 -16.5l-2.7998 -12.0996c-2.10059 -9.2002 -15.2002 -9.2002 -17.2998 0l-2.80078 12.0996c-3.39941 14.8008 -19.8994 24 -34.8994 16.5l-17.7002 -8.7998v-42.7998
c0 -35.2002 28 -64.5 63.0996 -65c35.8008 -0.5 64.9004 28.4004 64.9004 64zM340.2 14.7002c64 33.3994 107.8 100.3 107.8 177.3c0 110.3 -89.7002 200 -200 200s-200 -89.7002 -200 -200c0 -77 43.7998 -143.9 107.8 -177.3
c-2.2002 8.09961 -3.7998 16.5 -3.7998 25.2998v43.5c-14.2002 12.4004 -24.4004 27.5 -27.2998 44.5c-1.7002 10 7.7998 18.4004 17.7002 15.2998c26 -8.2998 64.3994 -13.0996 105.6 -13.0996s79.7002 4.7998 105.6 13.0996c10 3.2002 19.4004 -5.39941 17.7002 -15.2998
c-2.89941 -17 -13.0996 -32.0996 -27.2998 -44.5v-43.5c0 -8.7998 -1.59961 -17.2002 -3.7998 -25.2998z" />
<glyph glyph-name="grin-wink" unicode="&#xf58c;" horiz-adv-x="496"
d="M328 268c25.6904 0 55.8799 -16.9199 59.8701 -42.1201c1.72949 -11.0898 -11.3506 -18.2695 -19.8301 -10.8398l-9.5498 8.47949c-14.8105 13.1904 -46.1602 13.1904 -60.9707 0l-9.5498 -8.47949c-8.33008 -7.40039 -21.5801 -0.379883 -19.8301 10.8398
c3.98047 25.2002 34.1699 42.1201 59.8604 42.1201zM168 208c-17.6699 0 -32 14.3301 -32 32s14.3301 32 32 32s32 -14.3301 32 -32s-14.3301 -32 -32 -32zM353.55 143.36c10.04 3.13965 19.3906 -5.4502 17.71 -15.3408
c-7.92969 -47.1494 -71.3193 -80.0195 -123.26 -80.0195s-115.33 32.8701 -123.26 80.0195c-1.69043 9.9707 7.76953 18.4707 17.71 15.3408c25.9297 -8.31055 64.3994 -13.0605 105.55 -13.0605s79.6201 4.75977 105.55 13.0605zM248 440c136.97 0 248 -111.03 248 -248
s-111.03 -248 -248 -248s-248 111.03 -248 248s111.03 248 248 248zM248 -8c110.28 0 200 89.7197 200 200s-89.7197 200 -200 200s-200 -89.7197 -200 -200s89.7197 -200 200 -200z" />
<glyph glyph-name="kiss" unicode="&#xf596;" horiz-adv-x="496"
d="M168 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM304 140c0 -13 -13.4004 -27.2998 -35.0996 -36.4004c21.7998 -8.69922 35.1992 -23 35.1992 -36c0 -19.1992 -28.6992 -41.5 -71.5 -44h-0.5
c-3.69922 0 -7 2.60059 -7.7998 6.2002c-0.899414 3.7998 1.10059 7.7002 4.7002 9.2002l17 7.2002c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.2002c-6 2.59961 -5.7002 12.3994 0 14.7998l17 7.2002
c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.19922c-3.59961 1.5 -5.59961 5.40039 -4.7002 9.2002c0.799805 3.7998 4.40039 6.60059 8.2002 6.2002c42.7002 -2.5 71.5 -24.7998 71.5 -44zM248 440c137 0 248 -111 248 -248
s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32z
" />
<glyph glyph-name="kiss-beam" unicode="&#xf597;" horiz-adv-x="496"
d="M168 296c23.7998 0 52.7002 -29.2998 55.7998 -71.4004c0.299805 -3.7998 -2 -7.19922 -5.59961 -8.2998c-3.10059 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996c-12.3008 0 -23.8008 -7.89941 -31.5 -21.5996l-9.5 -17
c-1.80078 -3.2002 -5.80078 -4.7002 -9.30078 -3.7002c-3.59961 1.10059 -5.89941 4.60059 -5.59961 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004zM248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8
c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM304 140c0 -13 -13.4004 -27.2998 -35.0996 -36.4004c21.7998 -8.69922 35.1992 -23 35.1992 -36c0 -19.1992 -28.6992 -41.5 -71.5 -44h-0.5
c-3.69922 0 -7 2.60059 -7.7998 6.2002c-0.899414 3.7998 1.10059 7.7002 4.7002 9.2002l17 7.2002c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.2002c-6 2.59961 -5.7002 12.3994 0 14.7998l17 7.2002
c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.19922c-3.59961 1.5 -5.59961 5.40039 -4.7002 9.2002c0.799805 3.7998 4.40039 6.60059 8.2002 6.2002c42.7002 -2.5 71.5 -24.7998 71.5 -44zM328 296
c23.7998 0 52.7002 -29.2998 55.7998 -71.4004c0.299805 -3.7998 -2 -7.19922 -5.59961 -8.2998c-3.10059 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996c-12.3008 0 -23.8008 -7.89941 -31.5 -21.5996l-9.5 -17
c-1.80078 -3.2002 -5.80078 -4.7002 -9.30078 -3.7002c-3.59961 1.10059 -5.89941 4.60059 -5.59961 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004z" />
<glyph glyph-name="kiss-wink-heart" unicode="&#xf598;" horiz-adv-x="504"
d="M304 139.5c0 -13 -13.4004 -27.2998 -35.0996 -36.4004c21.7998 -8.69922 35.1992 -23 35.1992 -36c0 -19.1992 -28.6992 -41.5 -71.5 -44h-0.5c-3.69922 0 -7 2.60059 -7.7998 6.2002c-0.899414 3.7998 1.10059 7.7002 4.7002 9.2002l17 7.2002
c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.2002c-6 2.59961 -5.7002 12.3994 0 14.7998l17 7.2002c12.9004 5.5 20.7002 13.5 20.7002 21.5s-7.7998 16 -20.7998 21.5l-16.9004 7.19922c-3.59961 1.5 -5.59961 5.40039 -4.7002 9.2002
c0.799805 3.7998 4.40039 6.60059 8.2002 6.2002c42.7002 -2.5 71.5 -24.7998 71.5 -44zM374.5 223c-14.7998 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5c-2.5 -2.2998 -7.90039 -4.7002 -13.7002 -1.59961c-4.39941 2.39941 -6.89941 7.39941 -6.09961 12.3994
c3.89941 25.2002 34.2002 42.1006 59.7998 42.1006s55.7998 -16.9004 59.7998 -42.1006c0.799805 -5 -1.7002 -10 -6.09961 -12.3994c-4.40039 -2.40039 -9.90039 -1.7002 -13.7002 1.59961zM136 239.5c0 17.7002 14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32
s-32 14.2998 -32 32zM501.1 45.5c9.2002 -23.9004 -4.39941 -49.4004 -28.5 -55.7002l-83 -21.5c-5.39941 -1.39941 -10.8994 1.7998 -12.3994 7.10059l-22.9004 82.5996c-6.59961 24 8.7998 48.5996 34 52.5996c22 3.5 43.1006 -11.5996 49 -33l2.2998 -8.39941
l8.40039 2.2002c21.5996 5.59961 45.0996 -5.10059 53.0996 -25.9004zM334 11.7002c17.7002 -64 10.9004 -39.5 13.4004 -46.7998c-30.5 -13.4004 -64 -20.9004 -99.4004 -20.9004c-137 0 -248 111 -248 248s111 248 248 248s248 -111 247.9 -248
c0 -31.7998 -6.2002 -62.0996 -17.1006 -90c-6 1.5 -12.2002 2.7998 -18.5996 2.90039c-5.60059 9.69922 -13.6006 17.5 -22.6006 23.8994c6.7002 19.9004 10.4004 41.1006 10.4004 63.2002c0 110.3 -89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200
c30.7998 0 59.9004 7.2002 86 19.7002z" />
<glyph glyph-name="laugh" unicode="&#xf599;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM389.4 50.5996c37.7998 37.8008 58.5996 88 58.5996 141.4s-20.7998 103.6 -58.5996 141.4c-37.8008 37.7998 -88 58.5996 -141.4 58.5996s-103.6 -20.7998 -141.4 -58.5996
c-37.7998 -37.8008 -58.5996 -88 -58.5996 -141.4s20.7998 -103.6 58.5996 -141.4c37.8008 -37.7998 88 -58.5996 141.4 -58.5996s103.6 20.7998 141.4 58.5996zM328 224c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM168 224
c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM362.4 160c8.19922 0 14.5 -7 13.5 -15c-7.5 -59.2002 -58.9004 -105 -121.101 -105h-13.5996c-62.2002 0 -113.601 45.7998 -121.101 105c-1 8 5.30078 15 13.5 15h228.801z" />
<glyph glyph-name="laugh-beam" unicode="&#xf59a;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM389.4 50.5996c37.7998 37.8008 58.5996 88 58.5996 141.4s-20.7998 103.6 -58.5996 141.4c-37.8008 37.7998 -88 58.5996 -141.4 58.5996s-103.6 -20.7998 -141.4 -58.5996
c-37.7998 -37.8008 -58.5996 -88 -58.5996 -141.4s20.7998 -103.6 58.5996 -141.4c37.8008 -37.7998 88 -58.5996 141.4 -58.5996s103.6 20.7998 141.4 58.5996zM328 296c23.7998 0 52.7002 -29.2998 55.7998 -71.4004c0.700195 -8.5 -10.7998 -11.8994 -14.8994 -4.5
l-9.5 17c-7.7002 13.7002 -19.2002 21.6006 -31.5 21.6006c-12.3008 0 -23.8008 -7.90039 -31.5 -21.6006l-9.5 -17c-4.10059 -7.39941 -15.6006 -4.09961 -14.9004 4.5c3.2998 42.1006 32.2002 71.4004 56 71.4004zM127 220.1c-4.2002 -7.39941 -15.7002 -4 -15.0996 4.5
c3.2998 42.1006 32.1992 71.4004 56 71.4004c23.7998 0 52.6992 -29.2998 56 -71.4004c0.699219 -8.5 -10.8008 -11.8994 -14.9004 -4.5l-9.5 17c-7.7002 13.7002 -19.2002 21.6006 -31.5 21.6006s-23.7998 -7.90039 -31.5 -21.6006zM362.4 160c8.19922 0 14.5 -7 13.5 -15
c-7.5 -59.2002 -58.9004 -105 -121.101 -105h-13.5996c-62.2002 0 -113.601 45.7998 -121.101 105c-1 8 5.30078 15 13.5 15h228.801z" />
<glyph glyph-name="laugh-squint" unicode="&#xf59b;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM389.4 50.5996c37.7998 37.8008 58.5996 88 58.5996 141.4s-20.7998 103.6 -58.5996 141.4c-37.8008 37.7998 -88 58.5996 -141.4 58.5996s-103.6 -20.7998 -141.4 -58.5996
c-37.7998 -37.8008 -58.5996 -88 -58.5996 -141.4s20.7998 -103.6 58.5996 -141.4c37.8008 -37.7998 88 -58.5996 141.4 -58.5996s103.6 20.7998 141.4 58.5996zM343.6 252l33.6006 -40.2998c8.59961 -10.4004 -3.90039 -24.7998 -15.4004 -18l-80 48
c-7.7998 4.7002 -7.7998 15.8994 0 20.5996l80 48c11.6006 6.7998 24 -7.7002 15.4004 -18zM134.2 193.7c-11.6006 -6.7998 -24.1006 7.59961 -15.4004 18l33.6006 40.2998l-33.6006 40.2998c-8.59961 10.2998 3.7998 24.9004 15.4004 18l80 -48
c7.7998 -4.7002 7.7998 -15.8994 0 -20.5996zM362.4 160c8.19922 0 14.5 -7 13.5 -15c-7.5 -59.2002 -58.9004 -105 -121.101 -105h-13.5996c-62.2002 0 -113.601 45.7998 -121.101 105c-1 8 5.30078 15 13.5 15h228.801z" />
<glyph glyph-name="laugh-wink" unicode="&#xf59c;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM389.4 50.5996c37.7998 37.8008 58.5996 88 58.5996 141.4s-20.7998 103.6 -58.5996 141.4c-37.8008 37.7998 -88 58.5996 -141.4 58.5996s-103.6 -20.7998 -141.4 -58.5996
c-37.7998 -37.8008 -58.5996 -88 -58.5996 -141.4s20.7998 -103.6 58.5996 -141.4c37.8008 -37.7998 88 -58.5996 141.4 -58.5996s103.6 20.7998 141.4 58.5996zM328 284c25.7002 0 55.9004 -16.9004 59.7002 -42.0996c1.7998 -11.1006 -11.2998 -18.2002 -19.7998 -10.8008
l-9.5 8.5c-14.8008 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5c-8.30078 -7.39941 -21.5 -0.399414 -19.8008 10.8008c4 25.1992 34.2002 42.0996 59.9004 42.0996zM168 224c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32s-14.2998 -32 -32 -32z
M362.4 160c8.19922 0 14.5 -7 13.5 -15c-7.5 -59.2002 -58.9004 -105 -121.101 -105h-13.5996c-62.2002 0 -113.601 45.7998 -121.101 105c-1 8 5.30078 15 13.5 15h228.801z" />
<glyph glyph-name="meh-blank" unicode="&#xf5a4;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM168 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32
s-32 14.2998 -32 32s14.2998 32 32 32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32z" />
<glyph glyph-name="meh-rolling-eyes" unicode="&#xf5a5;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM336 296c39.7998 0 72 -32.2002 72 -72s-32.2002 -72 -72 -72
s-72 32.2002 -72 72s32.2002 72 72 72zM336 184c22.0996 0 40 17.9004 40 40c0 13.5996 -7.2998 25.0996 -17.7002 32.2998c1 -2.59961 1.7002 -5.39941 1.7002 -8.2998c0 -13.2998 -10.7002 -24 -24 -24s-24 10.7002 -24 24c0 3 0.700195 5.7002 1.7002 8.2998
c-10.4004 -7.2002 -17.7002 -18.7002 -17.7002 -32.2998c0 -22.0996 17.9004 -40 40 -40zM232 224c0 -39.7998 -32.2002 -72 -72 -72s-72 32.2002 -72 72s32.2002 72 72 72s72 -32.2002 72 -72zM120 224c0 -22.0996 17.9004 -40 40 -40s40 17.9004 40 40
c0 13.5996 -7.2998 25.0996 -17.7002 32.2998c1 -2.59961 1.7002 -5.39941 1.7002 -8.2998c0 -13.2998 -10.7002 -24 -24 -24s-24 10.7002 -24 24c0 3 0.700195 5.7002 1.7002 8.2998c-10.4004 -7.2002 -17.7002 -18.7002 -17.7002 -32.2998zM312 96
c13.2002 0 24 -10.7998 24 -24s-10.7998 -24 -24 -24h-128c-13.2002 0 -24 10.7998 -24 24s10.7998 24 24 24h128z" />
<glyph glyph-name="sad-cry" unicode="&#xf5b3;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM392 53.5996c34.5996 35.9004 56 84.7002 56 138.4c0 110.3 -89.7002 200 -200 200s-200 -89.7002 -200 -200c0 -53.7002 21.4004 -102.4 56 -138.4v114.4
c0 13.2002 10.7998 24 24 24s24 -10.7998 24 -24v-151.4c28.5 -15.5996 61.2002 -24.5996 96 -24.5996s67.5 9 96 24.5996v151.4c0 13.2002 10.7998 24 24 24s24 -10.7998 24 -24v-114.4zM205.8 213.5c-5.7998 -3.2002 -11.2002 -0.700195 -13.7002 1.59961l-9.5 8.5
c-14.7998 13.2002 -46.1992 13.2002 -61 0l-9.5 -8.5c-3.7998 -3.39941 -9.2998 -4 -13.6992 -1.59961c-4.40039 2.40039 -6.90039 7.40039 -6.10059 12.4004c3.90039 25.1992 34.2002 42.0996 59.7998 42.0996c25.6006 0 55.8008 -16.9004 59.8008 -42.0996
c0.799805 -5 -1.7002 -10 -6.10059 -12.4004zM344 268c25.7002 0 55.9004 -16.9004 59.7998 -42.0996c0.799805 -5 -1.7002 -10 -6.09961 -12.4004c-5.7002 -3.09961 -11.2002 -0.599609 -13.7002 1.59961l-9.5 8.5c-14.7998 13.2002 -46.2002 13.2002 -61 0l-9.5 -8.5
c-3.7998 -3.39941 -9.2002 -4 -13.7002 -1.59961c-4.39941 2.40039 -6.89941 7.40039 -6.09961 12.4004c3.89941 25.1992 34.0996 42.0996 59.7998 42.0996zM248 176c30.9004 0 56 -28.7002 56 -64s-25.0996 -64 -56 -64s-56 28.7002 -56 64s25.0996 64 56 64z" />
<glyph glyph-name="sad-tear" unicode="&#xf5b4;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM256 144c38.0996 0 74 -16.7998 98.5 -46.0996
c8.5 -10.2002 7.09961 -25.3008 -3.09961 -33.8008c-10.6006 -8.7998 -25.7002 -6.69922 -33.8008 3.10059c-15.2998 18.2998 -37.7998 28.7998 -61.5996 28.7998c-13.2002 0 -24 10.7998 -24 24s10.7998 24 24 24zM168 208c-17.7002 0 -32 14.2998 -32 32s14.2998 32 32 32
s32 -14.2998 32 -32s-14.2998 -32 -32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32zM162.4 173.2c2.7998 3.7002 8.39941 3.7002 11.1992 0c11.4004 -15.2998 36.4004 -50.6006 36.4004 -68.1006
c0 -22.6992 -18.7998 -41.0996 -42 -41.0996s-42 18.4004 -42 41.0996c0 17.5 25 52.8008 36.4004 68.1006z" />
<glyph glyph-name="smile-beam" unicode="&#xf5b8;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM332 135.4c8.5 10.1992 23.5996 11.5 33.7998 3.09961
c10.2002 -8.5 11.6006 -23.5996 3.10059 -33.7998c-30 -36 -74.1006 -56.6006 -120.9 -56.6006s-90.9004 20.6006 -120.9 56.6006c-8.39941 10.2002 -7.09961 25.2998 3.10059 33.7998c10.2002 8.40039 25.2998 7.09961 33.7998 -3.09961
c20.7998 -25.1006 51.5 -39.4004 84 -39.4004s63.2002 14.4004 84 39.4004zM136.5 237l-9.5 -17c-1.90039 -3.2002 -5.90039 -4.7998 -9.2998 -3.7002c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004s52.7002 -29.2998 56 -71.4004
c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996zM328 296c23.7998 0 52.7002 -29.2998 56 -71.4004
c0.299805 -3.7998 -2.09961 -7.19922 -5.7002 -8.2998c-3.09961 -1 -7.2002 0 -9.2998 3.7002l-9.5 17c-7.7002 13.7002 -19.2002 21.5996 -31.5 21.5996s-23.7998 -7.89941 -31.5 -21.5996l-9.5 -17c-1.90039 -3.2002 -5.7998 -4.7998 -9.2998 -3.7002
c-3.60059 1.10059 -6 4.60059 -5.7002 8.2998c3.2998 42.1006 32.2002 71.4004 56 71.4004z" />
<glyph glyph-name="surprise" unicode="&#xf5c2;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM248 168c35.2998 0 64 -28.7002 64 -64s-28.7002 -64 -64 -64
s-64 28.7002 -64 64s28.7002 64 64 64zM200 240c0 -17.7002 -14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32s32 -14.2998 32 -32zM328 272c17.7002 0 32 -14.2998 32 -32s-14.2998 -32 -32 -32s-32 14.2998 -32 32s14.2998 32 32 32z" />
<glyph glyph-name="tired" unicode="&#xf5c8;" horiz-adv-x="496"
d="M248 440c137 0 248 -111 248 -248s-111 -248 -248 -248s-248 111 -248 248s111 248 248 248zM248 -8c110.3 0 200 89.7002 200 200s-89.7002 200 -200 200s-200 -89.7002 -200 -200s89.7002 -200 200 -200zM377.1 295.8c3.80078 -4.39941 3.90039 -11 0.100586 -15.5
l-33.6006 -40.2998l33.6006 -40.2998c3.7998 -4.5 3.7002 -11 -0.100586 -15.5c-3.5 -4.10059 -9.89941 -5.7002 -15.2998 -2.5l-80 48c-3.59961 2.2002 -5.7998 6.09961 -5.7998 10.2998s2.2002 8.09961 5.7998 10.2998l80 48c5 2.90039 11.5 1.90039 15.2998 -2.5z
M220 240c0 -4.2002 -2.2002 -8.09961 -5.7998 -10.2998l-80 -48c-5.40039 -3.2002 -11.7998 -1.60059 -15.2998 2.5c-3.80078 4.5 -3.90039 11 -0.100586 15.5l33.6006 40.2998l-33.6006 40.2998c-3.7998 4.5 -3.7002 11 0.100586 15.5
c3.7998 4.40039 10.2998 5.5 15.2998 2.5l80 -48c3.59961 -2.2002 5.7998 -6.09961 5.7998 -10.2998zM248 176c45.4004 0 100.9 -38.2998 107.8 -93.2998c1.5 -11.9004 -7 -21.6006 -15.5 -17.9004c-22.7002 9.7002 -56.2998 15.2002 -92.2998 15.2002
s-69.5996 -5.5 -92.2998 -15.2002c-8.60059 -3.7002 -17 6.10059 -15.5 17.9004c6.89941 55 62.3994 93.2998 107.8 93.2998z" />
</font>
</defs></svg>

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 829 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

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,27 @@
import { Route, Routes } from 'react-router-dom'
import { Layout } from './layout'
import { routes } from './routes'
import { RouterProvider } from 'react-router-dom'
import { useEffect } from 'react'
import { router } from 'routes'
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>
)
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

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,5 @@
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
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

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,2 @@
Game Name,16 by 9 image,Boxart image
Marvel's Spider-Man 2,,https://image.nostr.build/b5ef5ef8bd99daab73148145b024a1e6177160fd287ce829f82ba46e821490b6.jpg
1 Game Name 16 by 9 image Boxart image
2 Marvel's Spider-Man 2 https://image.nostr.build/b5ef5ef8bd99daab73148145b024a1e6177160fd287ce829f82ba46e821490b6.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

View File

Before

Width:  |  Height:  |  Size: 277 KiB

After

Width:  |  Height:  |  Size: 277 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

@ -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

@ -15,10 +15,6 @@ export const BlogCard = ({ backgroundLink }: BlogCardProps) => {
>
<div
className='cardBlogMainInside'
style={{
background:
'linear-gradient( rgba(255, 255, 255, 0) 0%, #232323 100%)'
}}
>
<h3
style={{

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

@ -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,85 +1,304 @@
import Link from '@tiptap/extension-link'
import { Editor, EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React, { useEffect } from 'react'
import '../styles/styles.css'
import '../styles/tiptap.scss'
interface InputFieldProps {
label: string
description?: string
type?: 'text' | 'textarea'
type?: 'text' | 'textarea' | 'richtext'
placeholder: string
name: string
inputMode?: 'url'
value: string
error?: string
onChange: (name: string, value: string) => void
}
export const InputField = ({
label,
description,
type = 'text',
placeholder,
name,
inputMode
}: InputFieldProps) => (
<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}
></textarea>
) : (
<input
type={type}
className='inputMain'
placeholder={placeholder}
name={name}
inputMode={inputMode}
/>
)}
</div>
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' ? (
<RichTextEditor
content={value}
updateContent={(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
type?: 'default' | 'stylized'
}
export const CheckboxField = ({ label, name }: CheckboxFieldProps) => (
<div className='inputLabelWrapperMain inputLabelWrapperMainAlt inputLabelWrapperMainAltStylized'>
<label className='form-label labelMain'>{label}</label>
<input type='checkbox' className='CheckboxMain' name={name} />
</div>
)
interface ImageUploadFieldProps {
label: string
description: string
}
export const ImageUploadField = ({
label,
description
}: ImageUploadFieldProps) => (
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>{label}</label>
{description && <p className='labelDescriptionMain'>{description}</p>}
<div className='inputWrapperMain'>
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
type='text'
className='inputMain'
inputMode='url'
placeholder='We recommend to upload images to https://nostr.build/'
id={name}
type='checkbox'
className='CheckboxMain'
name={name}
checked={isChecked}
onChange={handleChange}
/>
<button className='btn btnMain btnMainRemove' type='button'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M135.2 17.69C140.6 6.848 151.7 0 163.8 0H284.2C296.3 0 307.4 6.848 312.8 17.69L320 32H416C433.7 32 448 46.33 448 64C448 81.67 433.7 96 416 96H32C14.33 96 0 81.67 0 64C0 46.33 14.33 32 32 32H128L135.2 17.69zM394.8 466.1C393.2 492.3 372.3 512 346.9 512H101.1C75.75 512 54.77 492.3 53.19 466.1L31.1 128H416L394.8 466.1z'></path>
</svg>
</button>
</div>
</div>
)
)
type RichTextEditorProps = {
content: string
updateContent: (updatedContent: string) => void
}
const RichTextEditor = ({ content, updateContent }: RichTextEditorProps) => {
const editor = useEditor({
extensions: [StarterKit, Link],
onUpdate: ({ editor }) => {
// Update the state when the editor content changes
updateContent(editor.getHTML())
},
content
})
// Update editor content when the `content` prop changes
useEffect(() => {
if (editor && editor.getHTML() !== content) {
editor.commands.setContent(content, false)
}
}, [content, editor])
return (
<div className='inputMain'>
{editor && (
<>
<MenuBar editor={editor} />
<EditorContent editor={editor} />
</>
)}
</div>
)
}
type MenuBarProps = {
editor: Editor
}
const MenuBar = ({ editor }: MenuBarProps) => {
const setLink = () => {
// Prompt the user to enter a URL
let url = prompt('URL')
// Check if the user provided a URL
if (url) {
// If the URL doesn't start with 'http://' or 'https://',
// prepend 'https://' to the URL
if (!/^(http|https):\/\//i.test(url)) {
url = `https://${url}`
}
return editor.chain().focus().setLink({ href: url }).run()
}
// If no URL was provided (e.g., the user cancels the prompt),
// return false, indicating that the link was not set.
return false
}
const unsetLink = () => editor.chain().focus().unsetLink().run()
const buttons: MenuBarButtonProps[] = [
{
label: 'Bold',
disabled: !editor.can().chain().focus().toggleBold().run(),
isActive: editor.isActive('bold'),
onClick: () => editor.chain().focus().toggleBold().run()
},
{
label: 'Italic',
disabled: !editor.can().chain().focus().toggleItalic().run(),
isActive: editor.isActive('italic'),
onClick: () => editor.chain().focus().toggleItalic().run()
},
{
label: 'Strike',
disabled: !editor.can().chain().focus().toggleStrike().run(),
isActive: editor.isActive('strike'),
onClick: () => editor.chain().focus().toggleStrike().run()
},
{
label: 'Clear marks',
onClick: () => editor.chain().focus().unsetAllMarks().run()
},
{
label: 'Clear nodes',
onClick: () => editor.chain().focus().clearNodes().run()
},
{
label: 'Paragraph',
isActive: editor.isActive('paragraph'),
onClick: () => editor.chain().focus().toggleStrike().run()
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
...[1, 2, 3, 4, 5, 6].map((level: any) => ({
label: `H${level}`,
isActive: editor.isActive('heading', { level }),
onClick: () => editor.chain().focus().toggleHeading({ level }).run()
})),
{
label: 'Bullet list',
isActive: editor.isActive('bulletList'),
onClick: () => editor.chain().focus().toggleBulletList().run()
},
{
label: 'Ordered list',
isActive: editor.isActive('orderedList'),
onClick: () => editor.chain().focus().toggleOrderedList().run()
},
{
label: 'Code block',
isActive: editor.isActive('codeBlock'),
onClick: () => editor.chain().focus().toggleCodeBlock().run()
},
{
label: 'Blockquote',
isActive: editor.isActive('blockquote'),
onClick: () => editor.chain().focus().toggleBlockquote().run()
},
{
label: 'Link',
isActive: editor.isActive('link'),
onClick: editor.isActive('link') ? unsetLink : setLink
},
{
label: 'Horizontal rule',
onClick: () => editor.chain().focus().setHorizontalRule().run()
},
{
label: 'Hard break',
onClick: () => editor.chain().focus().setHardBreak().run()
},
{
label: 'Undo',
disabled: !editor.can().chain().focus().undo().run(),
onClick: () => editor.chain().focus().undo().run()
},
{
label: 'Redo',
disabled: !editor.can().chain().focus().redo().run(),
onClick: () => editor.chain().focus().redo().run()
}
]
return (
<div className='control-group'>
<div className='button-group'>
{buttons.map(({ label, disabled, isActive, onClick }) => (
<MenuBarButton
key={label}
label={label}
disabled={disabled}
isActive={isActive}
onClick={onClick}
/>
))}
</div>
</div>
)
}
interface MenuBarButtonProps {
label: string
isActive?: boolean
disabled?: boolean
onClick: () => boolean
}
const MenuBarButton = ({
label,
isActive = false,
disabled = false,
onClick
}: MenuBarButtonProps) => (
<button
onClick={onClick}
disabled={disabled}
className={`btn btnMain btnMainTipTap ${isActive ? 'is-active' : ''}`}
>
{label}
</button>
)

View File

@ -0,0 +1,18 @@
import styles from '../../styles/loadingSpinner.module.scss'
interface Props {
desc: string
}
export const LoadingSpinner = (props: Props) => {
const { desc } = props
return (
<div className={styles.loadingSpinnerOverlay}>
<div className={styles.loadingSpinnerContainer}>
<div className={styles.loadingSpinner}></div>
{desc && <span>{desc}</span>}
</div>
</div>
)
}

View File

@ -1,35 +1,69 @@
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 = {
backgroundLink: string
}
export const ModCard = React.memo((props: ModDetails) => {
const [totalZappedAmount, setTotalZappedAmount] = useState(0)
const [commentCount, setCommentCount] = useState(0)
const { commentEvents } = useComments(props)
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 = ({ backgroundLink }: ModCardProps) => {
return (
<a className='cardModMainWrapperLink' href='mods-inner.html'>
<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>
)}
</div>
<div className='cMMBody'>
<h3 className='cMMBodyTitle'>
This is a mod title for an awesome game that will make everyone
happy! The happiest!
</h3>
<p className='cMMBodyText'>
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec
odio. Praesent libero. Sed cursus ante dapibus diam. Sed nisi. Nulla
quis sem at nibh elementum imperdiet. Duis sagittis ipsum. Praesent
mauris. Fusce nec tellus sed augue semper porta. Mauris massa.
Vestibulum lacinia arcu eget nulla. Class aptent taciti sociosqu ad
litora torquent per conubia nostra, per inceptos himenaeos.
Curabitur sodales ligula in libero.
<br />
<br />
</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'>
@ -43,7 +77,7 @@ export const ModCard = ({ backgroundLink }: ModCardProps) => {
>
<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
@ -55,7 +89,7 @@ export const ModCard = ({ backgroundLink }: ModCardProps) => {
>
<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
@ -67,11 +101,23 @@ export const ModCard = ({ backgroundLink }: ModCardProps) => {
>
<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>
)
}
})

View File

@ -1,26 +1,375 @@
import _ from 'lodash'
import { Event, kinds, nip19, UnsignedEvent } from 'nostr-tools'
import React, {
Fragment,
useCallback,
useEffect,
useMemo,
useRef,
useState
} from 'react'
import { useLocation, useNavigate } from 'react-router-dom'
import { toast } from 'react-toastify'
import { FixedSizeList as List } from 'react-window'
import { v4 as uuidv4 } from 'uuid'
import { T_TAG_VALUE } from '../constants'
import { useAppSelector, useGames, useNDKContext } from '../hooks'
import { appRoutes, getModPageRoute } from '../routes'
import '../styles/styles.css'
import { CheckboxField, ImageUploadField, InputField } from './Inputs'
import { DownloadUrl, ModDetails, ModFormState } from '../types'
import {
initializeFormState,
isReachable,
isValidImageUrl,
isValidUrl,
log,
LogType,
now
} from '../utils'
import { CheckboxField, InputError, InputField } from './Inputs'
import { LoadingSpinner } from './LoadingSpinner'
import { NDKEvent } from '@nostr-dev-kit/ndk'
interface FormErrors {
game?: string
title?: string
body?: string
featuredImageUrl?: string
summary?: string
nsfw?: string
screenshotsUrls?: string[]
tags?: string
downloadUrls?: string[]
}
interface GameOption {
value: string
label: string
}
type ModFormProps = {
existingModData?: ModDetails
}
export const ModForm = ({ existingModData }: ModFormProps) => {
const location = useLocation()
const navigate = useNavigate()
const { ndk, publish } = useNDKContext()
const games = useGames()
const userState = useAppSelector((state) => state.user)
const [isPublishing, setIsPublishing] = useState(false)
const [gameOptions, setGameOptions] = useState<GameOption[]>([])
const [formState, setFormState] = useState<ModFormState>(
initializeFormState()
)
const [formErrors, setFormErrors] = useState<FormErrors>({})
useEffect(() => {
if (location.pathname === appRoutes.submitMod) {
setFormState(initializeFormState())
}
}, [location.pathname]) // Only trigger when the pathname changes to submit-mod
useEffect(() => {
if (existingModData) {
setFormState(initializeFormState(existingModData))
}
}, [existingModData])
useEffect(() => {
const options = games.map((game) => ({
label: game['Game Name'],
value: game['Game Name']
}))
setGameOptions(options)
}, [games])
const handleInputChange = useCallback((name: string, value: string) => {
setFormState((prevState) => ({
...prevState,
[name]: value
}))
}, [])
const handleCheckboxChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
const { name, checked } = e.target
setFormState((prevState) => ({
...prevState,
[name]: checked
}))
},
[]
)
const addScreenshotUrl = useCallback(() => {
setFormState((prevState) => ({
...prevState,
screenshotsUrls: [...prevState.screenshotsUrls, '']
}))
}, [])
const removeScreenshotUrl = useCallback((index: number) => {
setFormState((prevState) => ({
...prevState,
screenshotsUrls: prevState.screenshotsUrls.filter((_, i) => i !== index)
}))
}, [])
const handleScreenshotUrlChange = useCallback(
(index: number, value: string) => {
setFormState((prevState) => ({
...prevState,
screenshotsUrls: [
...prevState.screenshotsUrls.map((url, i) => {
if (index === i) return value
return url
})
]
}))
},
[]
)
const addDownloadUrl = useCallback(() => {
setFormState((prevState) => ({
...prevState,
downloadUrls: [
...prevState.downloadUrls,
{
url: '',
hash: '',
signatureKey: '',
malwareScanLink: '',
modVersion: '',
customNote: ''
}
]
}))
}, [])
const removeDownloadUrl = useCallback((index: number) => {
setFormState((prevState) => ({
...prevState,
downloadUrls: prevState.downloadUrls.filter((_, i) => i !== index)
}))
}, [])
const handleDownloadUrlChange = useCallback(
(index: number, field: keyof DownloadUrl, value: string) => {
setFormState((prevState) => ({
...prevState,
downloadUrls: [
...prevState.downloadUrls.map((url, i) => {
if (index === i) url[field] = value
return url
})
]
}))
},
[]
)
const handlePublish = async () => {
setIsPublishing(true)
let hexPubkey: string
if (userState.auth && userState.user?.pubkey) {
hexPubkey = userState.user.pubkey as string
} else {
hexPubkey = (await window.nostr?.getPublicKey()) as string
}
if (!hexPubkey) {
toast.error('Could not get pubkey')
return
}
if (!(await validateState())) {
setIsPublishing(false)
return
}
const uuid = uuidv4()
const currentTimeStamp = now()
const aTag =
formState.aTag || `${kinds.ClassifiedListing}:${hexPubkey}:${uuid}`
const unsignedEvent: UnsignedEvent = {
kind: kinds.ClassifiedListing,
created_at: currentTimeStamp,
pubkey: hexPubkey,
content: formState.body,
tags: [
['d', formState.dTag || uuid],
['a', aTag],
['r', formState.rTag],
['t', T_TAG_VALUE],
[
'published_at',
existingModData
? existingModData.published_at.toString()
: currentTimeStamp.toString()
],
['game', formState.game],
['title', formState.title],
['featuredImageUrl', formState.featuredImageUrl],
['summary', formState.summary],
['nsfw', formState.nsfw.toString()],
['screenshotsUrls', ...formState.screenshotsUrls],
['tags', ...formState.tags.split(',')],
[
'downloadUrls',
...formState.downloadUrls.map((downloadUrl) =>
JSON.stringify(downloadUrl)
)
]
]
}
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) {
setIsPublishing(false)
return
}
const ndkEvent = new NDKEvent(ndk, signedEvent)
const publishedOnRelays = await publish(ndkEvent)
// Handle cases where publishing failed or succeeded
if (publishedOnRelays.length === 0) {
toast.error('Failed to publish event on any relay')
} else {
toast.success(
`Event published successfully to the following relays\n\n${publishedOnRelays.join(
'\n'
)}`
)
const naddr = nip19.naddrEncode({
identifier: aTag,
pubkey: signedEvent.pubkey,
kind: signedEvent.kind,
relays: publishedOnRelays
})
navigate(getModPageRoute(naddr))
}
setIsPublishing(false)
}
const validateState = async (): Promise<boolean> => {
const errors: FormErrors = {}
if (formState.game === '') {
errors.game = 'Game field can not be empty'
}
if (formState.title === '') {
errors.title = 'Title field can not be empty'
}
if (formState.body === '') {
errors.body = 'Body field can not be empty'
}
if (formState.featuredImageUrl === '') {
errors.featuredImageUrl = 'FeaturedImageUrl field can not be empty'
} else if (
!isValidImageUrl(formState.featuredImageUrl) ||
!(await isReachable(formState.featuredImageUrl))
) {
errors.featuredImageUrl =
'FeaturedImageUrl must be a valid and reachable image URL'
}
if (formState.summary === '') {
errors.summary = 'Summary field can not be empty'
}
if (formState.screenshotsUrls.length === 0) {
errors.screenshotsUrls = ['Required at least one screenshot url']
} else {
for (let i = 0; i < formState.screenshotsUrls.length; i++) {
const url = formState.screenshotsUrls[i]
if (
!isValidUrl(url) ||
!isValidImageUrl(url) ||
!(await isReachable(url))
) {
if (!errors.screenshotsUrls)
errors.screenshotsUrls = Array(formState.screenshotsUrls.length)
errors.screenshotsUrls![i] =
'All screenshot URLs must be valid and reachable image URLs'
}
}
}
if (formState.tags === '') {
errors.tags = 'Tags field can not be empty'
}
if (formState.downloadUrls.length === 0) {
errors.downloadUrls = ['Required at least one download url']
} else {
for (let i = 0; i < formState.downloadUrls.length; i++) {
const downloadUrl = formState.downloadUrls[i]
if (!isValidUrl(downloadUrl.url)) {
if (!errors.downloadUrls)
errors.downloadUrls = Array(formState.downloadUrls.length)
errors.downloadUrls![i] = 'Download url must be valid and reachable'
}
}
}
setFormErrors(errors)
return Object.keys(errors).length === 0
}
export const ModForm = () => {
return (
<>
<InputField
label='Game'
description="Can't find the game you're looking for? Send us a DM mentioning it so we can add it."
placeholder='The mod is for a game called...'
name='game'
{isPublishing && <LoadingSpinner desc='Publishing mod to relays' />}
<GameDropdown
options={gameOptions}
selected={formState.game}
error={formErrors.game}
onChange={handleInputChange}
/>
<InputField
label='Title'
placeholder='Return the banana mod'
name='title'
value={formState.title}
error={formErrors.title}
onChange={handleInputChange}
/>
<InputField
label='Body'
type='textarea'
type='richtext'
placeholder="Here's what this mod is all about"
name='body'
value={formState.body}
error={formErrors.body}
onChange={handleInputChange}
/>
<InputField
label='Featured Image URL'
description='We recommend to upload images to https://nostr.build/'
@ -28,29 +377,85 @@ export const ModForm = () => {
inputMode='url'
placeholder='Image URL'
name='featuredImageUrl'
value={formState.featuredImageUrl}
error={formErrors.featuredImageUrl}
onChange={handleInputChange}
/>
<InputField
label='Summary'
type='textarea'
placeholder='This is a quick description of my mod'
name='summary'
value={formState.summary}
error={formErrors.summary}
onChange={handleInputChange}
/>
<CheckboxField label='This mod not safe for work (NSFW)' name='nsfw' />
<ImageUploadField
label='Screenshots URLs'
description='We recommend to upload images to https://nostr.build/'
<CheckboxField
label='This mod not safe for work (NSFW)'
name='nsfw'
isChecked={formState.nsfw}
handleChange={handleCheckboxChange}
type='stylized'
/>
<div className='inputLabelWrapperMain'>
<div className='labelWrapperMain'>
<label className='form-label labelMain'>Screenshots URLs</label>
<button
className='btn btnMain btnMainAdd'
type='button'
onClick={addScreenshotUrl}
>
<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>
<p className='labelDescriptionMain'>
We recommend to upload images to https://nostr.build/
</p>
{formState.screenshotsUrls.map((url, index) => (
<Fragment key={`screenShot-${index}`}>
<ScreenshotUrlFields
index={index}
url={url}
onUrlChange={handleScreenshotUrlChange}
onRemove={removeScreenshotUrl}
/>
{formErrors.screenshotsUrls &&
formErrors.screenshotsUrls[index] && (
<InputError message={formErrors.screenshotsUrls[index]} />
)}
</Fragment>
))}
{formState.screenshotsUrls.length === 0 &&
formErrors.screenshotsUrls &&
formErrors.screenshotsUrls[0] && (
<InputError message={formErrors.screenshotsUrls[0]} />
)}
</div>
<InputField
label='Tags'
description='Separate each tag with a comma. (Example: tag1, tag2, tag3)'
placeholder='Tags'
name='tags'
value={formState.tags}
error={formErrors.tags}
onChange={handleInputChange}
/>
<div className='inputLabelWrapperMain'>
<div className='labelWrapperMain'>
<label className='form-label labelMain'>Download URLs</label>
<button className='btn btnMain btnMainAdd' type='button'>
<button
className='btn btnMain btnMainAdd'
type='button'
onClick={addDownloadUrl}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
@ -64,29 +469,248 @@ export const ModForm = () => {
</div>
<p className='labelDescriptionMain'>
You can upload your game mod to Github, as an example, and keep
updating it there. Also, its advisable that you hash your package as
well with your nostr public key.
updating it there (another option is catbox.moe). Also, it's advisable
that you hash your package as well with your nostr public key.
</p>
<DownloadUrlFields />
<DownloadUrlFields />
{formState.downloadUrls.map((download, index) => (
<Fragment key={`download-${index}`}>
<DownloadUrlFields
index={index}
url={download.url}
hash={download.hash}
signatureKey={download.signatureKey}
malwareScanLink={download.malwareScanLink}
modVersion={download.modVersion}
customNote={download.customNote}
onUrlChange={handleDownloadUrlChange}
onRemove={removeDownloadUrl}
/>
{formErrors.downloadUrls && formErrors.downloadUrls[index] && (
<InputError message={formErrors.downloadUrls[index]} />
)}
</Fragment>
))}
{formState.downloadUrls.length === 0 &&
formErrors.downloadUrls &&
formErrors.downloadUrls[0] && (
<InputError message={formErrors.downloadUrls[0]} />
)}
</div>
<div className='IBMSMSMBS_WriteAction'>
<button
className='btn btnMain'
type='button'
onClick={handlePublish}
disabled={isPublishing}
>
Publish
</button>
</div>
</>
)
}
type DownloadUrlFieldsProps = {
index: number
url: string
hash: string
signatureKey: string
malwareScanLink: string
modVersion: string
customNote: string
onUrlChange: (index: number, field: keyof DownloadUrl, value: string) => void
onRemove: (index: number) => void
}
const DownloadUrlFields = () => {
return (
<div className='inputWrapperMainWrapper'>
const DownloadUrlFields = React.memo(
({
index,
url,
hash,
signatureKey,
malwareScanLink,
modVersion,
customNote,
onUrlChange,
onRemove
}: DownloadUrlFieldsProps) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target
onUrlChange(index, name as keyof DownloadUrl, value)
}
return (
<div className='inputWrapperMainWrapper'>
<div className='inputWrapperMain'>
<input
type='text'
className='inputMain'
name='url'
placeholder='Download URL'
value={url}
onChange={handleChange}
/>
<button
className='btn btnMain btnMainRemove'
type='button'
onClick={() => onRemove(index)}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M135.2 17.69C140.6 6.848 151.7 0 163.8 0H284.2C296.3 0 307.4 6.848 312.8 17.69L320 32H416C433.7 32 448 46.33 448 64C448 81.67 433.7 96 416 96H32C14.33 96 0 81.67 0 64C0 46.33 14.33 32 32 32H128L135.2 17.69zM394.8 466.1C393.2 492.3 372.3 512 346.9 512H101.1C75.75 512 54.77 492.3 53.19 466.1L31.1 128H416L394.8 466.1z'></path>
</svg>
</button>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
name='hash'
placeholder='SHA-256 Hash'
value={hash}
onChange={handleChange}
/>
<div className='inputWrapperMainBox'></div>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
placeholder='Signature public key'
name='signatureKey'
value={signatureKey}
onChange={handleChange}
/>
<div className='inputWrapperMainBox'></div>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
name='malwareScanLink'
placeholder='Malware Scan Link'
value={malwareScanLink}
onChange={handleChange}
/>
<div className='inputWrapperMainBox'></div>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
placeholder='Mod version (1.0)'
name='modVersion'
value={modVersion}
onChange={handleChange}
/>
<div className='inputWrapperMainBox'></div>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
placeholder='Custom note/message'
name='customNote'
value={customNote}
onChange={handleChange}
/>
<div className='inputWrapperMainBox'></div>
</div>
</div>
)
}
)
type ScreenshotUrlFieldsProps = {
index: number
url: string
onUrlChange: (index: number, value: string) => void
onRemove: (index: number) => void
}
const ScreenshotUrlFields = React.memo(
({ index, url, onUrlChange, onRemove }: ScreenshotUrlFieldsProps) => {
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
onUrlChange(index, e.target.value)
}
return (
<div className='inputWrapperMain'>
<input
type='text'
className='inputMain'
inputMode='url'
placeholder='https://...'
value='https://github.com/'
placeholder='We recommend to upload images to https://nostr.build/'
value={url}
onChange={handleChange}
/>
<button className='btn btnMain btnMainRemove' type='button'>
<button
className='btn btnMain btnMainRemove'
type='button'
onClick={() => onRemove(index)}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
@ -94,111 +718,122 @@ const DownloadUrlFields = () => {
height='1em'
fill='currentColor'
>
<path d='M135.2 17.69C140.6 6.848 151.7 0 163.8 0H284.2C296.3 0 307.4 6.848 312.8 17.69L320 32H416C433.7 32 448 46.33 448 64C448 81.67 433.7 96 416 96H32C14.33 96 0 81.67 0 64C0 46.33 14.33 32 32 32H128L135.2 17.69zM394.8 466.1C393.2 492.3 372.3 512 346.9 512H101.1C75.75 512 54.77 492.3 53.19 466.1L31.1 128H416L394.8 466.1z'></path>
<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 className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
)
}
)
type GameDropdownProps = {
options: GameOption[]
selected: string
error?: string
onChange: (name: string, value: string) => void
}
const GameDropdown = ({
options,
selected,
error,
onChange
}: GameDropdownProps) => {
const [searchTerm, setSearchTerm] = useState('')
const [debouncedSearchTerm, setDebouncedSearchTerm] = useState('')
const inputRef = useRef<HTMLInputElement>(null)
const isOptionClicked = useRef(false)
const handleSearchChange = useCallback((value: string) => {
setSearchTerm(value)
_.debounce(() => {
setDebouncedSearchTerm(value)
}, 300)()
}, [])
const filteredOptions = useMemo(() => {
if (debouncedSearchTerm === '') return []
else {
return options.filter((option) =>
option.label.toLowerCase().includes(debouncedSearchTerm.toLowerCase())
)
}
}, [debouncedSearchTerm, options])
return (
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>Game</label>
<p className='labelDescriptionMain'>
Can't find the game you're looking for? You can temporarily publish the
mod under '(Unlisted Game)' and later edit it with the proper game name
once we add it.
</p>
<div className='dropdown dropdownMain'>
<div className='inputWrapperMain inputWrapperMainAlt'>
<input
ref={inputRef}
type='text'
className='inputMain inputMainWithBtn dropdown-toggle'
placeholder='This mod is for a game called...'
aria-expanded='false'
data-bs-toggle='dropdown'
value={searchTerm || selected}
onChange={(e) => handleSearchChange(e.target.value)}
onBlur={() => {
if (!isOptionClicked.current) {
setSearchTerm('')
}
isOptionClicked.current = false
}}
/>
<button
className='btn btnMain btnMainInsideField btnMainRemove'
type='button'
onClick={() => onChange('game', '')}
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
<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'>
<List
height={500}
width={'100%'}
itemCount={filteredOptions.length}
itemSize={35}
>
{({ index, style }) => (
<div
style={style}
className='dropdown-item dropdownMainMenuItem'
onMouseDown={() => (isOptionClicked.current = true)}
onClick={() => {
onChange('game', filteredOptions[index].value)
setSearchTerm('')
if (inputRef.current) {
inputRef.current.blur()
}
}}
>
{filteredOptions[index].label}
</div>
)}
</List>
</div>
</div>
<input
type='text'
className='inputMain'
inputMode='url'
placeholder='SHA-256 Hash'
/>
<div className='inputWrapperMainBox'></div>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
inputMode='url'
placeholder='Signature public key'
value='npub18n4ysp43ux5c98fs6h9c57qpr4p8r3j8f6e32v0vj8egzy878aqqyzzk9r'
/>
<div className='inputWrapperMainBox'></div>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
inputMode='url'
placeholder='Malware scan link'
/>
<div className='inputWrapperMainBox'></div>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
inputMode='url'
placeholder='Mod version (1.0)'
/>
<div className='inputWrapperMainBox'></div>
</div>
<div className='inputWrapperMain'>
<div className='inputWrapperMainBox'>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-96 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M320 448c0 17.67-14.31 32-32 32H64c-17.69 0-32-14.33-32-32v-384C32 46.34 46.31 32.01 64 32.01S96 46.34 96 64.01v352h192C305.7 416 320 430.3 320 448z'></path>
</svg>
</div>
<input
type='text'
className='inputMain'
inputMode='url'
placeholder='Custome note/message'
/>
<div className='inputWrapperMainBox'></div>
</div>
{error && <InputError message={error} />}
<p className='labelDescriptionMain'>
Note: Please mention the game name in the body text of your mod post
(e.g., 'This is a mod for Game Name') so we know what to look for and
add.
</p>
</div>
)
}

View File

@ -0,0 +1,164 @@
import { useAppSelector, useLocalStorage } from 'hooks'
import React from 'react'
import { FilterOptions, ModeratedFilter, NSFWFilter, SortBy } from 'types'
import { DEFAULT_FILTER_OPTIONS } from 'utils'
type Props = {
author?: string | undefined
filterKey?: string | undefined
}
export const ModFilter = React.memo(
({ author, filterKey = 'filter' }: Props) => {
const userState = useAppSelector((state) => state.user)
const [filterOptions, setFilterOptions] = useLocalStorage<FilterOptions>(
filterKey,
DEFAULT_FILTER_OPTIONS
)
return (
<div className='IBMSecMain'>
<div className='FiltersMain'>
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.sort}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(SortBy).map((item, index) => (
<div
key={`sortByItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
sort: item
}))
}
>
{item}
</div>
))}
</div>
</div>
</div>
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.moderated}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(ModeratedFilter).map((item, index) => {
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 (
<div
key={`moderatedFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
moderated: item
}))
}
>
{item}
</div>
)
})}
</div>
</div>
</div>
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.nsfw}
</button>
<div className='dropdown-menu dropdownMainMenu'>
{Object.values(NSFWFilter).map((item, index) => (
<div
key={`nsfwFilterItem-${index}`}
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
nsfw: item
}))
}
>
{item}
</div>
))}
</div>
</div>
</div>
<div className='FiltersMainElement'>
<div className='dropdown dropdownMain'>
<button
className='btn dropdown-toggle btnMain btnMainDropdown'
aria-expanded='false'
data-bs-toggle='dropdown'
type='button'
>
{filterOptions.source === window.location.host
? `Show From: ${filterOptions.source}`
: 'Show All'}
</button>
<div className='dropdown-menu dropdownMainMenu'>
<div
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: window.location.host
}))
}
>
Show From: {window.location.host}
</div>
<div
className='dropdown-item dropdownMainMenuItem'
onClick={() =>
setFilterOptions((prev) => ({
...prev,
source: 'Show All'
}))
}
>
Show All
</div>
</div>
</div>
</div>
</div>
</div>
)
}
)

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

@ -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' && (
<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' && (
<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,330 @@ 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 {
return (await window.nostr?.getPublicKey()) as string
}
}
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} />}
</>
)
}

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>
)
)

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>
)
}

918
src/components/Zap.tsx Normal file
View File

@ -0,0 +1,918 @@
import { getRelayListForUser } from '@nostr-dev-kit/ndk'
import { QRCodeSVG } from 'qrcode.react'
import React, {
Dispatch,
ReactNode,
SetStateAction,
useCallback,
useMemo,
useState
} from 'react'
import Countdown, { CountdownRenderProps } from 'react-countdown'
import { toast } from 'react-toastify'
import { ZapController } from '../controllers'
import { useAppSelector, useDidMount, useNDKContext } from '../hooks'
import '../styles/popup.css'
import { PaymentRequest, UserProfile } from '../types'
import {
copyTextToClipboard,
formatNumber,
getTagValue,
getZapAmount,
unformatNumber
} from '../utils'
import { LoadingSpinner } from './LoadingSpinner'
import { FALLBACK_PROFILE_IMAGE } from 'constants.ts'
type PresetAmountProps = {
label: string
value: number
setAmount: Dispatch<SetStateAction<number>>
}
export const PresetAmount = React.memo(
({ label, value, setAmount }: PresetAmountProps) => {
return (
<button
className='btn btnMain pUMCB_ZapsInsideAmountOptionsBtn'
type='button'
onClick={() => setAmount(value)}
>
<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' />
</svg>
{label}
</button>
)
}
)
type ZapPresetsProps = {
setAmount: Dispatch<SetStateAction<number>>
}
export const ZapPresets = React.memo(({ setAmount }: ZapPresetsProps) => {
return (
<>
<PresetAmount label='1K' value={1000} setAmount={setAmount} />
<PresetAmount label='5K' value={5000} setAmount={setAmount} />
<PresetAmount label='10K' value={10000} setAmount={setAmount} />
<PresetAmount label='25K' value={25000} setAmount={setAmount} />
</>
)
})
type ZapButtonsProps = {
disabled: boolean
handleGenerateQRCode: () => void
handleSend: () => void
}
export const ZapButtons = ({
disabled,
handleGenerateQRCode,
handleSend
}: ZapButtonsProps) => {
return (
<div className='pUMCB_ZapsInsideBtns'>
<button
className='btn btnMain pUMCB_ZapsInsideElementBtn'
type='button'
onClick={handleGenerateQRCode}
disabled={disabled}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-32 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<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>
</button>
<button
className='btn btnMain pUMCB_ZapsInsideElementBtn'
type='button'
onClick={handleSend}
disabled={disabled}
>
<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' />
</svg>
Send
</button>
</div>
)
}
type ZapQRProps = {
paymentRequest: PaymentRequest
handleClose: () => void
handleQRExpiry: () => void
setTotalZapAmount?: Dispatch<SetStateAction<number>>
setHasZapped?: Dispatch<SetStateAction<boolean>>
}
export const ZapQR = React.memo(
({
paymentRequest,
handleClose,
handleQRExpiry,
setTotalZapAmount,
setHasZapped
}: ZapQRProps) => {
const { ndk } = useNDKContext()
useDidMount(() => {
ZapController.getInstance()
.pollZapReceipt(paymentRequest, ndk)
.then((zapReceipt) => {
toast.success(`Successfully sent sats!`)
if (setTotalZapAmount) {
const amount = getZapAmount(zapReceipt)
setTotalZapAmount((prev) => prev + amount)
if (setHasZapped) setHasZapped(true)
}
})
.catch((err) => {
toast.error(err.message || err)
})
.finally(() => {
handleClose()
})
})
const onQrCodeClicked = async () => {
if (!paymentRequest) return
const zapController = ZapController.getInstance()
if (await zapController.isWeblnProviderExists()) {
zapController.sendPayment(paymentRequest.pr)
} else {
console.warn('Webln provider not present')
const href = `lightning:${paymentRequest.pr}`
const a = document.createElement('a')
a.href = href
a.click()
}
}
return (
<div className='inputLabelWrapperMain' style={{ alignItems: 'center' }}>
<QRCodeSVG
className='popUpMainCardBottomQR'
onClick={onQrCodeClicked}
value={paymentRequest.pr}
height={235}
width={235}
/>
<label
className='popUpMainCardBottomLnurl'
onClick={() => {
copyTextToClipboard(paymentRequest.pr).then((isCopied) => {
if (isCopied) toast.success('Lnurl copied to clipboard!')
})
}}
>
{paymentRequest.pr}
</label>
<Timer onTimerExpired={handleQRExpiry} />
</div>
)
}
)
const MAX_POLLING_TIME = 2 * 60 * 1000 // 2 minutes in milliseconds
const renderer = ({ minutes, seconds }: CountdownRenderProps) => (
<span>
{minutes}:{seconds}
</span>
)
type TimerProps = {
onTimerExpired: () => void
}
const Timer = React.memo(({ onTimerExpired }: TimerProps) => {
const expiryTime = useMemo(() => {
return Date.now() + MAX_POLLING_TIME
}, [])
return (
<div>
<i className='fas fa-clock'></i>
<Countdown
date={expiryTime}
renderer={renderer}
onComplete={onTimerExpired}
/>
</div>
)
})
type ZapPopUpProps = {
title: string
labelDescriptionMain?: ReactNode
receiver: string
eventId?: string
aTag?: string
notCloseAfterZap?: boolean
lastNode?: ReactNode
setTotalZapAmount?: Dispatch<SetStateAction<number>>
setHasZapped?: Dispatch<SetStateAction<boolean>>
handleClose: () => void
}
export const ZapPopUp = ({
title,
labelDescriptionMain,
receiver,
eventId,
aTag,
lastNode,
notCloseAfterZap,
setTotalZapAmount,
setHasZapped,
handleClose
}: ZapPopUpProps) => {
const { ndk, findMetadata } = useNDKContext()
const [isLoading, setIsLoading] = useState(false)
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
const [amount, setAmount] = useState<number>(0)
const [message, setMessage] = useState('')
const [paymentRequest, setPaymentRequest] = useState<PaymentRequest>()
const userState = useAppSelector((state) => state.user)
const handleAmountChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const unformattedValue = unformatNumber(event.target.value)
setAmount(unformattedValue)
}
const generatePaymentRequest =
useCallback(async (): Promise<PaymentRequest | null> => {
let userHexKey: string
setIsLoading(true)
setLoadingSpinnerDesc('Getting user pubkey')
if (userState.auth && userState.user?.pubkey) {
userHexKey = userState.user.pubkey as string
} else {
userHexKey = (await window.nostr?.getPublicKey()) as string
}
if (!userHexKey) {
setIsLoading(false)
toast.error('Could not get pubkey')
return null
}
setLoadingSpinnerDesc('finding receiver metadata')
const receiverMetadata = await findMetadata(receiver)
if (!receiverMetadata?.lud16) {
setIsLoading(false)
toast.error('Lighting address (lud16) is missing in receiver metadata!')
return null
}
if (!receiverMetadata?.pubkey) {
setIsLoading(false)
toast.error('pubkey is missing in receiver metadata!')
return null
}
// Find the receiver's read relays.
const receiverRelays = await getRelayListForUser(receiver, ndk)
.then((ndkRelayList) => {
if (ndkRelayList) return ndkRelayList.readRelayUrls
return [] // Return an empty array if ndkRelayList is undefined
})
.catch((err) => {
console.error(
`An error occurred in getting zap receiver's read relays`,
err
)
return [] as string[]
})
const zapController = ZapController.getInstance()
setLoadingSpinnerDesc('Creating zap request')
return await zapController
.getLightningPaymentRequest(
receiverMetadata.lud16,
amount,
receiverMetadata.pubkey as string,
receiverRelays,
userHexKey,
message,
eventId,
aTag
)
.catch((err) => {
toast.error(err.message || err)
return null
})
.finally(() => {
setIsLoading(false)
})
}, [amount, message, userState, receiver, eventId, aTag, ndk, findMetadata])
const handleGenerateQRCode = async () => {
const pr = await generatePaymentRequest()
if (!pr) return
setPaymentRequest(pr)
}
const handleSend = useCallback(async () => {
const pr = await generatePaymentRequest()
if (!pr) return
setIsLoading(true)
setLoadingSpinnerDesc('Sending payment!')
const zapController = ZapController.getInstance()
if (await zapController.isWeblnProviderExists()) {
await zapController
.sendPayment(pr.pr)
.then(() => {
toast.success(`Successfully sent ${amount} sats!`)
if (setTotalZapAmount) {
setTotalZapAmount((prev) => prev + amount)
if (setHasZapped) setHasZapped(true)
}
if (!notCloseAfterZap) {
handleClose()
}
})
.catch((err) => {
toast.error(err.message || err)
})
} else {
toast.warn('Webln is not present. Use QR code to send zap.')
setPaymentRequest(pr)
}
setIsLoading(false)
}, [
amount,
notCloseAfterZap,
handleClose,
generatePaymentRequest,
setTotalZapAmount,
setHasZapped
])
const handleQRExpiry = useCallback(() => {
setPaymentRequest(undefined)
}, [])
const handleQRClose = useCallback(() => {
setPaymentRequest(undefined)
setIsLoading(false)
if (!notCloseAfterZap) {
handleClose()
}
}, [notCloseAfterZap, handleClose])
return (
<>
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
<div className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard popUpMainCardQR'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>{title}</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' />
</svg>
</div>
</div>
<div className='pUMCB_Zaps'>
<div className='pUMCB_ZapsInside'>
<div className='pUMCB_ZapsInsideAmount'>
<div className='inputLabelWrapperMain'>
{labelDescriptionMain}
<label className='form-label labelMain'>
Amount (Satoshis)
</label>
<input
className='inputMain'
type='text'
inputMode='numeric'
value={amount ? formatNumber(amount) : ''}
onChange={handleAmountChange}
/>
</div>
<div className='pUMCB_ZapsInsideAmountOptions'>
<ZapPresets setAmount={setAmount} />
</div>
</div>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>
Message (optional)
</label>
<input
type='text'
className='inputMain'
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</div>
<ZapButtons
disabled={!amount}
handleGenerateQRCode={handleGenerateQRCode}
handleSend={handleSend}
/>
{paymentRequest && (
<ZapQR
paymentRequest={paymentRequest}
handleClose={handleQRClose}
handleQRExpiry={handleQRExpiry}
setTotalZapAmount={setTotalZapAmount}
setHasZapped={setHasZapped}
/>
)}
{lastNode}
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}
type ZapSplitProps = {
pubkey: string
eventId?: string
aTag?: string
setTotalZapAmount?: Dispatch<SetStateAction<number>>
setHasZapped?: Dispatch<SetStateAction<boolean>>
handleClose: () => void
}
export const ZapSplit = ({
pubkey,
eventId,
aTag,
setTotalZapAmount,
setHasZapped,
handleClose
}: ZapSplitProps) => {
const { ndk, findMetadata } = useNDKContext()
const [isLoading, setIsLoading] = useState(false)
const [loadingSpinnerDesc, setLoadingSpinnerDesc] = useState('')
const [amount, setAmount] = useState<number>(0)
const [message, setMessage] = useState('')
const [authorPercentage, setAuthorPercentage] = useState(90)
const [adminPercentage, setAdminPercentage] = useState(10)
const [author, setAuthor] = useState<UserProfile>()
const [admin, setAdmin] = useState<UserProfile>()
const userState = useAppSelector((state) => state.user)
const [invoices, setInvoices] = useState<Map<string, PaymentRequest>>()
useDidMount(async () => {
findMetadata(pubkey).then((res) => {
setAuthor(res)
})
const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
findMetadata(adminNpubs[0]).then((res) => {
setAdmin(res)
})
})
const handleAuthorPercentageChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
const newValue = parseInt(e.target.value)
setAuthorPercentage(newValue)
setAdminPercentage(100 - newValue)
}
const handleAdminPercentageChange = (
e: React.ChangeEvent<HTMLInputElement>
) => {
const newValue = parseInt(e.target.value)
setAdminPercentage(newValue)
setAuthorPercentage(100 - newValue) // Update the other slider to maintain 100%
}
const handleAmountChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const unformattedValue = unformatNumber(event.target.value)
setAmount(unformattedValue)
}
const generatePaymentInvoices = async () => {
if (!amount) return null
let userHexKey: string
setIsLoading(true)
setLoadingSpinnerDesc('Getting user pubkey')
if (userState.auth && userState.user?.pubkey) {
userHexKey = userState.user.pubkey as string
} else {
userHexKey = (await window.nostr?.getPublicKey()) as string
}
if (!userHexKey) {
setIsLoading(false)
toast.error('Could not get pubkey')
return null
}
const adminShare = Math.floor((amount * adminPercentage) / 100)
const authorShare = amount - adminShare
const zapController = ZapController.getInstance()
const invoices = new Map<string, PaymentRequest>()
if (authorShare > 0 && author?.pubkey && author?.lud16) {
// Find the receiver's read relays.
const authorRelays = await getRelayListForUser(
author.pubkey as string,
ndk
)
.then((ndkRelayList) => {
if (ndkRelayList) return ndkRelayList.readRelayUrls
return [] // Return an empty array if ndkRelayList is undefined
})
.catch((err) => {
console.error(
`An error occurred in getting zap receiver's read relays`,
err
)
return [] as string[]
})
setLoadingSpinnerDesc('Generating invoice for author')
const invoice = await zapController
.getLightningPaymentRequest(
author.lud16,
authorShare,
author.pubkey as string,
authorRelays,
userHexKey,
message,
eventId,
aTag
)
.catch((err) => {
toast.error(err.message || err)
return null
})
if (invoice) {
invoices.set('author', invoice)
}
}
if (adminShare > 0 && admin?.pubkey && admin?.lud16) {
// Find the receiver's read relays.
const adminRelays = await getRelayListForUser(admin.pubkey as string, ndk)
.then((ndkRelayList) => {
if (ndkRelayList) return ndkRelayList.readRelayUrls
return [] // Return an empty array if ndkRelayList is undefined
})
.catch((err) => {
console.error(
`An error occurred in getting zap receiver's read relays`,
err
)
return [] as string[]
})
setLoadingSpinnerDesc('Generating invoice for site owner')
const invoice = await zapController
.getLightningPaymentRequest(
admin.lud16,
adminShare,
admin.pubkey as string,
adminRelays,
userHexKey,
message,
eventId,
aTag
)
.catch((err) => {
toast.error(err.message || err)
return null
})
if (invoice) {
invoices.set('admin', invoice)
}
}
setIsLoading(false)
return invoices
}
const handleGenerateQRCode = async () => {
const paymentInvoices = await generatePaymentInvoices()
if (!paymentInvoices) return
setInvoices(paymentInvoices)
}
const handleSend = async () => {
const paymentInvoices = await generatePaymentInvoices()
if (!paymentInvoices) return
setIsLoading(true)
setLoadingSpinnerDesc('Sending payment!')
const zapController = ZapController.getInstance()
if (await zapController.isWeblnProviderExists()) {
const authorInvoice = paymentInvoices.get('author')
if (authorInvoice) {
setLoadingSpinnerDesc('Sending payment to author')
const sats = parseInt(getTagValue(authorInvoice, 'amount')![0]) / 1000
await zapController
.sendPayment(authorInvoice.pr)
.then(() => {
toast.success(`Successfully sent ${sats} sats to author!`)
if (setTotalZapAmount) {
setTotalZapAmount((prev) => prev + sats)
if (setHasZapped) setHasZapped(true)
}
})
.catch((err) => {
toast.error(err.message || err)
})
}
const adminInvoice = paymentInvoices.get('admin')
if (adminInvoice) {
setLoadingSpinnerDesc('Sending payment to site owner')
const sats = parseInt(getTagValue(adminInvoice, 'amount')![0]) / 1000
await zapController
.sendPayment(adminInvoice.pr)
.then(() => {
toast.success(`Successfully sent ${sats} sats to site owner!`)
})
.catch((err) => {
toast.error(err.message || err)
})
}
handleClose()
} else {
toast.warn('Webln is not present. Use QR code to send zap.')
setInvoices(paymentInvoices)
}
}
const removeInvoice = (key: string) => {
setInvoices((prev) => {
const newMap = new Map(prev)
newMap.delete(key)
return newMap
})
}
const displayQR = () => {
if (!invoices) return null
const authorInvoice = invoices.get('author')
if (authorInvoice) {
return (
<ZapQR
key={authorInvoice.pr}
paymentRequest={authorInvoice}
handleClose={() => removeInvoice('author')}
handleQRExpiry={() => removeInvoice('author')}
setTotalZapAmount={setTotalZapAmount}
setHasZapped={setHasZapped}
/>
)
}
const adminInvoice = invoices.get('admin')
if (adminInvoice) {
return (
<ZapQR
key={adminInvoice.pr}
paymentRequest={adminInvoice}
handleClose={() => {
removeInvoice('admin')
handleClose()
}}
handleQRExpiry={() => removeInvoice('admin')}
/>
)
}
return null
}
const authorName = author?.displayName || author?.name || '[name not set up]'
const adminName = admin?.displayName || admin?.name || '[name not set up]'
return (
<>
{isLoading && <LoadingSpinner desc={loadingSpinnerDesc} />}
<div className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard popUpMainCardQR'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>Tip/Zap</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' />
</svg>
</div>
</div>
<div className='pUMCB_Zaps'>
<div className='pUMCB_ZapsInside'>
<div className='pUMCB_ZapsInsideAmount'>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>
Amount (Satoshis)
</label>
<input
className='inputMain'
type='text'
inputMode='numeric'
value={amount ? formatNumber(amount) : ''}
onChange={handleAmountChange}
/>
</div>
<div className='pUMCB_ZapsInsideAmountOptions'>
<ZapPresets setAmount={setAmount} />
</div>
</div>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>
Message (optional)
</label>
<input
type='text'
className='inputMain'
value={message}
onChange={(e) => setMessage(e.target.value)}
/>
</div>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>Tip Split</label>
<div className='ZapSplitUserBox'>
<div className='ZapSplitUserBoxUser'>
<div
className='ZapSplitUserBoxUserPic'
style={{
background: `url('${
author?.image || FALLBACK_PROFILE_IMAGE
}') center / cover no-repeat`
}}
></div>
<div className='ZapSplitUserBoxUserDetails'>
<p className='ZapSplitUserBoxUserDetailsName'>
{authorName}
</p>
{author?.nip05 && (
<p className='ZapSplitUserBoxUserDetailsHandle'>
{author.nip05}
</p>
)}
</div>
</div>
<div className='ZapSplitUserBoxRange'>
<input
className='form-range inputRangeMain inputRangeMainZap'
type='range'
max='100'
min='0'
value={authorPercentage}
onChange={handleAuthorPercentageChange}
step='1'
required
name='ZapSplitName'
/>
<p className='ZapSplitUserBoxRangeText'>
{authorPercentage}%
</p>
</div>
<p className='ZapSplitUserBoxText'>
This goes to show your appreciation to the mod creator!
</p>
</div>
<div className='ZapSplitUserBox'>
<div className='ZapSplitUserBoxUser'>
<div
className='ZapSplitUserBoxUserPic'
style={{
background: `url('${
admin?.image || FALLBACK_PROFILE_IMAGE
}') center / cover no-repeat`
}}
></div>
<div className='ZapSplitUserBoxUserDetails'>
<p className='ZapSplitUserBoxUserDetailsName'>
{adminName}
</p>
{admin?.nip05 && (
<p className='ZapSplitUserBoxUserDetailsHandle'></p>
)}
</div>
</div>
<div className='ZapSplitUserBoxRange'>
<input
className='form-range inputRangeMain inputRangeMainZap'
type='range'
max='100'
min='0'
value={adminPercentage}
onChange={handleAdminPercentageChange}
step='1'
required
name='ZapSplitName'
/>
<p className='ZapSplitUserBoxRangeText'>
{adminPercentage}%
</p>
</div>
<p className='ZapSplitUserBoxText'>
Help with the development, maintenance, management, and
growth of DEG Mods.
</p>
</div>
</div>
<ZapButtons
disabled={!amount}
handleGenerateQRCode={handleGenerateQRCode}
handleSend={handleSend}
/>
{displayQR()}
</div>
</div>
</div>
</div>
</div>
</div>
</>
)
}

118
src/constants.ts Normal file
View File

@ -0,0 +1,118 @@
export const T_TAG_VALUE = 'GameMod'
export const MOD_FILTER_LIMIT = 20
export const LANDING_PAGE_DATA = {
featuredSlider: [
'naddr1qvzqqqrkcgpzquuz5nxzzap2c034s8cuv5ayr7gjaxz7d22pgwfh0qpmsesy9eflqp4nxvp5xqer5den8qexzdrrvverzde5xfskxvm9xv6nsvtxx93nvdfnvy6rze3exyex2wfcx4jnvcfexscngveexvmnwwpsxd3rsd3kxq6ryef4xdnr5cek8pnrwc34xgknyv33xqkngc34xyknscfjxsknzvp38quxgc33vejnqvqhqecq8',
'naddr1qvzqqqrkcgpzquuz5nxzzap2c034s8cuv5ayr7gjaxz7d22pgwfh0qpmsesy9eflqp4nxvp5xqer5den8qexzdrrvverzde5xfskxvm9xv6nsvtxx93nvdfnvy6rze3exyex2wfcx4jnvcfexscngveexvmnwwpsxd3rsd3kxq6ryef4xdnr5vpcxs6nwwp3x5knyd3evckngetxxcknjdfkx5kngdfhvgukvwfjxsunseqnend73',
'naddr1qvzqqqrkcgpzquuz5nxzzap2c034s8cuv5ayr7gjaxz7d22pgwfh0qpmsesy9eflqp4nxvp5xqer5den8qexzdrrvverzde5xfskxvm9xv6nsvtxx93nvdfnvy6rze3exyex2wfcx4jnvcfexscngveexvmnwwpsxd3rsd3kxq6ryef4xdnr5d3excenzvf5xgkkvdny8qkngveex5knjcnxxqkn2efnx3jrxvpcxgukxdggsmal6',
'naddr1qvzqqqrkcgpzquuz5nxzzap2c034s8cuv5ayr7gjaxz7d22pgwfh0qpmsesy9eflqp4nxvp5xqer5den8qexzdrrvverzde5xfskxvm9xv6nsvtxx93nvdfnvy6rze3exyex2wfcx4jnvcfexscngveexvmnwwpsxd3rsd3kxq6ryef4xdnr5dp4xsex2e3cxuknsdryvvkngc3sxcknjef4vcknvvmyvcukyd3kvd3rxdgnuver5',
'naddr1qvzqqqrkcgpzquuz5nxzzap2c034s8cuv5ayr7gjaxz7d22pgwfh0qpmsesy9eflqp4nxvp5xqer5den8qexzdrrvverzde5xfskxvm9xv6nsvtxx93nvdfnvy6rze3exyex2wfcx4jnvcfexscngveexvmnwwpsxd3rsd3kxq6ryef4xdnr5vf5x9nrxcekxvknjvmzxvkngcfsx5kkzcf3xqknsvmrvgenwe3j8p3nzwgka59vj',
'naddr1qvzqqqrkcgpzph2jv2ejvdk27hn36dt57j6f69f5h0zccve3xceujq5z9jk8ym8wqp4nxvp5xqer5eryx5ervvnzxvervvekvdskvdt9xuckgve4xu6xvdrzxsukgvf4xv6xycnrx5uxxvenxvcnxd3nxd3njvpj8qerycmpvvmnydnrv4jn5wrpv5mrvwpsxgknxwp4xqkngetpxqknjd35xcknsv3cv9jnxvtp8ycxyegq5ndhc'
],
awesomeMods: [
'naddr1qvzqqqrkcgpzquuz5nxzzap2c034s8cuv5ayr7gjaxz7d22pgwfh0qpmsesy9eflqp4nxvp5xqer5den8qexzdrrvverzde5xfskxvm9xv6nsvtxx93nvdfnvy6rze3exyex2wfcx4jnvcfexscngveexvmnwwpsxd3rsd3kxq6ryef4xdnr5d3excenzvf5xgkkvdny8qkngveex5knjcnxxqkn2efnx3jrxvpcxgukxdggsmal6',
'naddr1qvzqqqrkcgpzquuz5nxzzap2c034s8cuv5ayr7gjaxz7d22pgwfh0qpmsesy9eflqp4nxvp5xqer5den8qexzdrrvverzde5xfskxvm9xv6nsvtxx93nvdfnvy6rze3exyex2wfcx4jnvcfexscngveexvmnwwpsxd3rsd3kxq6ryef4xdnr5df5xccngvtrxqkkydpexukngvp4xgknsvp4vskkgdrxvgmkxdmp8quxycgx78rpf',
'naddr1qvzqqqrkcgpzquuz5nxzzap2c034s8cuv5ayr7gjaxz7d22pgwfh0qpmsesy9eflqp4nxvp5xqer5den8qexzdrrvverzde5xfskxvm9xv6nsvtxx93nvdfnvy6rze3exyex2wfcx4jnvcfexscngveexvmnwwpsxd3rsd3kxq6ryef4xdnr5vrrvgmnjc33xuknwde4vskngvekxgknsenyxvkk2ctxvscrvenpvsmnxeqydygjx'
],
featuredGames: [
'Persona 3 Reload',
"Baldur's Gate 3",
'Cyberpunk 2077',
'ELDEN RING',
'The Coffin of Andy and Leyley'
]
}
// we use this object to check if a user has reacted positively or negatively to a post
// reactions are kind 7 events and their content is either emoji icon or emoji shortcode
// Extend the following object as per need to include more emojis and shortcodes
// NOTE: In following object emojis and shortcode array are not interlinked.
// Both of these arrays can have separate items
export const REACTIONS = {
positive: {
emojis: [
'+',
'❤️',
'💙',
'💖',
'💚',
'⭐',
'🚀',
'🫂',
'🎉',
'🥳',
'🎊',
'👍',
'💪',
'😎'
],
shortCodes: [
':red_heart:',
':blue_heart:',
':sparkling_heart:',
':green_heart:',
':star:',
':rocket:',
':people_hugging:',
':party_popper:',
':tada:',
':partying_face:',
':confetti_ball:',
':thumbs_up:',
':+1:',
':thumbsup:',
':thumbup:',
':flexed_biceps:',
':muscle:'
]
},
negative: {
emojis: [
'-',
'💩',
'💔',
'👎',
'😠',
'😞',
'🤬',
'🤢',
'🤮',
'🖕',
'😡',
'💢',
'😠',
'💀'
],
shortCodes: [
':poop:',
':shit:',
':poo:',
':hankey:',
':pile_of_poo:',
':broken_heart:',
':thumbsdown:',
':thumbdown:',
':nauseated_face:',
':sick:',
':face_vomiting:',
':vomiting_face:',
':face_with_open_mouth_vomiting:',
':middle_finger:',
':rage:',
':anger:',
':anger_symbol:',
':angry_face:',
':angry:',
':smiling_face_with_sunglasses:',
':sunglasses:',
':skull:',
':skeleton:'
]
}
}
export const MAX_MODS_PER_PAGE = 10
export const MAX_GAMES_PER_PAGE = 10
// todo: add game and mod fallback image here
export const FALLBACK_PROFILE_IMAGE =
'https://image.nostr.build/a305f4b43f74af3c6dcda42e6a798105a56ac1e3e7b74d7bef171896b3ba7520.png'

509
src/contexts/NDKContext.tsx Normal file
View File

@ -0,0 +1,509 @@
import NDK, {
getRelayListForUser,
NDKEvent,
NDKFilter,
NDKKind,
NDKList,
NDKRelaySet,
NDKSubscriptionCacheUsage,
NDKUser,
zapInvoiceFromEvent
} from '@nostr-dev-kit/ndk'
import NDKCacheAdapterDexie from '@nostr-dev-kit/ndk-cache-dexie'
import { MOD_FILTER_LIMIT, T_TAG_VALUE } from 'constants.ts'
import { Dexie } from 'dexie'
import { createContext, ReactNode, useEffect, useMemo } from 'react'
import { toast } from 'react-toastify'
import { ModDetails, MuteLists, UserProfile, UserRelaysType } from 'types'
import {
constructModListFromEvents,
hexToNpub,
log,
LogType,
npubToHex,
orderEventsChronologically
} from 'utils'
type FetchModsOptions = {
source?: string
until?: number
since?: number
limit?: number
author?: string
}
interface NDKContextType {
ndk: NDK
fetchMods: (opts: FetchModsOptions) => Promise<ModDetails[]>
fetchEvents: (filter: NDKFilter) => Promise<NDKEvent[]>
fetchEvent: (filter: NDKFilter) => Promise<NDKEvent | null>
fetchEventsFromUserRelays: (
filter: NDKFilter | NDKFilter[],
hexKey: string,
userRelaysType: UserRelaysType
) => Promise<NDKEvent[]>
fetchEventFromUserRelays: (
filter: NDKFilter | NDKFilter[],
hexKey: string,
userRelaysType: UserRelaysType
) => Promise<NDKEvent | null>
findMetadata: (pubkey: string) => Promise<UserProfile>
getTotalZapAmount: (
user: string,
eTag: string,
aTag?: string,
currentLoggedInUser?: string
) => Promise<{
accumulatedZapAmount: number
hasZapped: boolean
}>
publish: (event: NDKEvent) => Promise<string[]>
getNSFWList: () => Promise<string[]>
getMuteLists: (pubkey?: string) => Promise<{
admin: MuteLists
user: MuteLists
}>
}
// Create the context with an initial value of `null`
export const NDKContext = createContext<NDKContextType | null>(null)
// Create a provider component to wrap around parts of your app
export const NDKContextProvider = ({ children }: { children: ReactNode }) => {
useEffect(() => {
window.onunhandledrejection = async (event: PromiseRejectionEvent) => {
event.preventDefault()
console.log(event.reason)
if (event.reason?.name === Dexie.errnames.DatabaseClosed) {
console.log(
'Could not open Dexie DB, probably version change. Deleting old DB and reloading...'
)
await Dexie.delete('degmod-db')
// Must reload to open a brand new DB
window.location.reload()
}
}
}, [])
const addAdminRelays = async (ndk: NDK) => {
const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
adminNpubs.forEach((npub) => {
const hexKey = npubToHex(npub)
if (hexKey) {
getRelayListForUser(hexKey, ndk)
.then((ndkRelayList) => {
if (ndkRelayList) {
ndkRelayList.bothRelayUrls.forEach((url) =>
ndk.addExplicitRelay(url)
)
}
})
.catch((err) => {
log(
true,
LogType.Error,
`❌ Error occurred in getting the instance of NDKRelayList for npub: ${npub}`,
err
)
})
}
})
}
const ndk = useMemo(() => {
localStorage.setItem('debug', '*')
const dexieAdapter = new NDKCacheAdapterDexie({ dbName: 'degmod-db' })
dexieAdapter.locking = true
const ndk = new NDK({
enableOutboxModel: true,
autoConnectUserRelays: true,
autoFetchUserMutelist: true,
explicitRelayUrls: [
'wss://user.kindpag.es',
'wss://purplepag.es',
'wss://relay.damus.io/',
import.meta.env.VITE_APP_RELAY
],
cacheAdapter: dexieAdapter
})
addAdminRelays(ndk)
ndk.connect()
return ndk
}, [])
/**
* Fetches a list of mods based on the provided source.
*
* @param source - The source URL to filter the mods. If it matches the current window location,
* it adds a filter condition to the request.
* @param until - Optional timestamp to filter events until this time.
* @param since - Optional timestamp to filter events from this time.
* @returns A promise that resolves to an array of `ModDetails` objects. In case of an error,
* it logs the error and shows a notification, then returns an empty array.
*/
const fetchMods = async ({
source,
until,
since,
limit,
author
}: FetchModsOptions): Promise<ModDetails[]> => {
// Define the filter criteria for fetching mods
const filter: NDKFilter = {
kinds: [NDKKind.Classified], // Specify the kind of events to fetch
limit: limit || MOD_FILTER_LIMIT, // Limit the number of events fetched to 20
'#t': [T_TAG_VALUE],
until, // Optional filter to fetch events until this timestamp
since, // Optional filter to fetch events from this timestamp
authors: author ? [author] : undefined // Optional filter to fetch events from only this author
}
// If the source matches the current window location, add a filter condition
if (source === window.location.host) {
filter['#r'] = [window.location.host] // Add a tag filter for the current host
}
return ndk
.fetchEvents(filter, {
closeOnEose: true,
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
})
.then((ndkEventSet) => {
const ndkEvents = Array.from(ndkEventSet)
orderEventsChronologically(ndkEvents)
// Convert the fetched events into a list of mods
const modList = constructModListFromEvents(ndkEvents)
return modList // Return the list of mods
})
.catch((err) => {
// Log the error and show a notification if fetching fails
log(
true,
LogType.Error,
'An error occurred in fetching mods from relays',
err
)
toast.error('An error occurred in fetching mods from relays') // Show error notification
return [] // Return an empty array in case of an error
})
}
/**
* Asynchronously retrieves multiple event based on a provided filter.
*
* @param filter - The filter criteria to find the event.
* @returns Returns a promise that resolves to the found event or null if not found.
*/
const fetchEvents = async (filter: NDKFilter): Promise<NDKEvent[]> => {
return ndk
.fetchEvents(filter, {
closeOnEose: true,
cacheUsage: NDKSubscriptionCacheUsage.PARALLEL
})
.then((ndkEventSet) => {
const ndkEvents = Array.from(ndkEventSet)
return orderEventsChronologically(ndkEvents)
})
.catch((err) => {
// Log the error and show a notification if fetching fails
log(true, LogType.Error, 'An error occurred in fetching events', err)
toast.error('An error occurred in fetching events') // Show error notification
return [] // Return an empty array in case of an error
})
}
/**
* Asynchronously retrieves an event based on a provided filter.
*
* @param filter - The filter criteria to find the event.
* @returns Returns a promise that resolves to the found event or null if not found.
*/
const fetchEvent = async (filter: NDKFilter) => {
const events = await fetchEvents(filter)
if (events.length === 0) return null
return events[0]
}
/**
* Asynchronously retrieves multiple events from the user's relays based on a specified filter.
* The function first retrieves the user's relays, and then fetches the events using the provided filter.
*
* @param filter - The event filter to use when fetching the event (e.g., kinds, authors).
* @param hexKey - The hexadecimal representation of the user's public key.
* @param userRelaysType - The type of relays to search (e.g., write, read).
* @returns A promise that resolves with an array of events.
*/
const fetchEventsFromUserRelays = async (
filter: NDKFilter | NDKFilter[],
hexKey: string,
userRelaysType: UserRelaysType
): Promise<NDKEvent[]> => {
// Find the user's relays.
const relayUrls = await getRelayListForUser(hexKey, ndk)
.then((ndkRelayList) => {
if (ndkRelayList) return ndkRelayList[userRelaysType]
return [] // Return an empty array if ndkRelayList is undefined
})
.catch((err) => {
log(
true,
LogType.Error,
`An error occurred in fetching user's (${hexKey}) ${userRelaysType}`,
err
)
return [] as string[]
})
return ndk
.fetchEvents(
filter,
{ closeOnEose: true, cacheUsage: NDKSubscriptionCacheUsage.PARALLEL },
NDKRelaySet.fromRelayUrls(relayUrls, ndk, true)
)
.then((ndkEventSet) => {
const ndkEvents = Array.from(ndkEventSet)
return orderEventsChronologically(ndkEvents)
})
.catch((err) => {
// Log the error and show a notification if fetching fails
log(true, LogType.Error, 'An error occurred in fetching events', err)
toast.error('An error occurred in fetching events') // Show error notification
return [] // Return an empty array in case of an error
})
}
/**
* Fetches an event from the user's relays based on a specified filter.
* The function first retrieves the user's relays, and then fetches the event using the provided filter.
*
* @param filter - The event filter to use when fetching the event (e.g., kinds, authors).
* @param hexKey - The hexadecimal representation of the user's public key.
* @param userRelaysType - The type of relays to search (e.g., write, read).
* @returns A promise that resolves to the fetched event or null if the operation fails.
*/
const fetchEventFromUserRelays = async (
filter: NDKFilter | NDKFilter[],
hexKey: string,
userRelaysType: UserRelaysType
) => {
const events = await fetchEventsFromUserRelays(
filter,
hexKey,
userRelaysType
)
if (events.length === 0) return null
return events[0]
}
/**
* Finds metadata for a given pubkey.
*
* @param hexKey - The pubkey to search for metadata.
* @returns A promise that resolves to the metadata event.
*/
const findMetadata = async (pubkey: string): Promise<UserProfile> => {
const npub = hexToNpub(pubkey)
const user = new NDKUser({ npub })
user.ndk = ndk
const userProfile = await user.fetchProfile()
return userProfile
}
const getTotalZapAmount = async (
user: string,
eTag: string,
aTag?: string,
currentLoggedInUser?: string
) => {
const filters: NDKFilter[] = [
{
kinds: [NDKKind.Zap],
'#e': [eTag],
'#p': [user]
}
]
if (aTag) {
filters.push({
kinds: [NDKKind.Zap],
'#a': [aTag],
'#p': [user]
})
}
const zapEvents = await fetchEventsFromUserRelays(
filters,
user,
UserRelaysType.Read
)
let accumulatedZapAmount = 0
let hasZapped = false
zapEvents.forEach((zap) => {
const zapInvoice = zapInvoiceFromEvent(zap)
if (zapInvoice) {
accumulatedZapAmount += Math.round(zapInvoice.amount / 1000)
if (!hasZapped) hasZapped = zapInvoice.zappee === currentLoggedInUser
}
})
return {
accumulatedZapAmount,
hasZapped
}
}
const publish = async (event: NDKEvent): Promise<string[]> => {
if (!event.sig) throw new Error('Before publishing first sign the event!')
return event
.publish(undefined, 30000)
.then((res) => {
const relaysPublishedOn = Array.from(res)
return relaysPublishedOn.map((relay) => relay.url)
})
.catch((err) => {
console.error(`An error occurred in publishing event`, err)
return []
})
}
/**
* Retrieves a list of NSFW (Not Safe For Work) posts that were not specified as NSFW by post author but marked as NSFW by admin.
*
* @returns {Promise<string[]>} - A promise that resolves to an array of NSFW post identifiers (e.g., URLs or IDs).
*/
const getNSFWList = async (): Promise<string[]> => {
// Initialize an array to store the NSFW post identifiers
const nsfwPosts: string[] = []
const reportingNpub = import.meta.env.VITE_REPORTING_NPUB
// Convert the public key (npub) to a hexadecimal format
const hexKey = npubToHex(reportingNpub)
// If the conversion is successful and we have a hexKey
if (hexKey) {
// Fetch the event that contains the NSFW list
const nsfwListEvent = await fetchEvent({
kinds: [NDKKind.ArticleCurationSet],
authors: [hexKey],
'#d': ['nsfw']
})
if (nsfwListEvent) {
// Convert the event data to an NDKList, which is a structured list format
const list = NDKList.from(nsfwListEvent)
// Iterate through the items in the list
list.items.forEach((item) => {
if (item[0] === 'a') {
// Add the identifier of the NSFW post to the nsfwPosts array
nsfwPosts.push(item[1])
}
})
}
}
// Return the array of NSFW post identifiers
return nsfwPosts
}
const getMuteLists = async (
pubkey?: string
): Promise<{
admin: MuteLists
user: MuteLists
}> => {
const adminMutedAuthors = new Set<string>()
const adminMutedPosts = new Set<string>()
const reportingNpub = import.meta.env.VITE_REPORTING_NPUB
const adminHexKey = npubToHex(reportingNpub)
if (adminHexKey) {
const muteListEvent = await fetchEvent({
kinds: [NDKKind.MuteList],
authors: [adminHexKey]
})
if (muteListEvent) {
const list = NDKList.from(muteListEvent)
list.items.forEach((item) => {
if (item[0] === 'p') {
adminMutedAuthors.add(item[1])
} else if (item[0] === 'a') {
adminMutedPosts.add(item[1])
}
})
}
}
const userMutedAuthors = new Set<string>()
const userMutedPosts = new Set<string>()
if (pubkey) {
const userHexKey = npubToHex(pubkey)
if (userHexKey) {
const muteListEvent = await fetchEvent({
kinds: [NDKKind.MuteList],
authors: [userHexKey]
})
if (muteListEvent) {
const list = NDKList.from(muteListEvent)
list.items.forEach((item) => {
if (item[0] === 'p') {
userMutedAuthors.add(item[1])
} else if (item[0] === 'a') {
userMutedPosts.add(item[1])
}
})
}
}
}
return {
admin: {
authors: Array.from(adminMutedAuthors),
replaceableEvents: Array.from(adminMutedPosts)
},
user: {
authors: Array.from(userMutedAuthors),
replaceableEvents: Array.from(userMutedPosts)
}
}
}
return (
<NDKContext.Provider
value={{
ndk,
fetchMods,
fetchEvents,
fetchEvent,
fetchEventsFromUserRelays,
fetchEventFromUserRelays,
findMetadata,
getTotalZapAmount,
publish,
getNSFWList,
getMuteLists
}}
>
{children}
</NDKContext.Provider>
)
}

View File

@ -1 +1 @@
export * from './metadata'
export * from './zap'

View File

@ -1,44 +0,0 @@
import NDK, { NDKUser } from '@nostr-dev-kit/ndk'
import { UserProfile } from '../types/user'
import { hexToNpub } from '../utils'
/**
* Singleton class to manage metadata operations using NDK.
*/
export class MetadataController {
private static instance: MetadataController
private profileNdk: NDK
private constructor() {
this.profileNdk = new NDK({
explicitRelayUrls: ['wss://user.kindpag.es', 'wss://purplepag.es']
})
this.profileNdk.connect()
}
/**
* Provides the singleton instance of MetadataController.
*
* @returns The singleton instance of MetadataController.
*/
public static getInstance(): MetadataController {
if (!MetadataController.instance) {
MetadataController.instance = new MetadataController()
}
return MetadataController.instance
}
/**
* Finds metadata for a given pubkey.
*
* @param hexKey - The pubkey to search for metadata.
* @returns A promise that resolves to the metadata event.
*/
public findMetadata = async (pubkey: string): Promise<UserProfile> => {
const npub = hexToNpub(pubkey)
const user = new NDKUser({ npub })
user.ndk = this.profileNdk
return await user.fetchProfile()
}
}

362
src/controllers/zap.ts Normal file
View File

@ -0,0 +1,362 @@
import { Invoice } from '@getalby/lightning-tools'
import NDK, {
NDKFilter,
NDKKind,
NDKRelaySet,
NDKSubscriptionCacheUsage
} from '@nostr-dev-kit/ndk'
import axios, { AxiosInstance } from 'axios'
import { kinds } from 'nostr-tools'
import { requestProvider, SendPaymentResponse, WebLNProvider } from 'webln'
import {
isLnurlResponse,
LnurlResponse,
PaymentRequest,
SignedEvent,
ZapReceipt,
ZapRequest
} from '../types'
import { log, LogType, npubToHex } from '../utils'
/**
* Singleton class to manage zap related operations.
*/
export class ZapController {
private static instance: ZapController
private webln: WebLNProvider | null = null
private httpClient: AxiosInstance
private appRelay = import.meta.env.VITE_APP_RELAY
private constructor() {
this.httpClient = axios.create()
}
/**
* @returns The singleton instance of ZapController.
*/
public static getInstance(): ZapController {
if (!ZapController.instance) {
ZapController.instance = new ZapController()
}
return ZapController.instance
}
/**
* Generates ZapRequest and payment request string. More info can be found at
* https://github.com/nostr-protocol/nips/blob/master/57.md.
* @param lud16 - LUD-16 of the recipient.
* @param amount - payment amount (will be multiplied by 1000 to represent sats).
* @param recipientPubKey - pubKey of the recipient.
* @param recipientRelays - relays on which zap receipt will be published.
* @param senderPubkey - pubKey of of the sender.
* @param content - optional content (comment).
* @param eventId - event id, if zapping an event.
* @param aTag - value of `a` tag.
* @returns - promise that resolves into object containing zap request and payment
* request string
*/
async getLightningPaymentRequest(
lud16: string,
amount: number,
recipientPubKey: string,
recipientRelays: string[],
senderPubkey: string,
content?: string,
eventId?: string,
aTag?: string
) {
// Check if amount is greater than 0
if (amount <= 0) throw 'Amount should be > 0.'
// convert to mili satoshis
amount *= 1000
// decode lud16 into lnurl
const lnurl = this.decodeLud16(lud16)
// get receiver lightning details from lnurl pay endpoint
const lnurlResponse = await this.getLnurlResponse(lnurl)
const { minSendable, maxSendable, callback } = lnurlResponse
// check if the amount is within minSendable and maxSendable values
if (amount < minSendable || amount > maxSendable) {
throw `Amount '${amount}' is not within minSendable and maxSendable values '${minSendable}-${maxSendable}'.`
}
// generate zap request
const zapRequest = await this.createZapRequest(
amount,
content,
recipientPubKey,
recipientRelays,
senderPubkey,
eventId,
aTag
)
if (!window.nostr?.signEvent) {
log(
true,
LogType.Error,
'Failed to sign the zap request!',
'window.nostr.signEvent is not defined'
)
throw 'Failed to sign zap Request!'
}
// Sign zap request. This is validated by the lightning provider prior to sending the invoice(NIP-57).
const signedEvent = await window.nostr
.signEvent(zapRequest)
.then((event) => event as SignedEvent)
.catch((err) => {
log(true, LogType.Error, 'Failed to sign the zap request!', err)
throw 'Failed to sign the zap request!'
})
// Kind 9734 event must be signed and sent
// in order to receive the invoice from the provider.
// Encode stringified signed zap request.
const encodedEvent = encodeURI(JSON.stringify(signedEvent))
// send zap request as GET request to callback url received from the lnurl pay endpoint
const { data } = await this.httpClient.get(
`${callback}?amount=${amount}&nostr=${encodedEvent}`
)
// data object of the response should contain payment request
if (data && data.pr) {
return Promise.resolve({ ...signedEvent, pr: data.pr })
}
throw 'lnurl callback did not return payment request.'
}
/**
* Polls zap receipt.
* @param paymentRequest - payment request object containing zap request and
* payment request string.
* @param pollingTimeout - polling timeout (secs), by default equals to 6min.
* @returns - promise that resolves into zap receipt.
*/
async pollZapReceipt(
paymentRequest: PaymentRequest,
ndk: NDK,
pollingTimeout?: number
) {
const { pr, ...zapRequest } = paymentRequest
const { created_at } = zapRequest
// stringify zap request
const zapRequestStringified = JSON.stringify(zapRequest)
// eslint-disable-next-line no-async-promise-executor
return new Promise<ZapReceipt>(async (resolve, reject) => {
// clear polling timeout
const cleanup = () => {
clearTimeout(timeout)
subscription.stop()
}
// Polling timeout
const timeout = setTimeout(
() => {
cleanup()
reject('Zap receipt was not received.')
},
pollingTimeout || 6 * 60 * 1000 // 6 minutes
)
const relaysTag = zapRequest.tags.find((t) => t[0] === 'relays')
if (!relaysTag)
throw new Error('Zap request does not contain relays tag.')
const relayUrls = relaysTag.slice(1)
// filter relay for event of kind 9735
const filter: NDKFilter = {
kinds: [NDKKind.Zap],
since: created_at
}
const subscription = ndk.subscribe(
filter,
{
closeOnEose: false,
cacheUsage: NDKSubscriptionCacheUsage.ONLY_RELAY
},
NDKRelaySet.fromRelayUrls(relayUrls, ndk, true)
)
subscription.on('event', async (ndkEvent) => {
// compare description tag of the event with stringified zap request
if (ndkEvent.tagValue('description') === zapRequestStringified) {
// validate zap receipt
if (
await this.validateZapReceipt(pr, ndkEvent.rawEvent() as ZapReceipt)
) {
cleanup()
resolve(ndkEvent.rawEvent() as ZapReceipt)
}
}
})
subscription.start()
})
}
async isWeblnProviderExists(): Promise<boolean> {
await this.requestWeblnProvider()
return !!this.webln
}
async sendPayment(invoice: string): Promise<SendPaymentResponse> {
if (this.webln) {
return await this.webln!.sendPayment(invoice).catch((err) => {
throw new Error(`Error while sending payment. Error: ${err.message}`)
})
}
throw 'Webln is not defined!'
}
/**
* Decodes LUD-16 into lnurl.
* @param lud16 - LUD-16 that looks like <username>@<domainname>.
* @returns - lnurl that looks like 'http://<domain>/.well-known/lnurlp/<username>'.
*/
private decodeLud16(lud16: string) {
const username = lud16.split('@')[0]
const domain = lud16.split('@')[1]
if (!domain || !username) throw `Provided lud16 '${lud16}' is not valid.`
return `https://${domain}/.well-known/lnurlp/${username}`
}
/**
* Fetches and validates response from lnurl pay endpoint.
*
* @param lnurl - lnurl pay endpoint.
* @returns response object that conforms to LnurlResponse interface.
*/
private async getLnurlResponse(lnurl: string): Promise<LnurlResponse> {
// get request from lnurl pay endpoint
const { data: lnurlResponse } = await this.httpClient.get(lnurl)
// validate lnurl response
this.validateLnurlResponse(lnurlResponse)
// return callback URL
return Promise.resolve(lnurlResponse)
}
/**
* Checks if response conforms to LnurlResponse interface and if 'allowsNostr'
* and 'nostrPubkey' supported.
*
* @param response - response received from lnurl pay endpoint.
*/
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private validateLnurlResponse(response: any) {
if (!isLnurlResponse(response)) {
throw 'Provided response is not LnurlResponse.'
}
if (!response.allowsNostr) throw `'allowsNostr' is not supported.`
if (!response.nostrPubkey) throw `'nostrPubkey' is not supported.`
}
/**
* Constructs zap request object.
* @param amount - request amount (sats).
* @param content - comment.
* @param recipientPubKey - pubKey of the recipient.
* @param recipientRelays - relays on which zap receipt will be published.
* @param senderPubkey - pubKey of of the sender.
* @param eventId - event id, if zapping an event.
* @param aTag - value of `a` tag.
* @returns zap request
*/
private async createZapRequest(
amount: number,
content = '',
recipientPubKey: string,
recipientRelays: string[],
senderPubkey: string,
eventId?: string,
aTag?: string
): Promise<ZapRequest> {
const recipientHexKey = npubToHex(recipientPubKey)
if (!recipientHexKey) throw 'Invalid recipient pubKey.'
if (!recipientRelays.includes(this.appRelay)) {
recipientRelays.push(this.appRelay)
}
const zapRequest: ZapRequest = {
kind: kinds.ZapRequest,
content,
tags: [
['relays', ...recipientRelays],
['amount', `${amount}`],
['p', recipientHexKey]
],
pubkey: senderPubkey,
created_at: Math.round(Date.now() / 1000)
}
// add event id to the tags, if zapping an event.
if (eventId) zapRequest.tags.push(['e', eventId])
if (aTag) zapRequest.tags.push(['a', aTag])
return zapRequest
}
/**
* Validates zap receipt preimage and payment request string
* @param paymentRequest - payment request string
* @param event - zap receipt.
* @returns - boolean indicating if preimage in zap receipt is valid
*/
private async validateZapReceipt(paymentRequest: string, event: ZapReceipt) {
const invoice = new Invoice({ pr: paymentRequest })
return invoice.validatePreimage(this.getPreimageFromZapReceipt(event))
}
/**
* Gets preimage from zap receipt.
* @param event - zap receipt (9735 kind).
* @returns - preimage string.
*/
private getPreimageFromZapReceipt(event: ZapReceipt) {
// filter tags by 1st item
const preimageTag = event.tags.filter((tag) => tag[0] === 'preimage')[0]
// throw an error if 'preimage' tag is not present
if (!preimageTag || preimageTag.length != 2) {
throw `'preimage' tag is not present.`
}
const preimage = preimageTag[1]
// throw an error if 'preimage' value is not present
if (!preimage) throw `'preimage' tag is not valid.`
return preimage
}
private async requestWeblnProvider() {
if (!this.webln)
this.webln = await requestProvider().catch((err) => {
console.log('err in requesting WebLNProvider :>> ', err.message)
return null
})
}
}

View File

@ -1 +1,10 @@
export * from './redux'
export * from './useDidMount'
export * from './useFilteredMods'
export * from './useGames'
export * from './useMuteLists'
export * from './useNSFWList'
export * from './useReactions'
export * from './useNDKContext'
export * from './useScrollDisable'
export * from './useLocalStorage'

101
src/hooks/useComments.ts Normal file
View File

@ -0,0 +1,101 @@
import {
getRelayListForUser,
NDKFilter,
NDKKind,
NDKRelaySet,
NDKSubscription,
NDKSubscriptionCacheUsage
} from '@nostr-dev-kit/ndk'
import { useEffect, useState } from 'react'
import { CommentEvent, ModDetails, UserRelaysType } from 'types'
import { log, LogType, timeout } from 'utils'
import { useNDKContext } from './useNDKContext'
export const useComments = (mod: ModDetails) => {
const { ndk } = useNDKContext()
const [commentEvents, setCommentEvents] = useState<CommentEvent[]>([])
useEffect(() => {
let subscription: NDKSubscription // Define the subscription variable here for cleanup
const setupSubscription = async () => {
// Find the mod author's relays.
const authorReadRelays = await Promise.race([
getRelayListForUser(mod.author, ndk),
timeout(10 * 1000) // add a 10 sec timeout
])
.then((ndkRelayList) => {
if (ndkRelayList) return ndkRelayList[UserRelaysType.Read]
return [] // Return an empty array if ndkRelayList is undefined
})
.catch((err) => {
log(
true,
LogType.Error,
`An error occurred in fetching user's (${mod.author}) ${UserRelaysType.Read}`,
err
)
return [] as string[]
})
const filter: NDKFilter = {
kinds: [NDKKind.Text],
'#a': [mod.aTag]
}
const relayUrls = new Set<string>()
ndk.pool.urls().forEach((relayUrl) => {
relayUrls.add(relayUrl)
})
authorReadRelays.forEach((relayUrl) => relayUrls.add(relayUrl))
subscription = ndk.subscribe(
filter,
{
closeOnEose: false,
cacheUsage: NDKSubscriptionCacheUsage.CACHE_FIRST
},
NDKRelaySet.fromRelayUrls(Array.from(relayUrls), ndk)
)
subscription.on('event', (ndkEvent) => {
setCommentEvents((prev) => {
if (prev.find((e) => e.id === ndkEvent.id)) {
return [...prev]
}
const commentEvent: CommentEvent = {
kind: NDKKind.Text,
tags: ndkEvent.tags,
content: ndkEvent.content,
created_at: ndkEvent.created_at!,
pubkey: ndkEvent.pubkey,
id: ndkEvent.id,
sig: ndkEvent.sig!
}
return [commentEvent, ...prev]
})
})
subscription.start()
}
setupSubscription()
// Cleanup function to stop the subscription on unmount
return () => {
if (subscription) {
subscription.stop()
}
}
}, [mod.aTag, mod.author, ndk])
return {
commentEvents,
setCommentEvents
}
}

12
src/hooks/useDidMount.ts Normal file
View File

@ -0,0 +1,12 @@
import { useRef, useEffect } from 'react'
export const useDidMount = (callback: () => void) => {
const didMount = useRef<boolean>(false)
useEffect(() => {
if (callback && !didMount.current) {
didMount.current = true
callback()
}
})
}

View File

@ -0,0 +1,93 @@
import { useMemo } from 'react'
import { IUserState } from 'store/reducers/user'
import {
FilterOptions,
ModDetails,
ModeratedFilter,
MuteLists,
NSFWFilter,
SortBy
} from 'types'
import { npubToHex } from 'utils'
export const useFilteredMods = (
mods: ModDetails[],
userState: IUserState,
filterOptions: FilterOptions,
nsfwList: string[],
muteLists: {
admin: MuteLists
user: MuteLists
},
author?: string | undefined
) => {
return useMemo(() => {
const nsfwFilter = (mods: ModDetails[]) => {
// Add nsfw tag to mods included in nsfwList
if (filterOptions.nsfw !== NSFWFilter.Hide_NSFW) {
mods = mods.map((mod) => {
return !mod.nsfw && nsfwList.includes(mod.aTag)
? { ...mod, nsfw: true }
: mod
})
}
// Determine the filtering logic based on the NSFW filter option
switch (filterOptions.nsfw) {
case NSFWFilter.Hide_NSFW:
// If 'Hide_NSFW' is selected, filter out NSFW mods
return mods.filter((mod) => !mod.nsfw && !nsfwList.includes(mod.aTag))
case NSFWFilter.Show_NSFW:
// If 'Show_NSFW' is selected, return all mods (no filtering)
return mods
case NSFWFilter.Only_NSFW:
// If 'Only_NSFW' is selected, filter to show only NSFW mods
return mods.filter((mod) => mod.nsfw || nsfwList.includes(mod.aTag))
}
}
let filtered = nsfwFilter(mods)
const isAdmin = userState.user?.npub === import.meta.env.VITE_REPORTING_NPUB
const isOwner =
userState.user?.npub &&
npubToHex(userState.user.npub as string) === author
const isUnmoderatedFully =
filterOptions.moderated === ModeratedFilter.Unmoderated_Fully
// Only apply filtering if the user is not an admin or the admin has not selected "Unmoderated Fully"
// Allow "Unmoderated Fully" when author visits own profile
if (!((isAdmin || isOwner) && isUnmoderatedFully)) {
filtered = filtered.filter(
(mod) =>
!muteLists.admin.authors.includes(mod.author) &&
!muteLists.admin.replaceableEvents.includes(mod.aTag)
)
}
if (filterOptions.moderated === ModeratedFilter.Moderated) {
filtered = filtered.filter(
(mod) =>
!muteLists.user.authors.includes(mod.author) &&
!muteLists.user.replaceableEvents.includes(mod.aTag)
)
}
if (filterOptions.sort === SortBy.Latest) {
filtered.sort((a, b) => b.published_at - a.published_at)
} else if (filterOptions.sort === SortBy.Oldest) {
filtered.sort((a, b) => a.published_at - b.published_at)
}
return filtered
}, [
userState.user?.npub,
filterOptions.sort,
filterOptions.moderated,
filterOptions.nsfw,
author,
mods,
muteLists,
nsfwList
])
}

82
src/hooks/useGames.ts Normal file
View File

@ -0,0 +1,82 @@
import Papa from 'papaparse'
import { useEffect, useRef, useState } from 'react'
import { toast } from 'react-toastify'
import { Game } from 'types'
import { log, LogType } from 'utils'
import gameFiles from '../utils/games'
export const useGames = () => {
const hasProcessedFiles = useRef(false)
const [games, setGames] = useState<Game[]>([])
useEffect(() => {
if (hasProcessedFiles.current) return
hasProcessedFiles.current = true
const readGamesCSVs = async () => {
const uniqueGames: Game[] = []
const gameNames = new Set<string>()
// Function to promisify PapaParse
const parseCSV = (csvText: string) =>
new Promise<Game[]>((resolve, reject) => {
Papa.parse<Game>(csvText, {
worker: true,
header: true,
complete: (results) => {
if (results.errors.length) {
reject(results.errors)
}
resolve(results.data)
}
})
})
try {
// Fetch and parse each file
const promises = Object.values(gameFiles).map(async (importFn) => {
const csvText = await importFn()
if (typeof csvText === 'string') {
const parsedGames = await parseCSV(csvText as string)
// Remove duplicate games based on 'Game Name'
parsedGames.forEach((game) => {
if (!gameNames.has(game['Game Name'])) {
gameNames.add(game['Game Name'])
uniqueGames.push(game)
}
})
}
})
await Promise.all(promises)
setGames(uniqueGames)
} catch (err) {
log(
true,
LogType.Error,
'An error occurred in reading and parsing games CSVs',
err
)
// Handle the unknown error type
if (err instanceof Error) {
toast.error(err.message)
} else if (Array.isArray(err) && err.length > 0 && err[0]?.message) {
// Handle the case when it's an array of PapaParse errors
toast.error(err[0].message)
} else {
toast.error(
'An unknown error occurred in reading and parsing csv files'
)
}
}
}
readGamesCSVs()
}, [])
return games
}

View File

@ -0,0 +1,50 @@
import React from 'react'
import {
getLocalStorageItem,
removeLocalStorageItem,
setLocalStorageItem
} from 'utils'
const useLocalStorageSubscribe = (callback: () => void) => {
window.addEventListener('storage', callback)
return () => window.removeEventListener('storage', callback)
}
export function useLocalStorage<T>(
key: string,
initialValue: T
): [T, React.Dispatch<React.SetStateAction<T>>] {
const getSnapshot = () => getLocalStorageItem(key, initialValue)
const data = React.useSyncExternalStore(useLocalStorageSubscribe, getSnapshot)
const setState: React.Dispatch<React.SetStateAction<T>> = React.useCallback(
(v: React.SetStateAction<T>) => {
try {
const nextState =
typeof v === 'function'
? (v as (prevState: T) => T)(JSON.parse(data))
: v
if (nextState === undefined || nextState === null) {
removeLocalStorageItem(key)
} else {
setLocalStorageItem(key, JSON.stringify(nextState))
}
} catch (e) {
console.warn(e)
}
},
[key, data]
)
React.useEffect(() => {
// Set local storage only when it's empty
const data = window.localStorage.getItem(key)
if (data === null) {
setLocalStorageItem(key, JSON.stringify(initialValue))
}
}, [key, initialValue])
return [JSON.parse(data) as T, setState]
}

32
src/hooks/useMuteLists.ts Normal file
View File

@ -0,0 +1,32 @@
import { useEffect, useState } from 'react'
import { MuteLists } from 'types'
import { useAppSelector } from './redux'
import { useNDKContext } from './useNDKContext'
export const useMuteLists = () => {
const { getMuteLists } = useNDKContext()
const [muteLists, setMuteLists] = useState<{
admin: MuteLists
user: MuteLists
}>({
admin: {
authors: [],
replaceableEvents: []
},
user: {
authors: [],
replaceableEvents: []
}
})
const userState = useAppSelector((state) => state.user)
useEffect(() => {
const pubkey = userState.user?.pubkey as string | undefined
getMuteLists(pubkey).then((lists) => {
setMuteLists(lists)
})
}, [userState, getMuteLists])
return muteLists
}

View File

@ -0,0 +1,13 @@
import { NDKContext } from 'contexts/NDKContext'
import { useContext } from 'react'
export const useNDKContext = () => {
const ndkContext = useContext(NDKContext)
if (!ndkContext)
throw new Error(
'NDKContext should not be used in out component tree hierarchy'
)
return { ...ndkContext }
}

16
src/hooks/useNSFWList.ts Normal file
View File

@ -0,0 +1,16 @@
import { useState } from 'react'
import { useDidMount } from './useDidMount'
import { useNDKContext } from './useNDKContext'
export const useNSFWList = () => {
const { getNSFWList } = useNDKContext()
const [nsfwList, setNSFWList] = useState<string[]>([])
useDidMount(async () => {
getNSFWList().then((list) => {
setNSFWList(list)
})
})
return nsfwList
}

18
src/hooks/useProfile.tsx Normal file
View File

@ -0,0 +1,18 @@
import { useNDKContext } from 'hooks'
import { useState, useEffect } from 'react'
import { UserProfile } from 'types'
export const useProfile = (pubkey?: string) => {
const { findMetadata } = useNDKContext()
const [profile, setProfile] = useState<UserProfile>()
useEffect(() => {
if (pubkey) {
findMetadata(pubkey).then((res) => {
setProfile(res)
})
}
}, [findMetadata, pubkey])
return profile
}

173
src/hooks/useReactions.ts Normal file
View File

@ -0,0 +1,173 @@
import { NDKEvent, NDKFilter } from '@nostr-dev-kit/ndk'
import { REACTIONS } from 'constants.ts'
import { useAppSelector, useDidMount, useNDKContext } from 'hooks'
import { Event, kinds, UnsignedEvent } from 'nostr-tools'
import { useMemo, useState } from 'react'
import { toast } from 'react-toastify'
import { UserRelaysType } from 'types'
import { abbreviateNumber, log, LogType, now } from 'utils'
type UseReactionsParams = {
pubkey: string
eTag: string
aTag?: string
}
export const useReactions = (params: UseReactionsParams) => {
const { ndk, fetchEventsFromUserRelays, publish } = useNDKContext()
const [isReactionInProgress, setIsReactionInProgress] = useState(false)
const [isDataLoaded, setIsDataLoaded] = useState(false)
const [reactionEvents, setReactionEvents] = useState<NDKEvent[]>([])
const userState = useAppSelector((state) => state.user)
useDidMount(() => {
const filter: NDKFilter = {
kinds: [kinds.Reaction]
}
if (params.aTag) {
filter['#a'] = [params.aTag]
} else {
filter['#e'] = [params.eTag]
}
fetchEventsFromUserRelays(filter, params.pubkey, UserRelaysType.Read)
.then((events) => {
setReactionEvents(events)
})
.finally(() => {
setIsDataLoaded(true)
})
})
const hasReactedPositively = useMemo(() => {
return (
!!userState.auth &&
reactionEvents.some(
(event) =>
event.pubkey === userState.user?.pubkey &&
(REACTIONS.positive.emojis.includes(event.content) ||
REACTIONS.positive.shortCodes.includes(event.content))
)
)
}, [reactionEvents, userState])
const hasReactedNegatively = useMemo(() => {
return (
!!userState.auth &&
reactionEvents.some(
(event) =>
event.pubkey === userState.user?.pubkey &&
(REACTIONS.negative.emojis.includes(event.content) ||
REACTIONS.negative.shortCodes.includes(event.content))
)
)
}, [reactionEvents, userState])
const getPubkey = async () => {
let hexPubkey: string
if (userState.auth && userState.user?.pubkey) {
hexPubkey = userState.user.pubkey as string
} else {
hexPubkey = (await window.nostr?.getPublicKey()) as string
}
if (!hexPubkey) {
toast.error('Could not get pubkey')
return null
}
return hexPubkey
}
const handleReaction = async (isPositive?: boolean) => {
if (!isDataLoaded || hasReactedPositively || hasReactedNegatively) return
if (isReactionInProgress) return
setIsReactionInProgress(true)
try {
const pubkey = await getPubkey()
if (!pubkey) return
const unsignedEvent: UnsignedEvent = {
kind: kinds.Reaction,
created_at: now(),
content: isPositive ? '+' : '-',
pubkey,
tags: [
['e', params.eTag],
['p', params.pubkey]
]
}
if (params.aTag) {
unsignedEvent.tags.push(['a', params.aTag])
}
const signedEvent = await window.nostr
?.signEvent(unsignedEvent)
.then((event) => event as Event)
.catch((err) => {
toast.error('Failed to sign the reaction event!')
log(true, LogType.Error, 'Failed to sign the event!', err)
return null
})
if (!signedEvent) return
const ndkEvent = new NDKEvent(ndk, signedEvent)
setReactionEvents((prev) => [...prev, ndkEvent])
const publishedOnRelays = await publish(ndkEvent)
if (publishedOnRelays.length === 0) {
log(
true,
LogType.Error,
'Failed to publish reaction event on any relay'
)
return
}
} finally {
setIsReactionInProgress(false)
}
}
const { likesCount, disLikesCount } = useMemo(() => {
let positiveCount = 0
let negativeCount = 0
reactionEvents.forEach((event) => {
if (
REACTIONS.positive.emojis.includes(event.content) ||
REACTIONS.positive.shortCodes.includes(event.content)
) {
positiveCount++
} else if (
REACTIONS.negative.emojis.includes(event.content) ||
REACTIONS.negative.shortCodes.includes(event.content)
) {
negativeCount++
}
})
return {
likesCount: abbreviateNumber(positiveCount),
disLikesCount: abbreviateNumber(negativeCount)
}
}, [reactionEvents])
return {
isDataLoaded,
likesCount,
disLikesCount,
hasReactedPositively,
hasReactedNegatively,
handleReaction
}
}

View File

@ -0,0 +1,11 @@
import { useEffect } from 'react'
export const useBodyScrollDisable = (disable: boolean) => {
useEffect(() => {
if (disable) document.body.style.overflow = 'hidden'
return () => {
document.body.style.overflow = ''
}
}, [disable])
}

View File

@ -43,10 +43,12 @@ a:hover {
color: #535bf2;
}
/*
h1 {
font-size: 3.2em;
line-height: 1.1;
}
*/
button {
border-radius: 8px;

10
src/layout/feed.tsx Normal file
View File

@ -0,0 +1,10 @@
import { Outlet } from 'react-router-dom'
export const FeedLayout = () => {
return (
<div className='InnerBodyMain'>
<h1>WIP</h1>
<Outlet />
</div>
)
}

View File

@ -25,14 +25,6 @@ export const Footer = () => {
<a className={styles.secMainFooterParaLink} href='backers.html'>
Supporters
</a>
&nbsp;and Nostr developer&nbsp;
<a
className={styles.secMainFooterParaLink}
href='https://nostrdev.com'
target='_blank'
>
NostrDev
</a>
. Check our&nbsp;
<a className={styles.secMainFooterParaLink} href='backup.html'>
Backup Plan

36
src/layout/head.tsx Normal file
View File

@ -0,0 +1,36 @@
import { Helmet } from 'react-helmet'
import thumb from '../assets/img/DEGM Thumb.png'
import logoWithCircle from '../assets/img/Logo with circle.png'
export const Head = () => {
return (
<Helmet>
{/* Open Graph Meta Tags */}
<meta property='og:title' content='DEG Mods - Liberating Game Mods' />
<meta
property='og: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={thumb} />
<meta property='og:url' content='https://degmods.com' />
<meta property='og:type' content='website' />
{/* Twitter Card Meta Tags */}
<meta name='twitter:card' content='summary_large_image' />
<meta name='twitter:title' content='DEG Mods - Liberating Game Mods' />
<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 name='twitter:image' content={thumb} />
{/* Other Meta Tags */}
<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.'
/>
<link rel='icon' type='image/png' sizes='935x934' href={logoWithCircle} />
</Helmet>
)
}

View File

@ -1,45 +1,84 @@
import navStyles from '../styles/nav.module.scss'
import mainStyles from '../styles//main.module.scss'
import { Banner } from '../components/Banner'
import { Link } from 'react-router-dom'
import { appRoutes } from '../routes'
import {
init as initNostrLogin,
launch as launchNostrLoginDialog
} from 'nostr-login'
import { useEffect } from 'react'
import { useAppDispatch, useAppSelector } from '../hooks'
import { setIsAuth, setUser } from '../store/reducers/user'
import { MetadataController } from '../controllers'
import React, { useEffect, useState } from 'react'
import { Link } from 'react-router-dom'
import { Banner } from '../components/Banner'
import { ZapPopUp } from '../components/Zap'
import {
useAppDispatch,
useAppSelector,
useBodyScrollDisable,
useDidMount,
useNDKContext
} from '../hooks'
import { appRoutes } from '../routes'
import { setAuth, setUser } from '../store/reducers/user'
import mainStyles from '../styles//main.module.scss'
import navStyles from '../styles/nav.module.scss'
import '../styles/popup.css'
import { npubToHex } from '../utils'
import logo from '../assets/img/DEG Mods Logo With Text.svg'
import placeholder from '../assets/img/DEG Mods Default PP.png'
export const Header = () => {
const dispatch = useAppDispatch()
const { findMetadata } = useNDKContext()
const userState = useAppSelector((state) => state.user)
// Track nostr-login extension modal open state
const [isOpen, setIsOpen] = useState(false)
const handleOpen = () => setIsOpen(true)
const handleClose = () => setIsOpen(false)
useEffect(() => {
window.addEventListener('nlCloseModal', handleClose)
return () => {
window.removeEventListener('nlCloseModal', handleClose)
}
}, [])
useBodyScrollDisable(isOpen)
useEffect(() => {
initNostrLogin({
darkMode: true,
localSignup: true,
noBanner: true,
methods: ['extension'],
onAuth: (npub, opts) => {
if (opts.type === 'logout') {
dispatch(setIsAuth(false))
dispatch(setAuth(null))
dispatch(setUser(null))
} else {
dispatch(setIsAuth(true))
dispatch(setUser({ npub }))
const metadataController = MetadataController.getInstance()
metadataController.findMetadata(npub).then((userProfile) => {
dispatch(
setAuth({
method: opts.method,
localNsec: opts.localNsec
})
)
dispatch(
setUser({
npub,
pubkey: npubToHex(npub)!
})
)
findMetadata(npub).then((userProfile) => {
if (userProfile) {
dispatch(setUser(userProfile))
dispatch(
setUser({
npub,
pubkey: npubToHex(npub)!,
...userProfile
})
)
}
})
}
}
})
}, [dispatch])
}, [dispatch, findMetadata])
const handleLogin = () => {
handleOpen()
launchNostrLoginDialog()
}
@ -57,27 +96,14 @@ export const Header = () => {
<div className={navStyles.NMTI_Sec_HomeLink_Logo}>
<img
className={navStyles.NMTI_Sec_HomeLink_LogoImg}
src='assets/img/DEG%20Mods%20Logo%20With%20Text.svg'
src={logo}
/>
</div>
</Link>
</div>
<div className={navStyles.NMTI_Sec}>
<div className={navStyles.NMTI_SecInside}>
<a
className={`${navStyles.NMTI_SecInside_Link} ${navStyles.NMTI_SI_LinkTip}`}
>
<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>
Tip
</a>
<TipButtonWithDialog />
<Link
to={appRoutes.submitMod}
className={navStyles.NMTI_SecInside_Link}
@ -123,20 +149,23 @@ export const Header = () => {
</svg>
Settings
</Link>
{!userState.isAuth && (
<a
id='loginNav'
className={navStyles.NMTI_SecInside_Link}
onClick={handleLogin}
>
<img
className={navStyles.NMTI_SecInside_LinkImg}
src='assets/img/DEG%20Mods%20Default%20PP.png'
/>
Login
</a>
{!userState.auth && (
<>
<RegisterButtonWithDialog />
<a
id='loginNav'
className={navStyles.NMTI_SecInside_Link}
onClick={handleLogin}
>
<img
className={navStyles.NMTI_SecInside_LinkImg}
src={placeholder}
/>
Login
</a>
</>
)}
{userState.isAuth && userState.user && (
{userState.auth && userState.user && (
<div className={navStyles.NMTI_SecInside_Link}>
{userState.user.image && (
<img
@ -160,33 +189,286 @@ export const Header = () => {
<div className={navStyles.NavMainBottom}>
<div className={mainStyles.ContainerMain}>
<div className={navStyles.NavMainBottomInside}>
<Link
to={appRoutes.games}
className={navStyles.NavMainBottomInsideLink}
<div
className={`${navStyles.NavMainBottomInsideOther} ${navStyles.NavMainBottomInsideOtherLeft}`}
></div>
<div className={navStyles.NavMainBottomInsideLinks}>
<Link
to={appRoutes.games}
className={navStyles.NavMainBottomInsideLink}
>
Games
</Link>
<Link
to={appRoutes.mods}
className={navStyles.NavMainBottomInsideLink}
>
Mods
</Link>
<Link
to={appRoutes.about}
className={navStyles.NavMainBottomInsideLink}
>
About
</Link>
<Link
to={appRoutes.blog}
className={navStyles.NavMainBottomInsideLink}
>
Blog
</Link>
</div>
<div
className={`${navStyles.NavMainBottomInsideOther} ${navStyles.NavMainBottomInsideOtherRight}`}
>
Games
</Link>
<Link
to={appRoutes.mods}
className={navStyles.NavMainBottomInsideLink}
>
Mods
</Link>
<Link
to={appRoutes.about}
className={navStyles.NavMainBottomInsideLink}
>
About
</Link>
<Link
to={appRoutes.blog}
className={navStyles.NavMainBottomInsideLink}
>
Blog
</Link>
<a
className={navStyles.NavMainBottomInsideOtherLink}
href='https://primal.net/p/npub17jl3ldd6305rnacvwvchx03snauqsg4nz8mruq0emj9thdpglr2sst825x'
target='_blank'
>
<img
src='https://image.nostr.build/fb557f1b6d58c7bbcdf4d1edb1b48090c76ff1d1384b9d1aae13d652e7a3cfe4.gif'
width='15px'
/>
</a>
<a
className={navStyles.NavMainBottomInsideOtherLink}
href='https://x.com/DEGMods'
target='_blank'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z'></path>
</svg>
</a>
<a
className={navStyles.NavMainBottomInsideOtherLink}
href='https://www.youtube.com/@DEGModsDotCom'
target='_blank'
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='0 0 512 512'
width='1em'
height='1em'
fill='currentColor'
>
<path d='M549.655 124.083c-6.281-23.65-24.787-42.276-48.284-48.597C458.781 64 288 64 288 64S117.22 64 74.629 75.486c-23.497 6.322-42.003 24.947-48.284 48.597-11.412 42.867-11.412 132.305-11.412 132.305s0 89.438 11.412 132.305c6.281 23.65 24.787 41.5 48.284 47.821C117.22 448 288 448 288 448s170.78 0 213.371-11.486c23.497-6.321 42.003-24.171 48.284-47.821 11.412-42.867 11.412-132.305 11.412-132.305s0-89.438-11.412-132.305zm-317.51 213.508V175.185l142.739 81.205-142.739 81.201z'></path>
</svg>
</a>
</div>
</div>
</div>
</div>
</div>
)
}
const TipButtonWithDialog = React.memo(() => {
const [adminNpub, setAdminNpub] = useState<string | null>(null)
const [isOpen, setIsOpen] = useState(false)
useBodyScrollDisable(isOpen)
useDidMount(async () => {
const adminNpubs = import.meta.env.VITE_ADMIN_NPUBS.split(',')
setAdminNpub(adminNpubs[0])
})
return (
<>
<a
className={`${navStyles.NMTI_SecInside_Link} ${navStyles.NMTI_SI_LinkTip}`}
onClick={() => setIsOpen(true)}
>
<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>
Tip
</a>
{isOpen && adminNpub && (
<ZapPopUp
title='Tip/Zap DEG Mods'
receiver={adminNpub}
handleClose={() => setIsOpen(false)}
labelDescriptionMain={
<p className='labelDescriptionMain' style={{ textAlign: 'center' }}>
If you don't want the development and maintenance of DEG Mods to
stop, then a tip helps!
</p>
}
lastNode={
<div className='BTCAddressPopZap'>
<p>
DEG Mod's Silent Payment Bitcoin Address (Be careful.{' '}
<a
href='https://youtu.be/payDPlHzp58?t=215'
className='linkMain'
target='_blank'
>
Learn more
</a>
):
<br />
<span className='BTCAddressPopZapTextSpan'>
sp1qq205tj23sq3z6qjxt5ts5ps8gdwcrkwypej3h2z2hdclmaptl25xxqjfqhc2de4gaxprgm0yqwfr737swpvvmrph9ctkeyk60knz6xpjhqumafrd
</span>
</p>
</div>
}
/>
)}
</>
)
})
const RegisterButtonWithDialog = () => {
const [showPopUp, setShowPopUp] = useState(false)
useBodyScrollDisable(showPopUp)
return (
<>
<a
id='registerNav'
className={navStyles.NMTI_SecInside_Link}
onClick={() => setShowPopUp(true)}
>
Register
</a>
{showPopUp && (
<div id='PopUpMainRegister' className='popUpMain'>
<div className='ContainerMain'>
<div className='popUpMainCardWrapper'>
<div className='popUpMainCard popUpMainCardQR'>
<div className='popUpMainCardTop'>
<div className='popUpMainCardTopInfo'>
<h3>Create an account via</h3>
</div>
<div
className='popUpMainCardTopClose'
onClick={() => setShowPopUp(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='pUMCB_Zaps'>
<div className='pUMCB_ZapsInside'>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>
Browser Extensions (Windows)
</label>
<p className='labelDescriptionMain'>
Once you create your "account" on any of these (
<a
href='https://video.nostr.build/765aa9bf16dd58bca701efee2572f7e77f29b2787cddd2bee8bbbdea35798153.mp4'
target='blank_'
>
Here's a quick video guide
</a>
), come back and click login, then sign-in with
extension.
</p>
</div>
<a
className='btn btnMain btnMainPopup'
role='button'
href='https://chromewebstore.google.com/detail/nostr-connect/ampjiinddmggbhpebhaegmjkbbeofoaj'
target='_blank'
>
Nostr Connect
</a>
<a
className='btn btnMain btnMainPopup'
role='button'
href='https://addons.mozilla.org/en-US/firefox/addon/nostr-connect/'
target='_blank'
>
Nostr Connect (Firefox)
</a>
<a
className='btn btnMain btnMainPopup'
role='button'
href='https://keys.band/'
target='_blank'
>
Keys.Band
</a>
<a
className='btn btnMain btnMainPopup'
role='button'
href='https://chromewebstore.google.com/detail/nos2x/kpgefcfmnafjgpblomihpgmejjdanjjp'
target='_blank'
>
nos2x
</a>
</div>
<p
className='labelDescriptionMain'
style={{
padding: '10px',
borderRadius: '10px',
background: 'rgba(0,0,0,0.1)',
border: 'solid 1px rgba(255,255,255,0.1)',
margin: '10px 0 0 0'
}}
>
Q:&nbsp;Why can't I create an account normally?
<br />
A: DEG Mods can't ban you or delete your content (we can
only hide you), and the consequence of that is this kind of
registration/login system.
</p>
<div className='dividerPopup'>
<div className='dividerPopupLine'></div>
<p>or</p>
<div className='dividerPopupLine'></div>
</div>
<div className='pUMCB_ZapsInside'>
<div className='inputLabelWrapperMain'>
<label className='form-label labelMain'>
Browser Extensions (iOS)
</label>
<p className='labelDescriptionMain'>
Once you create your "account" on any of these, come
back and click login, then sign-in with extension.
</p>
</div>
<a
className='btn btnMain btnMainPopup'
role='button'
href='https://apps.apple.com/us/app/nostore/id1666553677'
target='_blank'
>
Nostore Browser Extension
</a>
</div>
</div>
</div>
</div>
</div>
</div>
)}
</>
)
}

View File

@ -1,13 +1,17 @@
import { Outlet } from 'react-router-dom'
import { Footer } from './footer'
import { Header } from './header'
import { SocialNav } from './socialNav'
import { Head } from './head'
export const Layout = () => {
return (
<>
<Head />
<Header />
<Outlet />
<Footer />
<SocialNav />
</>
)
}

114
src/layout/socialNav.tsx Normal file
View File

@ -0,0 +1,114 @@
import { useAppSelector } from 'hooks'
import { nip19 } from 'nostr-tools'
import { useState } from 'react'
import { NavLink, NavLinkProps } from 'react-router-dom'
import { appRoutes, getProfilePageRoute } from 'routes'
import 'styles/socialNav.css'
import { npubToHex } from 'utils'
export const SocialNav = () => {
const [isCollapsed, setIsCollapsed] = useState<boolean>(false)
const userState = useAppSelector((state) => state.user)
let profileRoute = ''
if (userState.auth && userState.user) {
// Redirect to user's profile is no profile is linked
const userHexKey = npubToHex(userState.user.npub as string)
if (userHexKey) {
profileRoute = getProfilePageRoute(
nip19.nprofileEncode({
pubkey: userHexKey
})
)
}
}
const toggleNav = () => {
setIsCollapsed(!isCollapsed)
}
return (
<div
className='socialNav'
style={{
transform: isCollapsed ? 'translateX(0)' : 'translateX(50%)',
right: isCollapsed ? '0%' : '50%'
}}
>
<div className='socialNavInsideWrapper'>
{!isCollapsed && (
<div className='socialNavInside'>
<NavButton
to={appRoutes.home}
viewBox='0 -32 576 576'
svgPath='M511.8 287.6L512.5 447.7C512.5 450.5 512.3 453.1 512 455.8V472C512 494.1 494.1 512 472 512H456C454.9 512 453.8 511.1 452.7 511.9C451.3 511.1 449.9 512 448.5 512H392C369.9 512 352 494.1 352 472V384C352 366.3 337.7 352 320 352H256C238.3 352 224 366.3 224 384V472C224 494.1 206.1 512 184 512H128.1C126.6 512 125.1 511.9 123.6 511.8C122.4 511.9 121.2 512 120 512H104C81.91 512 64 494.1 64 472V360C64 359.1 64.03 358.1 64.09 357.2V287.6H32.05C14.02 287.6 0 273.5 0 255.5C0 246.5 3.004 238.5 10.01 231.5L266.4 8.016C273.4 1.002 281.4 0 288.4 0C295.4 0 303.4 2.004 309.5 7.014L416 100.7V64C416 46.33 430.3 32 448 32H480C497.7 32 512 46.33 512 64V185L564.8 231.5C572.8 238.5 576.9 246.5 575.8 255.5C575.8 273.5 560.8 287.6 543.8 287.6L511.8 287.6z'
/>
<NavButton
to={appRoutes.feed}
svgPath='M88 48C101.3 48 112 58.75 112 72V120C112 133.3 101.3 144 88 144H40C26.75 144 16 133.3 16 120V72C16 58.75 26.75 48 40 48H88zM480 64C497.7 64 512 78.33 512 96C512 113.7 497.7 128 480 128H192C174.3 128 160 113.7 160 96C160 78.33 174.3 64 192 64H480zM480 224C497.7 224 512 238.3 512 256C512 273.7 497.7 288 480 288H192C174.3 288 160 273.7 160 256C160 238.3 174.3 224 192 224H480zM480 384C497.7 384 512 398.3 512 416C512 433.7 497.7 448 480 448H192C174.3 448 160 433.7 160 416C160 398.3 174.3 384 192 384H480zM16 232C16 218.7 26.75 208 40 208H88C101.3 208 112 218.7 112 232V280C112 293.3 101.3 304 88 304H40C26.75 304 16 293.3 16 280V232zM88 368C101.3 368 112 378.7 112 392V440C112 453.3 101.3 464 88 464H40C26.75 464 16 453.3 16 440V392C16 378.7 26.75 368 40 368H88z'
/>
<NavButton
to={appRoutes.notifications}
svgPath='M256 32V51.2C329 66.03 384 130.6 384 208V226.8C384 273.9 401.3 319.2 432.5 354.4L439.9 362.7C448.3 372.2 450.4 385.6 445.2 397.1C440 408.6 428.6 416 416 416H32C19.4 416 7.971 408.6 2.809 397.1C-2.353 385.6-.2883 372.2 8.084 362.7L15.5 354.4C46.74 319.2 64 273.9 64 226.8V208C64 130.6 118.1 66.03 192 51.2V32C192 14.33 206.3 0 224 0C241.7 0 256 14.33 256 32H256zM224 512C207 512 190.7 505.3 178.7 493.3C166.7 481.3 160 464.1 160 448H288C288 464.1 281.3 481.3 269.3 493.3C257.3 505.3 240.1 512 224 512z'
/>
<NavButton
to={appRoutes.search}
svgPath='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'
/>
{!!userState.auth && (
<NavButton
to={profileRoute}
svgPath='M256 288c79.53 0 144-64.47 144-144s-64.47-144-144-144c-79.52 0-144 64.47-144 144S176.5 288 256 288zM351.1 320H160c-88.36 0-160 71.63-160 160c0 17.67 14.33 32 31.1 32H480c17.67 0 31.1-14.33 31.1-32C512 391.6 440.4 320 351.1 320z'
/>
)}
</div>
)}
<div className='socialNavCollapse' onClick={toggleNav}>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox='-128 0 512 512'
width='1em'
height='1em'
fill='currentColor'
className='socialNavCollapseIcon'
style={{
transform: isCollapsed ? 'rotate(0deg)' : 'rotate(180deg)'
}}
>
<path d='M192 448c-8.188 0-16.38-3.125-22.62-9.375l-160-160c-12.5-12.5-12.5-32.75 0-45.25l160-160c12.5-12.5 32.75-12.5 45.25 0s12.5 32.75 0 45.25L77.25 256l137.4 137.4c12.5 12.5 12.5 32.75 0 45.25C208.4 444.9 200.2 448 192 448z'></path>
</svg>
</div>
</div>
</div>
)
}
interface NavButtonProps extends NavLinkProps {
svgPath: string
viewBox?: string
}
const NavButton = ({
svgPath,
viewBox = '0 0 512 512',
...rest
}: NavButtonProps) => (
<NavLink
{...rest}
className={({ isActive }) =>
`btn btnMain socialNavInsideBtn ${
isActive ? 'socialNavInsideBtnActive' : ''
}`
}
>
<svg
xmlns='http://www.w3.org/2000/svg'
viewBox={viewBox}
width='1em'
height='1em'
fill='currentColor'
>
<path d={svgPath}></path>
</svg>
</NavLink>
)

View File

@ -1,17 +1,20 @@
import React from 'react'
import ReactDOM from 'react-dom/client'
import { Provider } from 'react-redux'
import { BrowserRouter } from 'react-router-dom'
import { ToastContainer } from 'react-toastify'
import 'react-toastify/dist/ReactToastify.css'
import App from './App.tsx'
import './index.css'
import { store } from './store/index.ts'
import { NDKContextProvider } from 'contexts/NDKContext.tsx'
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<Provider store={store}>
<BrowserRouter>
<NDKContextProvider>
<App />
</BrowserRouter>
</NDKContextProvider>
<ToastContainer />
</Provider>
</React.StrictMode>
)

30
src/pages/404.tsx Normal file
View File

@ -0,0 +1,30 @@
import { Link } from 'react-router-dom'
import { appRoutes } from 'routes'
export const NotFoundPage = () => {
return (
<div className='InnerBodyMain'>
<div className='ContainerMain'>
<div className='IBMSecMainGroup IBMSecMainGroupAlt'>
<div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMTitleMain'>
<h2 className='IBMSMTitleMainHeading'>Page not found</h2>
</div>
<div>
<p>The page you're attempting to visit doesn't exist</p>
</div>
<div className='IBMSMAction'>
<Link
to={appRoutes.home}
className='btn btnMain IBMSMActionBtn'
type='button'
>
Go home
</Link>
</div>
</div>
</div>
</div>
</div>
)
}

View File

@ -2,6 +2,8 @@ import { FAQAccordion } from '../components/FAQAccordion'
import '../styles/about.css'
import '../styles/FAQ.css'
import '../styles/styles.css'
import thumb from '../assets/img/DEGM Thumb.png'
import vivian from '../assets/img/vivian james.png'
export type FAQItem = {
question: string
@ -10,53 +12,68 @@ export type FAQItem = {
const FAQ_ITEMS: FAQItem[] = [
{
question: "You don't host mods files?",
answer: "Nope. And that's for the better."
question: "You don't host mod files?",
answer: `We don't handle that directly, but you, as the creator, will.`
},
{
question: 'How do you assure security on the files?',
answer:
"We don't, but you, as the user, do. You know how sometimes you go to some forums or social sites and someone shares a download link, you'd see someone asking 'is this link safe?' and people would reply with yes/no, etc. People will be doing that same process here, with a bit of help by having a simple reaction system for each link so you'd get a quicker idea on the safety of these links that's being provided by mod creators."
question:
'How do you assure security of game mod files that someone downloads?',
answer: `We don't assure security directly. However, we will provide a reaction
system to help users gauge the safety of download links, and mod creators
are encouraged to include scan links.`
},
{
question: "Why are you quoting 'account'?",
answer:
"Because technically you aren't creating an 'account', you're generating/creating/obtaining an address/key-pair. Check the next question/answer to get more details."
answer: `We use 'account' in quotes because technically you're generating a
key pair, not creating a traditional account. The next FAQ explains more.`
},
{
question: "You 'can't' remove mods or ban accounts? How does that work?",
answer:
"I'll try my best to simplify the technicalities of this answer... Because of the nature of Nostr, the 'account' creation process involves the generation/obtaining two cryptographic key pairs, one private (think of that as your password that you cannot change), and one public (think of that as your username that you cannot change). These keypairs are coming from the Nostr protocol itself, and nobody controls Nostr, it's just there. Considering that, we can't 'ban' anyone directly. We might have a mute-list with public address that won't show their posts/submissions on this site, but they are still there and accessible by anyone. It's the same with someone's posts, we can't touch those as well. Gist: If someone put a gun to your / the team's head, will you censor or ban anyone? No, because we can't."
answer: `I'll try my best to simplify the technicalities of this answer... Because of the nature of Nostr,
the 'account' creation process involves the generation/obtaining two cryptographic key pairs,
one private (think of that as your password that you cannot change), and one public (think of that
as your username that you cannot change). These key pairs are coming from the Nostr protocol itself,
and nobody controls Nostr, it's just there. Considering that, we can't 'ban' anyone directly. We might
have a mute-list with public addresses that won't show their posts/submissions on this site, but they
are still there and accessible by anyone. It's the same with someone's posts, we can't touch those as well.
Gist: If someone put a gun to your / the team's head, will you censor or ban anyone? No, because we can't.`
},
{
question:
"You can't do anything about any mod or person? Nothing at all? What about the illegal stuff?",
answer:
"Directly removing the content can't be done, and directly 'banning' someone also can't be done. At most, pages/posts and people can be filtered out / hidden from the website, but people can still see the content with a quick copy/paste."
answer: `Direct removal or banning is not possible. We can only filter or
hide content on the site, but it remains accessible on here and elsewhere.`
},
{
question:
'Why did you have to add Bitcoin? Why not traditional payment methods like Visa, PayPal, etc?',
answer:
"For various reasons. With traditional payment methods, not everyone has access to them, they can pressure/threaten us or mod creators or even gamers to censor or ban or not use this site by holding our funds or take them away, they can prevent you from tipping on this site or specific mod creators, and there's no privacy, among other reasons. With Bitcoin, anyone has access to it, nobody controls it so you can't be threatened with/by it, you can actually own it so you control it, and with it you're Pseudonymous."
answer: `For various reasons. With traditional payment methods, not everyone has access to them, they
can pressure or threaten us, mod creators, or even gamers to censor or ban, or restrict usage of this site
by holding our funds or stealing them. They can prevent you from tipping on this site or specific mod creators,
and there's no privacy. These are just a few reasons why we aren't using traditional payment methods.
With Bitcoin, anyone has access to it, nobody controls it so you can't be threatened with/by it,
you can actually own and properly control it, and it provides pseudonymity.`
},
{
question: 'Is this an open-source project?',
answer: "Yes. Here's the repo."
answer: `Yes, DEG Mods is open-source. You can access the code repository
[here](https://github.com/your-repo).`
},
{
question: "Who's developing / maintaining DEG Mods?",
answer:
'Considering this is an open-source project, anyone can contribute to its development and maintenance. With that said, the initial idea-tor, designer, and frontend developer is Freakoverse, the initial backend developer and Nostr implementor is NAME.'
answer: `Considering this is an open-source project, anyone can contribute to its development and maintenance.
With that said, the initial idea-tor, designer, and frontend developer is [Freakoverse](https://primal.net/p/npub18n4ysp43ux5c98fs6h9c57qpr4p8r3j8f6e32v0vj8egzy878aqqyzzk9r), and the co-developer
is [Nostr Dev](https://nostrdev.com/).`
},
{
question: "Who's that character above with the orange hair?",
answer: "That's Vivian James. A gamer that just wants to game in peace."
answer: `That's Vivian James, a fictional gamer character.`
},
{
question: "Who's that character above with the purple hair?",
answer:
"That's Moda-chan. DEG Mods' mascot. She's a master game mod creator! (Yes, she was AI generated, as such her design is temporary and will be replaced with a design created by an artist (or artists) when that time comes.)"
answer: `That's Moda-chan. DEG Mods' mascot. She's a master game mod creator! (Yes, she was AI-generated,
as such her design is temporary and will be replaced with a design created by an artist (or artists)
when that time comes.)`
}
]
@ -80,7 +97,7 @@ export const AboutPage = () => {
>
Liberating Game Mods
</h1>
<img src='/assets/img/DEGM%20Thumb.png' alt='Thumbnail' />
<img src={thumb} alt='Thumbnail' />
<p className='LearnTextPara'>
Never get your game mods censored, get banned, lose your
history, nor lose the connection between creators and fans.
@ -108,7 +125,11 @@ export const AboutPage = () => {
imposing their ideals. DEG Mods aims to change that
narrative by being developed on Nostr, a revolutionary new
communications protocol.{' '}
<a className='linkMain' href='#'>
<a
className='linkMain'
href='https://nostr.com/'
target='_blank'
>
Learn more about Nostr here.
</a>
<br />
@ -159,9 +180,11 @@ export const AboutPage = () => {
on this platform/site. Pretend its not even there. We're not
even making any money out of this project/site, in-fact,
we're running at a loss (unless direct donations/tips covers
it). This is just a passion project to help free (liberate)
game mods and their creators, and this part potentially
helps them financially, even those in other countries where
it, and/or we managed to add reasonable monetization systems
to help cover further development and maintenance costs).
This is just a passion project to help free (liberate) game
mods and their creators, and this part potentially helps
them financially, even those in other countries where
"normal" methods of money payment/transfer are not an
option. You can just find the mod you want and download it,
or upload the mod you've created, and never even touch
@ -214,10 +237,7 @@ export const AboutPage = () => {
</a>
</div>
</div>
<img
className='LearnTextCharacterImgRight'
src='assets/img/vivian%20james.png'
/>
<img className='LearnTextCharacterImgRight' src={vivian} />
</div>
<div className='LearnFAQ'>

View File

@ -3,6 +3,7 @@ import '../styles/filters.css'
import '../styles/pagination.css'
import '../styles/search.css'
import '../styles/styles.css'
import placeholder from '../assets/img/DEGMods Placeholder Img.png'
export const BlogsPage = () => {
return (
@ -12,7 +13,7 @@ export const BlogsPage = () => {
<div className='IBMSecMain'>
<div className='SearchMainWrapper'>
<div className='IBMSMTitleMain'>
<h2 className='IBMSMTitleMainHeading'>Blogs</h2>
<h2 className='IBMSMTitleMainHeading'>Blogs (WIP)</h2>
</div>
<div className='SearchMain'>
<div className='SearchMainInside'>
@ -92,14 +93,14 @@ export const BlogsPage = () => {
<div className='IBMSecMain IBMSMListWrapper'>
<div className='IBMSMList'>
<BlogCard backgroundLink='https://image.nostr.build/d6af39fb1d47feaf09831ddf9d447ccc435ba10fcbb9b6d6e800390f6bbac851.png' />
<BlogCard backgroundLink='https://nichegamer.com/wp-content/uploads/2023/01/onimai-01-07-2023.jpg' />
<BlogCard backgroundLink='https://pbs.twimg.com/media/GDrRJOOXYAAeysT.jpg:large' />
<BlogCard backgroundLink='/assets/img/DEGMods%20Placeholder%20Img.png' />
<BlogCard backgroundLink='/assets/img/DEGMods%20Placeholder%20Img.png' />
<BlogCard backgroundLink='/assets/img/DEGMods%20Placeholder%20Img.png' />
<BlogCard backgroundLink='/assets/img/DEGMods%20Placeholder%20Img.png' />
<BlogCard backgroundLink='/assets/img/DEGMods%20Placeholder%20Img.png' />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
<BlogCard backgroundLink={placeholder} />
</div>
</div>

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