├── .dockerignore ├── .drone.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── release.yml ├── .gitignore ├── .prettierignore ├── .vscode ├── extensions.json └── settings.json ├── .yarn ├── releases │ └── yarn-4.1.1.cjs └── sdks │ ├── eslint │ ├── bin │ │ └── eslint.js │ ├── lib │ │ ├── api.js │ │ └── unsupported-api.js │ └── package.json │ ├── integrations.yml │ ├── prettier │ ├── bin │ │ └── prettier.cjs │ ├── index.cjs │ └── package.json │ └── typescript │ ├── bin │ ├── tsc │ └── tsserver │ ├── lib │ ├── tsc.js │ ├── tsserver.js │ ├── tsserverlibrary.js │ └── typescript.js │ └── package.json ├── .yarnrc ├── .yarnrc.yml ├── Dockerfile ├── Dockerfile.prebuilt ├── LICENSE ├── README.md ├── crowdin.yml ├── dev-docs └── query.md ├── docker └── nginx.conf ├── functions ├── _middleware.ts └── tsconfig.json ├── maintainers.yaml ├── nap.yaml ├── package.json ├── packages ├── app │ ├── .eslintrc.js │ ├── .gitignore │ ├── CHANGELOG.md │ ├── babel.config.json │ ├── config │ │ ├── README.md │ │ ├── default.json │ │ ├── iris.json │ │ ├── meku.json │ │ ├── nostr.json │ │ ├── snort.json │ │ └── soloco.json │ ├── custom.d.ts │ ├── index.html │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── iris │ │ │ ├── .well-known │ │ │ │ └── assetlinks.json │ │ │ ├── _headers │ │ │ ├── favicon.png │ │ │ ├── img │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── icon128.png │ │ │ │ ├── irisconnects.png │ │ │ │ ├── maskable_icon.png │ │ │ │ └── maskable_icon_x192.png │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ ├── nostr │ │ │ ├── _headers │ │ │ ├── favicon.png │ │ │ ├── img │ │ │ │ └── apple-touch-icon.png │ │ │ └── nostr.jpg │ │ ├── phoenix │ │ │ ├── .well-known │ │ │ │ ├── apple-app-site-association │ │ │ │ └── assetlinks.json │ │ │ ├── _headers │ │ │ ├── favicon.png │ │ │ ├── img │ │ │ │ └── apple-touch-icon.png │ │ │ ├── logo.svg │ │ │ ├── logo_256.png │ │ │ ├── manifest.json │ │ │ └── robots.txt │ │ └── snort │ │ │ ├── .well-known │ │ │ ├── apple-app-site-association │ │ │ └── assetlinks.json │ │ │ ├── _headers │ │ │ ├── favicon.png │ │ │ ├── img │ │ │ └── apple-touch-icon.png │ │ │ ├── manifest.json │ │ │ ├── nostrich_256.png │ │ │ ├── nostrich_512.png │ │ │ ├── nostrich_orig.jpeg │ │ │ └── robots.txt │ ├── src │ │ ├── Cache │ │ │ ├── ChatCache.ts │ │ │ ├── CommunityLeadersStore.tsx │ │ │ ├── EventCacheWorker.ts │ │ │ ├── GiftWrapCache.ts │ │ │ ├── ProfileWorkerCache.ts │ │ │ ├── RefreshFeedCache.ts │ │ │ ├── TextCache.tsx │ │ │ ├── UserFollowsWorker.ts │ │ │ └── index.ts │ │ ├── Components │ │ │ ├── Button │ │ │ │ ├── AsyncButton.css │ │ │ │ ├── AsyncButton.tsx │ │ │ │ ├── AsyncIcon.tsx │ │ │ │ ├── BackButton.css │ │ │ │ ├── BackButton.tsx │ │ │ │ ├── CloseButton.tsx │ │ │ │ ├── IconButton.tsx │ │ │ │ ├── LogoutButton.tsx │ │ │ │ └── NavLink.tsx │ │ │ ├── Collapsed.tsx │ │ │ ├── CommunityLeaders │ │ │ │ ├── Award.tsx │ │ │ │ └── LeaderBadge.tsx │ │ │ ├── Copy │ │ │ │ ├── Copy.css │ │ │ │ └── Copy.tsx │ │ │ ├── Embed │ │ │ │ ├── AppleMusicEmbed.tsx │ │ │ │ ├── CashuNuts.css │ │ │ │ ├── CashuNuts.tsx │ │ │ │ ├── GenericPlayer.tsx │ │ │ │ ├── Hashtag.css │ │ │ │ ├── Hashtag.tsx │ │ │ │ ├── HyperText.tsx │ │ │ │ ├── Invoice.css │ │ │ │ ├── Invoice.tsx │ │ │ │ ├── LinkPreview.css │ │ │ │ ├── LinkPreview.tsx │ │ │ │ ├── MagnetLink.tsx │ │ │ │ ├── MediaElement.tsx │ │ │ │ ├── Mention.tsx │ │ │ │ ├── MixCloudEmbed.tsx │ │ │ │ ├── NostrLink.tsx │ │ │ │ ├── NostrNestsEmbed.tsx │ │ │ │ ├── PubkeyList.tsx │ │ │ │ ├── SoundCloudEmded.tsx │ │ │ │ ├── SpotifyEmbed.tsx │ │ │ │ ├── TidalEmbed.tsx │ │ │ │ ├── TwitchEmbed.tsx │ │ │ │ ├── WavlakeEmbed.tsx │ │ │ │ ├── YoutubeEmbed.tsx │ │ │ │ ├── ZapstrEmbed.css │ │ │ │ └── ZapstrEmbed.tsx │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── ErrorOrOffline.tsx │ │ │ ├── Event │ │ │ │ ├── Application.tsx │ │ │ │ ├── Create │ │ │ │ │ ├── NoteCreator.tsx │ │ │ │ │ ├── NoteCreatorButton.tsx │ │ │ │ │ ├── OkResponseRow.tsx │ │ │ │ │ └── util.ts │ │ │ │ ├── EventComponent.css │ │ │ │ ├── EventComponent.tsx │ │ │ │ ├── FileUpload.tsx │ │ │ │ ├── HiddenNote.tsx │ │ │ │ ├── LoadMore.tsx │ │ │ │ ├── LongFormText.css │ │ │ │ ├── LongFormText.tsx │ │ │ │ ├── Markdown.css │ │ │ │ ├── Markdown.tsx │ │ │ │ ├── NostrFileHeader.tsx │ │ │ │ ├── Note │ │ │ │ │ ├── ClientTag.tsx │ │ │ │ │ ├── Note.tsx │ │ │ │ │ ├── NoteAppHandler.tsx │ │ │ │ │ ├── NoteContextMenu.tsx │ │ │ │ │ ├── NoteFooter │ │ │ │ │ │ ├── AsyncFooterIcon.tsx │ │ │ │ │ │ ├── FooterZapButton.tsx │ │ │ │ │ │ ├── LikeButton.tsx │ │ │ │ │ │ ├── NoteFooter.tsx │ │ │ │ │ │ ├── PowIcon.tsx │ │ │ │ │ │ ├── ReplyButton.tsx │ │ │ │ │ │ ├── RepostButton.tsx │ │ │ │ │ │ └── ZapperQueue.tsx │ │ │ │ │ ├── NoteGhost.tsx │ │ │ │ │ ├── NoteHeader.tsx │ │ │ │ │ ├── NoteQuote.tsx │ │ │ │ │ ├── NoteText.tsx │ │ │ │ │ ├── NoteTime.tsx │ │ │ │ │ ├── ReactionsModal.css │ │ │ │ │ ├── ReactionsModal.tsx │ │ │ │ │ ├── ReplyTag.tsx │ │ │ │ │ ├── TranslationInfo.tsx │ │ │ │ │ └── types.tsx │ │ │ │ ├── NoteReaction.css │ │ │ │ ├── NoteReaction.tsx │ │ │ │ ├── Poll.tsx │ │ │ │ ├── Reveal.tsx │ │ │ │ ├── RevealMedia.tsx │ │ │ │ ├── Thread │ │ │ │ │ ├── Divider.tsx │ │ │ │ │ ├── Subthread.tsx │ │ │ │ │ ├── Thread.css │ │ │ │ │ ├── Thread.tsx │ │ │ │ │ ├── ThreadNote.tsx │ │ │ │ │ ├── ThreadRoute.tsx │ │ │ │ │ ├── TierThree.tsx │ │ │ │ │ ├── TierTwo.tsx │ │ │ │ │ └── util.ts │ │ │ │ ├── Zap.css │ │ │ │ ├── Zap.tsx │ │ │ │ ├── ZapButton.css │ │ │ │ ├── ZapButton.tsx │ │ │ │ ├── ZapGoal.css │ │ │ │ ├── ZapGoal.tsx │ │ │ │ └── ZapsSummary.tsx │ │ │ ├── Feed │ │ │ │ ├── DisplayAsSelector.tsx │ │ │ │ ├── Generic.tsx │ │ │ │ ├── ImageGridItem.tsx │ │ │ │ ├── LoadMore.tsx │ │ │ │ ├── RootTabItems.tsx │ │ │ │ ├── RootTabs.css │ │ │ │ ├── RootTabs.tsx │ │ │ │ ├── Timeline.css │ │ │ │ ├── Timeline.tsx │ │ │ │ ├── TimelineChunk.tsx │ │ │ │ ├── TimelineFollows.tsx │ │ │ │ ├── TimelineFragment.tsx │ │ │ │ ├── TimelineRenderer.tsx │ │ │ │ └── UsersFeed.tsx │ │ │ ├── Icons │ │ │ │ ├── Alby.tsx │ │ │ │ ├── BlueWallet.tsx │ │ │ │ ├── Cashu.tsx │ │ │ │ ├── ECash.tsx │ │ │ │ ├── Icon.tsx │ │ │ │ ├── NWC.tsx │ │ │ │ ├── Nostrich.tsx │ │ │ │ ├── Spinner.css │ │ │ │ ├── Spinner.tsx │ │ │ │ ├── Toggle.css │ │ │ │ ├── Toggle.tsx │ │ │ │ └── icons.svg │ │ │ ├── IntlProvider │ │ │ │ ├── IntlProvider.tsx │ │ │ │ ├── IntlProviderUtils.tsx │ │ │ │ ├── langStore.tsx │ │ │ │ └── useLocale.tsx │ │ │ ├── Invite.tsx │ │ │ ├── IrisAccount │ │ │ │ ├── AccountName.tsx │ │ │ │ ├── ActiveAccount.tsx │ │ │ │ ├── IrisAccount.tsx │ │ │ │ └── ReservedAccount.tsx │ │ │ ├── LineChart.tsx │ │ │ ├── LiveStream │ │ │ │ ├── LiveEvent.tsx │ │ │ │ ├── LiveStreams.tsx │ │ │ │ ├── VU.tsx │ │ │ │ ├── livekit.tsx │ │ │ │ └── nests-participants.tsx │ │ │ ├── Modal │ │ │ │ ├── Modal.css │ │ │ │ └── Modal.tsx │ │ │ ├── Nip5Service.tsx │ │ │ ├── Offline.tsx │ │ │ ├── PageSpinner.tsx │ │ │ ├── PinPrompt │ │ │ │ ├── PinPrompt.css │ │ │ │ └── PinPrompt.tsx │ │ │ ├── Progress │ │ │ │ ├── Progress.css │ │ │ │ └── Progress.tsx │ │ │ ├── ProxyImg.tsx │ │ │ ├── QrCode.tsx │ │ │ ├── ReBroadcaster.tsx │ │ │ ├── Relay │ │ │ │ ├── Relay.tsx │ │ │ │ ├── RelaysMetadata.tsx │ │ │ │ ├── paid.tsx │ │ │ │ ├── permissions.tsx │ │ │ │ ├── software.tsx │ │ │ │ ├── status-label.tsx │ │ │ │ ├── uptime-label.tsx │ │ │ │ └── uptime.tsx │ │ │ ├── Review.tsx │ │ │ ├── RightWidgets │ │ │ │ ├── articles.tsx │ │ │ │ ├── base.tsx │ │ │ │ ├── index.tsx │ │ │ │ ├── invite-friends.tsx │ │ │ │ └── mini-stream.tsx │ │ │ ├── ScrollToTop.tsx │ │ │ ├── SearchBox │ │ │ │ ├── SearchBox.css │ │ │ │ └── SearchBox.tsx │ │ │ ├── Spotlight │ │ │ │ ├── SpotlightMedia.tsx │ │ │ │ └── SpotlightThreadModal.tsx │ │ │ ├── SuggestedProfiles.tsx │ │ │ ├── TabSelectors │ │ │ │ └── TabSelectors.tsx │ │ │ ├── Tasks │ │ │ │ ├── BackupKey.tsx │ │ │ │ ├── DonateTask.tsx │ │ │ │ ├── FollowMorePeople.tsx │ │ │ │ ├── Nip5Task.tsx │ │ │ │ ├── NoticeZapPool.tsx │ │ │ │ ├── RenewSubscription.tsx │ │ │ │ ├── TaskList.css │ │ │ │ ├── TaskList.tsx │ │ │ │ └── index.ts │ │ │ ├── Text │ │ │ │ ├── DisableMedia.tsx │ │ │ │ ├── HighlightedText.tsx │ │ │ │ ├── Text.css │ │ │ │ ├── Text.tsx │ │ │ │ └── const.ts │ │ │ ├── Textarea │ │ │ │ ├── Textarea.css │ │ │ │ └── Textarea.tsx │ │ │ ├── Toaster │ │ │ │ ├── Toaster.css │ │ │ │ └── Toaster.tsx │ │ │ ├── Trending │ │ │ │ ├── ShortNote.tsx │ │ │ │ ├── TrendingHashtags.tsx │ │ │ │ ├── TrendingPosts.tsx │ │ │ │ └── TrendingUsers.tsx │ │ │ ├── Upload │ │ │ │ └── file-picker.tsx │ │ │ ├── User │ │ │ │ ├── AnimalName.ts │ │ │ │ ├── Avatar.tsx │ │ │ │ ├── AvatarEditor.css │ │ │ │ ├── AvatarEditor.tsx │ │ │ │ ├── AvatarGroup.tsx │ │ │ │ ├── BadgeList.css │ │ │ │ ├── BadgeList.tsx │ │ │ │ ├── Bookmarks.tsx │ │ │ │ ├── Debug.tsx │ │ │ │ ├── DisplayName.css │ │ │ │ ├── DisplayName.tsx │ │ │ │ ├── FollowButton.tsx │ │ │ │ ├── FollowDistanceIndicator.tsx │ │ │ │ ├── FollowListBase.tsx │ │ │ │ ├── FollowedBy.tsx │ │ │ │ ├── Following.css │ │ │ │ ├── Following.tsx │ │ │ │ ├── FollowsYou.css │ │ │ │ ├── FollowsYou.tsx │ │ │ │ ├── MuteButton.tsx │ │ │ │ ├── MutedList.tsx │ │ │ │ ├── Nip05.css │ │ │ │ ├── Nip05.tsx │ │ │ │ ├── NoteToSelf.css │ │ │ │ ├── NoteToSelf.tsx │ │ │ │ ├── ProfileCard.css │ │ │ │ ├── ProfileCard.tsx │ │ │ │ ├── ProfileImage.tsx │ │ │ │ ├── ProfileLink.tsx │ │ │ │ ├── ProfilePreview.tsx │ │ │ │ ├── UserWebsiteLink.css │ │ │ │ ├── UserWebsiteLink.tsx │ │ │ │ └── Username.tsx │ │ │ ├── WarningNotice │ │ │ │ ├── WarningNotice.css │ │ │ │ └── WarningNotice.tsx │ │ │ ├── ZapModal │ │ │ │ ├── SuccessAction.tsx │ │ │ │ ├── ZapModal.css │ │ │ │ ├── ZapModal.tsx │ │ │ │ ├── ZapModalInput.tsx │ │ │ │ ├── ZapModalInvoice.tsx │ │ │ │ ├── ZapModalTitle.tsx │ │ │ │ ├── ZapType.tsx │ │ │ │ └── ZapTypeSelector.tsx │ │ │ ├── flyout.tsx │ │ │ ├── kind-name.tsx │ │ │ ├── messages.ts │ │ │ ├── nip.tsx │ │ │ └── zap-amount.tsx │ │ ├── Db │ │ │ ├── FuzzySearch.ts │ │ │ └── index.ts │ │ ├── External │ │ │ ├── NostrBand.ts │ │ │ ├── NostrServices.ts │ │ │ ├── SemisolDev.ts │ │ │ ├── SnortApi.ts │ │ │ └── index.ts │ │ ├── Feed │ │ │ ├── ArticlesFeed.ts │ │ │ ├── BadgesFeed.ts │ │ │ ├── FollowersFeed.ts │ │ │ ├── FollowsFeed.ts │ │ │ ├── HashtagsFeed.ts │ │ │ ├── LoginFeed.ts │ │ │ ├── RelayState.ts │ │ │ ├── RelaysFeed.tsx │ │ │ ├── StatusFeed.ts │ │ │ ├── ThreadFeed.ts │ │ │ ├── TimelineFeed.ts │ │ │ ├── WorkerRelayView.ts │ │ │ └── ZapsFeed.ts │ │ ├── Hooks │ │ │ ├── useAppHandler.ts │ │ │ ├── useCachedFetch.ts │ │ │ ├── useCloseRelays.ts │ │ │ ├── useCommunityLeaders.tsx │ │ │ ├── useCopy.ts │ │ │ ├── useDiscoverMediaServers.ts │ │ │ ├── useEventPublisher.tsx │ │ │ ├── useFollowControls.ts │ │ │ ├── useHistoryState.tsx │ │ │ ├── useHorizontalScroll.tsx │ │ │ ├── useImgProxy.ts │ │ │ ├── useInteractionCache.tsx │ │ │ ├── useKeyboardShortcut.ts │ │ │ ├── useLists.tsx │ │ │ ├── useLiveStreams.ts │ │ │ ├── useLoading.tsx │ │ │ ├── useLogin.tsx │ │ │ ├── useLoginHandler.tsx │ │ │ ├── useLoginRelays.tsx │ │ │ ├── useMediaServerList.ts │ │ │ ├── useModeration.tsx │ │ │ ├── usePageDimensions.tsx │ │ │ ├── usePreferences.ts │ │ │ ├── useProfileLink.ts │ │ │ ├── useProfileSearch.tsx │ │ │ ├── useRates.tsx │ │ │ ├── useRelays.tsx │ │ │ ├── useTextTransformCache.tsx │ │ │ ├── useTheme.tsx │ │ │ ├── useTimelineChunks.ts │ │ │ ├── useTimelineWindow.tsx │ │ │ ├── useWindowSize.ts │ │ │ └── useWoT.ts │ │ ├── Pages │ │ │ ├── About.tsx │ │ │ ├── CacheDebug.tsx │ │ │ ├── Deck │ │ │ │ ├── Articles.tsx │ │ │ │ ├── Columns.tsx │ │ │ │ ├── Deck.css │ │ │ │ └── DeckLayout.tsx │ │ │ ├── Discover.tsx │ │ │ ├── Donate │ │ │ │ ├── DonatePage.tsx │ │ │ │ ├── ZapPoolDonateSection.tsx │ │ │ │ └── const.ts │ │ │ ├── ErrorPage.tsx │ │ │ ├── FreeNostrAddressPage.tsx │ │ │ ├── HashTagsPage.tsx │ │ │ ├── HelpPage.tsx │ │ │ ├── Layout │ │ │ │ ├── Footer.tsx │ │ │ │ ├── HasNotificationsMarker.tsx │ │ │ │ ├── Header.tsx │ │ │ │ ├── Layout.css │ │ │ │ ├── LogoHeader.tsx │ │ │ │ ├── NavSidebar.tsx │ │ │ │ ├── NotificationsHeader.tsx │ │ │ │ ├── ProfileMenu.tsx │ │ │ │ ├── RightColumn.tsx │ │ │ │ ├── WalletBalance.tsx │ │ │ │ └── index.tsx │ │ │ ├── ListFeedPage.tsx │ │ │ ├── Messages │ │ │ │ ├── ChatParticipant.tsx │ │ │ │ ├── DM.css │ │ │ │ ├── DM.tsx │ │ │ │ ├── DmWindow.tsx │ │ │ │ ├── MessagesPage.tsx │ │ │ │ ├── NewChatWindow.tsx │ │ │ │ ├── UnreadCount.css │ │ │ │ ├── UnreadCount.tsx │ │ │ │ └── WriteMessage.tsx │ │ │ ├── NostrAddressPage.tsx │ │ │ ├── NostrLinkHandler.tsx │ │ │ ├── Notifications │ │ │ │ ├── NotificationGroup.tsx │ │ │ │ ├── Notifications.css │ │ │ │ ├── Notifications.tsx │ │ │ │ ├── getNotificationContext.tsx │ │ │ │ └── notificationContext.tsx │ │ │ ├── Profile │ │ │ │ ├── AvatarSection.tsx │ │ │ │ ├── MusicStatus.tsx │ │ │ │ ├── ProfileDetails.tsx │ │ │ │ ├── ProfilePage.css │ │ │ │ ├── ProfilePage.tsx │ │ │ │ ├── ProfileTabComponents.tsx │ │ │ │ ├── ProfileTabSelectors.tsx │ │ │ │ └── ProfileTabType.tsx │ │ │ ├── Root │ │ │ │ ├── ConversationsTab.tsx │ │ │ │ ├── DefaultTab.tsx │ │ │ │ ├── FollowSets.tsx │ │ │ │ ├── FollowedByFriendsTab.tsx │ │ │ │ ├── ForYouTab.tsx │ │ │ │ ├── Media.tsx │ │ │ │ ├── NotesTab.tsx │ │ │ │ ├── RootRoutes.tsx │ │ │ │ ├── RootTabRoutes.tsx │ │ │ │ └── TagsTab.tsx │ │ │ ├── SearchPage.tsx │ │ │ ├── TopicsPage.tsx │ │ │ ├── ZapPool │ │ │ │ ├── ZapPool.css │ │ │ │ ├── ZapPool.tsx │ │ │ │ ├── ZapPoolPageInner.tsx │ │ │ │ └── ZapPoolTarget.tsx │ │ │ ├── messages.ts │ │ │ ├── onboarding │ │ │ │ ├── discover.tsx │ │ │ │ ├── fixedModeration.tsx │ │ │ │ ├── fixedTopics.tsx │ │ │ │ ├── index.css │ │ │ │ ├── index.tsx │ │ │ │ ├── moderation.tsx │ │ │ │ ├── profile.tsx │ │ │ │ ├── start.tsx │ │ │ │ └── topics.tsx │ │ │ ├── settings │ │ │ │ ├── Accounts.tsx │ │ │ │ ├── Cache.tsx │ │ │ │ ├── Keys.css │ │ │ │ ├── Keys.tsx │ │ │ │ ├── Menu │ │ │ │ │ ├── Menu.tsx │ │ │ │ │ └── SettingsMenuComponent.tsx │ │ │ │ ├── Moderation.tsx │ │ │ │ ├── Notifications.tsx │ │ │ │ ├── Preferences.css │ │ │ │ ├── Preferences.tsx │ │ │ │ ├── Profile.css │ │ │ │ ├── Profile.tsx │ │ │ │ ├── Referrals.tsx │ │ │ │ ├── RelayInfo.tsx │ │ │ │ ├── Relays.tsx │ │ │ │ ├── Routes.tsx │ │ │ │ ├── SnortNostrAddressService.tsx │ │ │ │ ├── WalletSettings.tsx │ │ │ │ ├── handle │ │ │ │ │ ├── LNAddress.tsx │ │ │ │ │ ├── ListHandles.tsx │ │ │ │ │ ├── Manage.tsx │ │ │ │ │ ├── TransferHandle.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── media-settings.tsx │ │ │ │ ├── messages.ts │ │ │ │ ├── relays │ │ │ │ │ └── discover.tsx │ │ │ │ ├── saveRelays.tsx │ │ │ │ ├── tools │ │ │ │ │ ├── follows-relay-health.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── prune-follows.tsx │ │ │ │ │ └── sync-account.tsx │ │ │ │ └── wallet │ │ │ │ │ ├── Alby.tsx │ │ │ │ │ ├── LNDHub.tsx │ │ │ │ │ ├── NWC.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ └── utils.ts │ │ │ ├── subscribe │ │ │ │ ├── ManageSubscription.tsx │ │ │ │ ├── RenewSub.tsx │ │ │ │ ├── SubscriptionCard.tsx │ │ │ │ ├── index.css │ │ │ │ ├── index.tsx │ │ │ │ └── utils.tsx │ │ │ └── wallet │ │ │ │ ├── index.tsx │ │ │ │ ├── price-chart.tsx │ │ │ │ ├── receive.tsx │ │ │ │ └── send.tsx │ │ ├── State │ │ │ └── NoteCreator.ts │ │ ├── Utils │ │ │ ├── Const.ts │ │ │ ├── Login │ │ │ │ ├── Functions.ts │ │ │ │ ├── LoginSession.ts │ │ │ │ ├── MultiAccountStore.ts │ │ │ │ ├── Nip7OsSigner.ts │ │ │ │ ├── Preferences.ts │ │ │ │ └── index.ts │ │ │ ├── Nip05 │ │ │ │ ├── ServiceProvider.ts │ │ │ │ ├── SnortServiceProvider.ts │ │ │ │ └── Verifier.ts │ │ │ ├── Notifications.ts │ │ │ ├── Number.ts │ │ │ ├── Subscription │ │ │ │ └── index.ts │ │ │ ├── Thread │ │ │ │ ├── ChainKey.tsx │ │ │ │ ├── ThreadContext.tsx │ │ │ │ └── ThreadContextWrapper.tsx │ │ │ ├── Upload │ │ │ │ ├── blossom.ts │ │ │ │ └── index.ts │ │ │ ├── Utils.test.ts │ │ │ ├── ZapPoolController.ts │ │ │ ├── emoji-search.ts │ │ │ ├── getEventMedia.ts │ │ │ ├── index.ts │ │ │ ├── nip6.ts │ │ │ ├── stream.ts │ │ │ └── wasm.ts │ │ ├── Wallet │ │ │ └── index.ts │ │ ├── assets │ │ │ ├── fonts │ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa0ZL7W0Q5n-wU.woff2 │ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1ZL7W0Q5nw.woff2 │ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa1pL7W0Q5n-wU.woff2 │ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa25L7W0Q5n-wU.woff2 │ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2JL7W0Q5n-wU.woff2 │ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2ZL7W0Q5n-wU.woff2 │ │ │ │ ├── UcC73FwrK3iLTeHuS_fvQtMwCp50KnMa2pL7W0Q5n-wU.woff2 │ │ │ │ └── inter.css │ │ │ └── img │ │ │ │ ├── cashu.png │ │ │ │ ├── lnd-logo.png │ │ │ │ ├── nostrich.webp │ │ │ │ └── telegram.svg │ │ ├── bench.html │ │ ├── benchmarks.ts │ │ ├── chat │ │ │ ├── index.ts │ │ │ └── nip17.ts │ │ ├── hug.json │ │ ├── index.css │ │ ├── index.tsx │ │ ├── lang.json │ │ ├── service-worker.ts │ │ ├── setupTests.ts │ │ ├── system.ts │ │ ├── translations │ │ │ ├── af_ZA.json │ │ │ ├── ar_SA.json │ │ │ ├── az_AZ.json │ │ │ ├── ca_ES.json │ │ │ ├── cs_CZ.json │ │ │ ├── da_DK.json │ │ │ ├── de_DE.json │ │ │ ├── el_GR.json │ │ │ ├── en.json │ │ │ ├── es_ES.json │ │ │ ├── fa_IR.json │ │ │ ├── fi_FI.json │ │ │ ├── fr_FR.json │ │ │ ├── he_IL.json │ │ │ ├── hr_HR.json │ │ │ ├── hu_HU.json │ │ │ ├── id_ID.json │ │ │ ├── it_IT.json │ │ │ ├── ja_JP.json │ │ │ ├── ko_KR.json │ │ │ ├── ms_MY.json │ │ │ ├── nl_NL.json │ │ │ ├── no_NO.json │ │ │ ├── pa_IN.json │ │ │ ├── pl_PL.json │ │ │ ├── pt_BR.json │ │ │ ├── pt_PT.json │ │ │ ├── ro_RO.json │ │ │ ├── ru_RU.json │ │ │ ├── sr_SP.json │ │ │ ├── sv_SE.json │ │ │ ├── sw_KE.json │ │ │ ├── ta_IN.json │ │ │ ├── th_TH.json │ │ │ ├── tr_TR.json │ │ │ ├── uk_UA.json │ │ │ ├── vi_VN.json │ │ │ ├── zh_CN.json │ │ │ └── zh_TW.json │ │ ├── tz.json │ │ └── wdyr.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ └── vite.config.ts ├── bot │ ├── README.md │ ├── example │ │ └── simple.ts │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── typedoc.json ├── shared │ ├── package.json │ ├── src │ │ ├── LRUSet.ts │ │ ├── SortedMap │ │ │ ├── SortedMap.test.ts │ │ │ └── SortedMap.ts │ │ ├── const.ts │ │ ├── custom.d.ts │ │ ├── dexie-like.ts │ │ ├── external-store.ts │ │ ├── feed-cache.ts │ │ ├── index.ts │ │ ├── invoices.ts │ │ ├── lnurl.ts │ │ ├── utils.ts │ │ └── work-queue.ts │ ├── tsconfig.json │ └── typedoc.json ├── system-react │ ├── README.md │ ├── example │ │ └── example.tsx │ ├── package.json │ ├── src │ │ ├── context.tsx │ │ ├── index.ts │ │ ├── useEventFeed.ts │ │ ├── useEventReactions.tsx │ │ ├── useReactions.ts │ │ ├── useRequestBuilder.tsx │ │ ├── useSystemState.tsx │ │ ├── useUserProfile.ts │ │ └── useUserSearch.tsx │ ├── tsconfig.json │ └── typedoc.json ├── system-svelte │ ├── README.md │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── request-builder.ts │ ├── tsconfig.json │ └── typedoc.json ├── system-wasm │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── README.md │ ├── benches │ │ └── basic.rs │ ├── package.json │ ├── pkg │ │ ├── README.md │ │ ├── package.json │ │ ├── system_wasm.d.ts │ │ ├── system_wasm.js │ │ ├── system_wasm_bg.js │ │ ├── system_wasm_bg.wasm │ │ └── system_wasm_bg.wasm.d.ts │ ├── src │ │ ├── diff.rs │ │ ├── filter.rs │ │ ├── lib.rs │ │ ├── merge.rs │ │ └── pow.rs │ ├── system-query.iml │ └── typedoc.json ├── system-web │ ├── package.json │ ├── src │ │ └── index.ts │ ├── tsconfig.json │ └── typedoc.json ├── system │ ├── .npmignore │ ├── README.md │ ├── examples │ │ └── simple.ts │ ├── jest.config.js │ ├── package.json │ ├── src │ │ ├── background-loader.ts │ │ ├── cache-relay.ts │ │ ├── cache │ │ │ ├── events.ts │ │ │ ├── index.ts │ │ │ ├── relay-metric.ts │ │ │ ├── user-follows-lists.ts │ │ │ ├── user-metadata.ts │ │ │ └── user-relays.ts │ │ ├── connection-cache-relay.ts │ │ ├── connection-pool.ts │ │ ├── connection-stats.ts │ │ ├── connection.ts │ │ ├── const.ts │ │ ├── encryption │ │ │ ├── index.ts │ │ │ ├── nip44.ts │ │ │ └── pin-encrypted.ts │ │ ├── event-builder.ts │ │ ├── event-ext.ts │ │ ├── event-kind.ts │ │ ├── event-publisher.ts │ │ ├── filter-cache-layer.ts │ │ ├── impl │ │ │ ├── nip10.ts │ │ │ ├── nip22.ts │ │ │ ├── nip4.ts │ │ │ ├── nip44.ts │ │ │ ├── nip46.ts │ │ │ ├── nip55.ts │ │ │ ├── nip57.ts │ │ │ ├── nip7.ts │ │ │ ├── nip92.ts │ │ │ └── nip94.ts │ │ ├── index.ts │ │ ├── links.ts │ │ ├── negentropy │ │ │ ├── accumulator.ts │ │ │ ├── negentropy-flow.ts │ │ │ ├── negentropy.ts │ │ │ ├── utils.ts │ │ │ ├── vector-storage.ts │ │ │ └── wrapped-buffer.ts │ │ ├── nips.ts │ │ ├── nostr-link.ts │ │ ├── nostr-system.ts │ │ ├── nostr.ts │ │ ├── note-collection.ts │ │ ├── outbox │ │ │ ├── index.ts │ │ │ ├── outbox-model.ts │ │ │ └── relay-loader.ts │ │ ├── pow-util.ts │ │ ├── pow-worker.ts │ │ ├── pow.ts │ │ ├── profile-cache.ts │ │ ├── query-manager.ts │ │ ├── query-optimizer │ │ │ ├── index.ts │ │ │ ├── request-expander.ts │ │ │ ├── request-merger.ts │ │ │ └── request-splitter.ts │ │ ├── query.ts │ │ ├── relay-info.ts │ │ ├── relay-metric-handler.ts │ │ ├── request-builder.ts │ │ ├── request-matcher.ts │ │ ├── request-router.ts │ │ ├── request-trim.ts │ │ ├── signer.ts │ │ ├── sync │ │ │ ├── connection.ts │ │ │ ├── diff-sync.ts │ │ │ ├── index.ts │ │ │ ├── json-in-event-sync.ts │ │ │ ├── range-sync.ts │ │ │ └── safe-sync.ts │ │ ├── system-base.ts │ │ ├── system.ts │ │ ├── text.ts │ │ ├── user-state.ts │ │ └── utils.ts │ ├── tests │ │ ├── Impl.test.ts │ │ ├── Query.test.ts │ │ ├── event-ext.test.ts │ │ ├── negentropy.test.ts │ │ ├── node.ts │ │ ├── note-collection.test.ts │ │ ├── outbox-model.test.ts │ │ ├── request-builder.test.ts │ │ ├── request-expander.test.ts │ │ ├── request-matcher.test.ts │ │ ├── request-merger.test.ts │ │ ├── request-splitter.test.ts │ │ ├── setupTests.ts │ │ └── utils.test.ts │ ├── tsconfig.json │ └── typedoc.json ├── wallet │ ├── README.md │ ├── package.json │ ├── src │ │ ├── AlbyWallet.ts │ │ ├── LNDHub.ts │ │ ├── NostrWalletConnect.ts │ │ ├── WebLN.ts │ │ ├── custom.d.ts │ │ ├── index.ts │ │ └── zapper.ts │ ├── tsconfig.json │ └── typedoc.json ├── webrtc-server │ ├── README.md │ ├── index.js │ └── package.json └── worker-relay │ ├── README.md │ ├── example │ └── basic.ts │ ├── package.json │ ├── src │ ├── custom.d.ts │ ├── debug.ts │ ├── forYouFeed.ts │ ├── index.ts │ ├── interface.ts │ ├── memory-relay.ts │ ├── queue.ts │ ├── sqlite │ │ ├── fixers.ts │ │ ├── migrations.ts │ │ ├── sqlite-relay.ts │ │ └── sqlite3.wasm │ ├── types.ts │ └── worker.ts │ ├── tsconfig.json │ └── typedoc.json ├── src-tauri ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── capabilities │ └── migrated.json ├── gen │ └── schemas │ │ ├── acl-manifests.json │ │ ├── capabilities.json │ │ ├── desktop-schema.json │ │ └── linux-schema.json ├── icons │ ├── 128x128.png │ └── 128x128@2x.png ├── src │ └── main.rs └── tauri.conf.json ├── yarn.lock └── zapstore.yaml /.dockerignore: -------------------------------------------------------------------------------- 1 | **/node_modules 2 | **/.pnp.* 3 | **/.yarn/* 4 | !**/.yarn/patches 5 | !**/.yarn/plugins 6 | !**/.yarn/releases 7 | !**/.yarn/sdks 8 | !**/.yarn/versions 9 | **/.idea 10 | **/target -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | 15 | Steps to reproduce the behavior: 16 | 17 | 1. Go to '...' 18 | 2. Click on '....' 19 | 3. Scroll down to '....' 20 | 4. See error 21 | 22 | **Expected behavior** 23 | 24 | A clear and concise description of what you expected to happen. 25 | 26 | **Screenshots** 27 | 28 | If applicable, add screenshots to help explain your problem. 29 | 30 | **Desktop (please complete the following information):** 31 | 32 | - OS: [e.g. iOS] 33 | - Browser: [e.g. chrome, safari] 34 | - Version: [e.g. 22] 35 | 36 | **Smartphone (please complete the following information):** 37 | 38 | - Device: [e.g. iPhone6] 39 | - OS: [e.g. iOS8.1] 40 | - Browser: [e.g. stock browser, safari] 41 | - Version: [e.g. 22] 42 | 43 | **Additional context** 44 | Add any other context about the problem here. 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea 3 | .pnp.* 4 | .yarn/* 5 | !.yarn/patches 6 | !.yarn/plugins 7 | !.yarn/releases 8 | !.yarn/sdks 9 | !.yarn/versions 10 | dist/ 11 | *.tgz 12 | *.log 13 | .DS_Store 14 | .pnp* 15 | docs/ 16 | .wrangler/ -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .yarn/ 2 | build/ 3 | .vscode/ 4 | .github/ 5 | transifex.yml 6 | dist/ 7 | src-tauri/ 8 | target/ -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "arcanis.vscode-zipfs", 4 | "dbaeumer.vscode-eslint", 5 | "esbenp.prettier-vscode" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "**/.git": true, 4 | "**/.svn": true, 5 | "**/.hg": true, 6 | "**/CVS": true, 7 | "**/.DS_Store": true, 8 | "**/Thumbs.db": true, 9 | "**/node_modules": true 10 | }, 11 | "search.exclude": { 12 | "**/.yarn": true, 13 | "**/.pnp.*": true 14 | }, 15 | "typescript.tsdk": ".yarn/sdks/typescript/lib", 16 | "typescript.enablePromptUseWorkspaceTsdk": true, 17 | "eslint.nodePath": ".yarn/sdks", 18 | "prettier.prettierPath": ".yarn/sdks/prettier/index.cjs" 19 | } 20 | -------------------------------------------------------------------------------- /.yarn/sdks/eslint/bin/eslint.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require eslint/bin/eslint.js 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real eslint/bin/eslint.js your application uses 20 | module.exports = absRequire(`eslint/bin/eslint.js`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/eslint/lib/api.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require eslint 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real eslint your application uses 20 | module.exports = absRequire(`eslint`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/eslint/lib/unsupported-api.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require eslint/use-at-your-own-risk 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real eslint/use-at-your-own-risk your application uses 20 | module.exports = absRequire(`eslint/use-at-your-own-risk`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/eslint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eslint", 3 | "version": "8.53.0-sdk", 4 | "main": "./lib/api.js", 5 | "type": "commonjs", 6 | "bin": { 7 | "eslint": "./bin/eslint.js" 8 | }, 9 | "exports": { 10 | "./package.json": "./package.json", 11 | ".": "./lib/api.js", 12 | "./use-at-your-own-risk": "./lib/unsupported-api.js" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /.yarn/sdks/integrations.yml: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by @yarnpkg/sdks. 2 | # Manual changes might be lost! 3 | 4 | integrations: 5 | - vscode 6 | -------------------------------------------------------------------------------- /.yarn/sdks/prettier/bin/prettier.cjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require prettier/bin/prettier.cjs 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real prettier/bin/prettier.cjs your application uses 20 | module.exports = absRequire(`prettier/bin/prettier.cjs`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/prettier/index.cjs: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require prettier 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real prettier your application uses 20 | module.exports = absRequire(`prettier`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/prettier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "prettier", 3 | "version": "3.1.0-sdk", 4 | "main": "./index.cjs", 5 | "type": "commonjs", 6 | "bin": "./bin/prettier.cjs" 7 | } 8 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/bin/tsc: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/bin/tsc 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/bin/tsc your application uses 20 | module.exports = absRequire(`typescript/bin/tsc`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/bin/tsserver: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/bin/tsserver 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/bin/tsserver your application uses 20 | module.exports = absRequire(`typescript/bin/tsserver`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/lib/tsc.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript/lib/tsc.js 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript/lib/tsc.js your application uses 20 | module.exports = absRequire(`typescript/lib/tsc.js`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/lib/typescript.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const {existsSync} = require(`fs`); 4 | const {createRequire} = require(`module`); 5 | const {resolve} = require(`path`); 6 | 7 | const relPnpApiPath = "../../../../.pnp.cjs"; 8 | 9 | const absPnpApiPath = resolve(__dirname, relPnpApiPath); 10 | const absRequire = createRequire(absPnpApiPath); 11 | 12 | if (existsSync(absPnpApiPath)) { 13 | if (!process.versions.pnp) { 14 | // Setup the environment to be able to require typescript 15 | require(absPnpApiPath).setup(); 16 | } 17 | } 18 | 19 | // Defer to the real typescript your application uses 20 | module.exports = absRequire(`typescript`); 21 | -------------------------------------------------------------------------------- /.yarn/sdks/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typescript", 3 | "version": "5.2.2-sdk", 4 | "main": "./lib/typescript.js", 5 | "type": "commonjs", 6 | "bin": { 7 | "tsc": "./bin/tsc", 8 | "tsserver": "./bin/tsserver" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | yarn-path ".yarn/releases/yarn-1.22.19.cjs" 6 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | compressionLevel: mixed 2 | 3 | enableGlobalCache: false 4 | 5 | npmScopes: 6 | here: 7 | npmRegistryServer: "https://repo.platform.here.com/artifactory/api/npm/maps-api-for-javascript/" 8 | 9 | yarnPath: .yarn/releases/yarn-4.1.1.cjs 10 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:current as build 2 | WORKDIR /src 3 | RUN apt update \ 4 | && apt install -y --no-install-recommends git \ 5 | && git clone --single-branch -b main https://git.v0l.io/Kieran/snort \ 6 | && cd snort \ 7 | && yarn --network-timeout 1000000 \ 8 | && yarn build 9 | 10 | FROM nginxinc/nginx-unprivileged:mainline-alpine 11 | COPY docker/nginx.conf /etc/nginx/conf.d/default.conf 12 | COPY --from=build /src/snort/packages/app/build /usr/share/nginx/html 13 | -------------------------------------------------------------------------------- /Dockerfile.prebuilt: -------------------------------------------------------------------------------- 1 | FROM nginxinc/nginx-unprivileged:mainline-alpine 2 | COPY packages/app/build /usr/share/nginx/html 3 | COPY docker/nginx.conf /etc/nginx/conf.d/default.conf -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Kieran (v0l) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | project_id: 568149 2 | preserve_hierarchy: true 3 | files: 4 | - source: packages/app/src/translations/en.json 5 | translation: packages/app/src/translations/%locale_with_underscore%.json 6 | -------------------------------------------------------------------------------- /docker/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 8080 default_server; 3 | server_name _; 4 | root /usr/share/nginx/html; 5 | index index.html; 6 | add_header Content-Security-Policy "default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src youtube.com www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com"; 7 | add_header Cross-Origin-Opener-Policy same-origin; 8 | add_header Cross-Origin-Embedder-Policy require-corp; 9 | 10 | location / { 11 | try_files $uri $uri/ /index.html =404; 12 | } 13 | } -------------------------------------------------------------------------------- /functions/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "lib": ["esnext"], 6 | "types": ["@cloudflare/workers-types"] 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /maintainers.yaml: -------------------------------------------------------------------------------- 1 | maintainers: 2 | - npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk 3 | - npub1v0lxxxxutpvrelsksy8cdhgfux9l6a42hsj2qzquu2zk7vc9qnkszrqj49 4 | relays: 5 | - wss://relay.snort.social/ 6 | - wss://pyramid.fiatjaf.com/ 7 | - wss://nos.lol/ 8 | - ws://skzzn6cimfdv5e2phjc4yr5v7ikbxtn5f7dkwn5c7v47tduzlbosqmqd.onion/ 9 | -------------------------------------------------------------------------------- /nap.yaml: -------------------------------------------------------------------------------- 1 | id: "social.snort.app" 2 | name: "Snort" 3 | description: "" 4 | icon: "https://snort.social/nostrich_256.png" 5 | images: 6 | - "https://snort.social/nostrich_512.png" 7 | repository: "https://github.com/v0l/snort" 8 | license: "MIT" 9 | tags: 10 | - "social" 11 | - "twitter" 12 | -------------------------------------------------------------------------------- /packages/app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | .idea 26 | 27 | dist/ 28 | dev-dist/ 29 | .wrangler/ -------------------------------------------------------------------------------- /packages/app/babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | [ 4 | "formatjs", 5 | { 6 | "idInterpolationPattern": "[sha512:contenthash:base64:6]", 7 | "ast": true 8 | } 9 | ] 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /packages/app/config/README.md: -------------------------------------------------------------------------------- 1 | Choose config with NODE_CONFIG_ENV: `NODE_CONFIG_ENV=iris yarn start` 2 | -------------------------------------------------------------------------------- /packages/app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [require("tailwindcss"), require("autoprefixer")], 3 | }; 4 | -------------------------------------------------------------------------------- /packages/app/public/iris/.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "relation": ["delegate_permission/common.handle_all_urls"], 4 | "target": { 5 | "namespace": "android_app", 6 | "package_name": "to.iris.twa", 7 | "sha256_cert_fingerprints": [ 8 | "63:B5:70:E8:F1:75:7E:D6:EF:81:11:66:F4:9D:47:AB:49:3C:2E:00:B9:67:92:40:89:A5:03:0B:96:B9:40:09" 9 | ] 10 | } 11 | } 12 | ] 13 | -------------------------------------------------------------------------------- /packages/app/public/iris/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src https://youtube.com https://www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com; 3 | /service-worker.js 4 | Cache-Control: max-age=604800, must-revalidate; -------------------------------------------------------------------------------- /packages/app/public/iris/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/iris/favicon.png -------------------------------------------------------------------------------- /packages/app/public/iris/img/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/iris/img/android-chrome-192x192.png -------------------------------------------------------------------------------- /packages/app/public/iris/img/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/iris/img/android-chrome-512x512.png -------------------------------------------------------------------------------- /packages/app/public/iris/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/iris/img/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/app/public/iris/img/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/iris/img/icon128.png -------------------------------------------------------------------------------- /packages/app/public/iris/img/irisconnects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/iris/img/irisconnects.png -------------------------------------------------------------------------------- /packages/app/public/iris/img/maskable_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/iris/img/maskable_icon.png -------------------------------------------------------------------------------- /packages/app/public/iris/img/maskable_icon_x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/iris/img/maskable_icon_x192.png -------------------------------------------------------------------------------- /packages/app/public/iris/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Iris", 3 | "name": "Iris", 4 | "description": "Fast nostr web ui", 5 | "id": "/", 6 | "icons": [ 7 | { 8 | "src": "/img/android-chrome-192x192.png", 9 | "sizes": "192x192", 10 | "type": "image/png" 11 | }, 12 | { 13 | "src": "/img/android-chrome-512x512.png", 14 | "sizes": "512x512", 15 | "type": "image/png", 16 | "purpose": "any" 17 | }, 18 | { 19 | "src": "/img/maskable_icon.png", 20 | "sizes": "640x640", 21 | "type": "image/png", 22 | "purpose": "maskable" 23 | }, 24 | { 25 | "src": "/img/maskable_icon_x192.png", 26 | "sizes": "192x192", 27 | "type": "image/png", 28 | "purpose": "maskable" 29 | } 30 | ], 31 | "start_url": "/", 32 | "display": "standalone", 33 | "theme_color": "#000000", 34 | "background_color": "#000000", 35 | "protocol_handlers": [ 36 | { 37 | "protocol": "web+nostr", 38 | "url": "/%s" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /packages/app/public/iris/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /packages/app/public/nostr/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src https://youtube.com https://www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com; -------------------------------------------------------------------------------- /packages/app/public/nostr/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/nostr/favicon.png -------------------------------------------------------------------------------- /packages/app/public/nostr/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/nostr/img/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/app/public/nostr/nostr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/nostr/nostr.jpg -------------------------------------------------------------------------------- /packages/app/public/phoenix/.well-known/apple-app-site-association: -------------------------------------------------------------------------------- 1 | { 2 | "applinks": { 3 | "details": [ 4 | { 5 | "appIDs": [ 6 | "snort.social.app" 7 | ] 8 | } 9 | ] 10 | }, 11 | "webcredentials": { 12 | "apps": [ 13 | "snort.social.app" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/app/public/phoenix/.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "relation": ["delegate_permission/common.handle_all_urls"], 4 | "target": { 5 | "namespace": "android_app", 6 | "package_name": "social.snort.app", 7 | "sha256_cert_fingerprints": [ 8 | "78:CE:8A:F7:C1:E2:30:12:77:55:BF:0E:86:E4:5C:BA:99:93:A0:D7:D7:42:F8:27:8B:C9:1B:AC:FC:8A:85:05", 9 | "FC:C1:CA:02:C0:81:81:0C:1F:EC:1E:38:CA:38:61:62:6B:6E:90:88:62:DE:4A:66:FC:EC:08:33:B6:94:EE:3C" 10 | ] 11 | } 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /packages/app/public/phoenix/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src https://youtube.com https://www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com; 3 | /service-worker.js 4 | Cache-Control: max-age=604800, must-revalidate; -------------------------------------------------------------------------------- /packages/app/public/phoenix/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/phoenix/favicon.png -------------------------------------------------------------------------------- /packages/app/public/phoenix/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/phoenix/img/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/app/public/phoenix/logo_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/phoenix/logo_256.png -------------------------------------------------------------------------------- /packages/app/public/phoenix/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Phoenix", 3 | "name": "phoenix.social - Nostr interface", 4 | "description": "Fast nostr web ui", 5 | "id": "/", 6 | "icons": [ 7 | { 8 | "src": "phoenix_256.png", 9 | "type": "image/png", 10 | "sizes": "256x256" 11 | } 12 | ], 13 | "start_url": "/", 14 | "display": "standalone", 15 | "theme_color": "#000000", 16 | "background_color": "#000000", 17 | "protocol_handlers": [ 18 | { 19 | "protocol": "web+nostr", 20 | "url": "/%s" 21 | } 22 | ], 23 | "screenshots": [], 24 | "display_override": [ 25 | "fullscreen" 26 | ], 27 | "related_applications": [ 28 | { 29 | "platform": "play", 30 | "url": "https://play.google.com/store/apps/details?id=social.snort.app", 31 | "id": "social.snort.app" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /packages/app/public/phoenix/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | Sitemap: https://api.snort.social/api/v1/sitemap/index.xml -------------------------------------------------------------------------------- /packages/app/public/snort/.well-known/apple-app-site-association: -------------------------------------------------------------------------------- 1 | { 2 | "applinks": { 3 | "details": [ 4 | { 5 | "appIDs": [ 6 | "snort.social.app" 7 | ] 8 | } 9 | ] 10 | }, 11 | "webcredentials": { 12 | "apps": [ 13 | "snort.social.app" 14 | ] 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/app/public/snort/.well-known/assetlinks.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "relation": ["delegate_permission/common.handle_all_urls"], 4 | "target": { 5 | "namespace": "android_app", 6 | "package_name": "social.snort.app", 7 | "sha256_cert_fingerprints": [ 8 | "78:CE:8A:F7:C1:E2:30:12:77:55:BF:0E:86:E4:5C:BA:99:93:A0:D7:D7:42:F8:27:8B:C9:1B:AC:FC:8A:85:05", 9 | "FC:C1:CA:02:C0:81:81:0C:1F:EC:1E:38:CA:38:61:62:6B:6E:90:88:62:DE:4A:66:FC:EC:08:33:B6:94:EE:3C" 10 | ] 11 | } 12 | } 13 | ] 14 | -------------------------------------------------------------------------------- /packages/app/public/snort/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | Content-Security-Policy: default-src 'self'; manifest-src *; child-src 'none'; worker-src 'self'; frame-src https://youtube.com https://www.youtube.com https://platform.twitter.com https://embed.tidal.com https://w.soundcloud.com https://www.mixcloud.com https://open.spotify.com https://player.twitch.tv https://embed.music.apple.com https://embed.wavlake.com https://challenges.cloudflare.com; style-src 'self' 'unsafe-inline'; connect-src *; img-src * data: blob:; font-src 'self'; media-src * blob:; script-src 'self' 'wasm-unsafe-eval' https://platform.twitter.com https://embed.tidal.com https://challenges.cloudflare.com; 3 | /service-worker.js 4 | Cache-Control: max-age=604800, must-revalidate; -------------------------------------------------------------------------------- /packages/app/public/snort/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/snort/favicon.png -------------------------------------------------------------------------------- /packages/app/public/snort/img/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/snort/img/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/app/public/snort/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Snort", 3 | "name": "snort.social - Nostr interface", 4 | "description": "Fast nostr web ui", 5 | "id": "/", 6 | "icons": [ 7 | { 8 | "src": "nostrich_256.png", 9 | "type": "image/png", 10 | "sizes": "256x256" 11 | }, 12 | { 13 | "src": "nostrich_512.png", 14 | "type": "image/png", 15 | "sizes": "512x512" 16 | } 17 | ], 18 | "start_url": "/", 19 | "display": "standalone", 20 | "theme_color": "#000000", 21 | "background_color": "#000000", 22 | "protocol_handlers": [ 23 | { 24 | "protocol": "web+nostr", 25 | "url": "/%s" 26 | } 27 | ], 28 | "screenshots": [], 29 | "display_override": ["fullscreen"], 30 | "related_applications": [ 31 | { 32 | "platform": "play", 33 | "url": "https://play.google.com/store/apps/details?id=social.snort.app", 34 | "id": "social.snort.app" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /packages/app/public/snort/nostrich_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/snort/nostrich_256.png -------------------------------------------------------------------------------- /packages/app/public/snort/nostrich_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/snort/nostrich_512.png -------------------------------------------------------------------------------- /packages/app/public/snort/nostrich_orig.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/v0l/snort/2b48641db176012377e6a43d787e4d9a6be52f3f/packages/app/public/snort/nostrich_orig.jpeg -------------------------------------------------------------------------------- /packages/app/public/snort/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | Sitemap: https://api.snort.social/api/v1/sitemap/index.xml -------------------------------------------------------------------------------- /packages/app/src/Cache/ChatCache.ts: -------------------------------------------------------------------------------- 1 | import { FeedCache } from "@snort/shared"; 2 | import { NostrEvent } from "@snort/system"; 3 | 4 | import { db } from "@/Db"; 5 | 6 | export class ChatCache extends FeedCache { 7 | constructor() { 8 | super("ChatCache", db.chats); 9 | } 10 | 11 | key(of: NostrEvent): string { 12 | return of.id; 13 | } 14 | 15 | override async preload(): Promise { 16 | await super.preload(); 17 | // load all dms to memory 18 | await this.buffer([...this.onTable]); 19 | } 20 | 21 | newest(): number { 22 | let ret = 0; 23 | this.cache.forEach(v => (ret = v.created_at > ret ? v.created_at : ret)); 24 | return ret; 25 | } 26 | 27 | takeSnapshot(): Array { 28 | return [...this.cache.values()]; 29 | } 30 | 31 | async search() { 32 | return >[]; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/app/src/Cache/CommunityLeadersStore.tsx: -------------------------------------------------------------------------------- 1 | import { ExternalStore } from "@snort/shared"; 2 | 3 | class CommunityLeadersStore extends ExternalStore> { 4 | #leaders: Array = []; 5 | 6 | setLeaders(arr: Array) { 7 | this.#leaders = arr; 8 | this.notifyChange(); 9 | } 10 | 11 | takeSnapshot(): string[] { 12 | return [...this.#leaders]; 13 | } 14 | } 15 | 16 | export const LeadersStore = new CommunityLeadersStore(); 17 | -------------------------------------------------------------------------------- /packages/app/src/Cache/RefreshFeedCache.ts: -------------------------------------------------------------------------------- 1 | import { FeedCache } from "@snort/shared"; 2 | import { EventPublisher, RequestBuilder, TaggedNostrEvent } from "@snort/system"; 3 | 4 | import { LoginSession } from "@/Utils/Login"; 5 | 6 | export type TWithCreated = (T | Readonly) & { created_at: number }; 7 | 8 | export abstract class RefreshFeedCache extends FeedCache> { 9 | abstract buildSub(session: LoginSession, rb: RequestBuilder): void; 10 | abstract onEvent(evs: Readonly>, pubKey: string, pub?: EventPublisher): void; 11 | 12 | /** 13 | * Get latest event 14 | */ 15 | protected newest(filter?: (e: TWithCreated) => boolean) { 16 | let ret = 0; 17 | this.cache.forEach(v => { 18 | if (!filter || filter(v)) { 19 | ret = v.created_at > ret ? v.created_at : ret; 20 | } 21 | }); 22 | return ret; 23 | } 24 | 25 | override async preload(): Promise { 26 | await super.preload(); 27 | await this.buffer([...this.onTable]); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/app/src/Cache/TextCache.tsx: -------------------------------------------------------------------------------- 1 | import { ParsedFragment } from "@snort/system"; 2 | import { LRUCache } from "typescript-lru-cache"; 3 | 4 | export const TextCache = new LRUCache>({ 5 | maxSize: 1000, 6 | }); 7 | -------------------------------------------------------------------------------- /packages/app/src/Components/Button/AsyncButton.css: -------------------------------------------------------------------------------- 1 | .spinner-wrapper { 2 | position: absolute; 3 | width: 100%; 4 | height: 100%; 5 | top: 0; 6 | left: 0; 7 | } 8 | 9 | .spinner-button > span { 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | gap: 8px; 14 | } 15 | 16 | .light .spinner-button { 17 | border: 1px solid var(--border-color); 18 | color: var(--font-secondary); 19 | box-shadow: rgba(0, 0, 0, 0.08) 0 1px 1px; 20 | } 21 | 22 | .light .spinner-button:not(.primary):hover { 23 | box-shadow: rgba(0, 0, 0, 0.2) 0 1px 3px; 24 | } 25 | 26 | .light .spinner-button:not(.primary) > span { 27 | color: black; 28 | } 29 | -------------------------------------------------------------------------------- /packages/app/src/Components/Button/AsyncIcon.tsx: -------------------------------------------------------------------------------- 1 | import Icon from "@/Components/Icons/Icon"; 2 | import Spinner from "@/Components/Icons/Spinner"; 3 | import useLoading from "@/Hooks/useLoading"; 4 | 5 | export type AsyncIconProps = React.HTMLProps & { 6 | iconName: string; 7 | iconSize?: number; 8 | onClick?: (e: React.MouseEvent) => Promise | void; 9 | }; 10 | 11 | export function AsyncIcon(props: AsyncIconProps) { 12 | const { loading, handle } = useLoading(props.onClick, props.disabled); 13 | 14 | const mergedProps = { ...props } as Record; 15 | delete mergedProps["iconName"]; 16 | delete mergedProps["iconSize"]; 17 | delete mergedProps["loading"]; 18 | return ( 19 |
20 | {loading ? : } 21 | {props.children} 22 |
23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /packages/app/src/Components/Button/BackButton.css: -------------------------------------------------------------------------------- 1 | .back-button { 2 | background: none; 3 | padding: 0; 4 | color: var(--highlight); 5 | font-weight: 400; 6 | font-size: var(--font-size); 7 | display: flex; 8 | align-items: center; 9 | border: none !important; 10 | box-shadow: none !important; 11 | } 12 | 13 | .back-button svg { 14 | margin-right: 0.5em; 15 | } 16 | 17 | .back-button:hover:hover, 18 | .light .back-button:hover { 19 | text-decoration: underline; 20 | box-shadow: none !important; 21 | background: none !important; 22 | } 23 | -------------------------------------------------------------------------------- /packages/app/src/Components/Button/BackButton.tsx: -------------------------------------------------------------------------------- 1 | import "./BackButton.css"; 2 | 3 | import { useIntl } from "react-intl"; 4 | 5 | import Icon from "@/Components/Icons/Icon"; 6 | 7 | import messages from "../messages"; 8 | 9 | interface BackButtonProps { 10 | text?: string; 11 | onClick?(): void; 12 | } 13 | 14 | const BackButton = ({ text, onClick }: BackButtonProps) => { 15 | const { formatMessage } = useIntl(); 16 | const onClickHandler = () => { 17 | if (onClick) { 18 | onClick(); 19 | } 20 | }; 21 | 22 | return ( 23 | 27 | ); 28 | }; 29 | 30 | export default BackButton; 31 | -------------------------------------------------------------------------------- /packages/app/src/Components/Button/CloseButton.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | 3 | import Icon from "@/Components/Icons/Icon"; 4 | 5 | export default function CloseButton({ onClick, className }: { onClick?: () => void; className?: string }) { 6 | return ( 7 |
13 | 14 |
15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/app/src/Components/Button/IconButton.tsx: -------------------------------------------------------------------------------- 1 | import classNames from "classnames"; 2 | import type { ReactNode } from "react"; 3 | 4 | import Icon, { IconProps } from "@/Components/Icons/Icon"; 5 | 6 | interface IconButtonProps { 7 | onClick?: () => void; 8 | icon: IconProps; 9 | className?: string; 10 | children?: ReactNode; 11 | } 12 | 13 | const IconButton = ({ onClick, icon, children, className }: IconButtonProps) => { 14 | return ( 15 | 24 | ); 25 | }; 26 | 27 | export default IconButton; 28 | -------------------------------------------------------------------------------- /packages/app/src/Components/Button/LogoutButton.tsx: -------------------------------------------------------------------------------- 1 | import { FormattedMessage } from "react-intl"; 2 | import { useNavigate } from "react-router-dom"; 3 | 4 | import useLogin from "@/Hooks/useLogin"; 5 | import { logout } from "@/Utils/Login"; 6 | 7 | import messages from "../messages"; 8 | 9 | export default function LogoutButton() { 10 | const navigate = useNavigate(); 11 | const login = useLogin(s => ({ publicKey: s.publicKey, id: s.id })); 12 | 13 | if (!login.publicKey) return; 14 | return ( 15 | 24 | ); 25 | } 26 | -------------------------------------------------------------------------------- /packages/app/src/Components/Button/NavLink.tsx: -------------------------------------------------------------------------------- 1 | import { NavLink as RouterNavLink, NavLinkProps, useLocation } from "react-router-dom"; 2 | 3 | export default function NavLink(props: NavLinkProps) { 4 | const { to, onClick, ...rest } = props; 5 | const location = useLocation(); 6 | 7 | const isActive = location.pathname === to.toString(); 8 | 9 | const handleClick = event => { 10 | if (onClick) { 11 | onClick(event); 12 | } 13 | 14 | if (isActive) { 15 | window.scrollTo({ top: 0, behavior: "instant" }); 16 | } 17 | }; 18 | 19 | return ; 20 | } 21 | -------------------------------------------------------------------------------- /packages/app/src/Components/Copy/Copy.css: -------------------------------------------------------------------------------- 1 | .copy .copy-body { 2 | font-size: var(--font-size-small); 3 | color: var(--font-color); 4 | } 5 | -------------------------------------------------------------------------------- /packages/app/src/Components/Embed/AppleMusicEmbed.tsx: -------------------------------------------------------------------------------- 1 | const AppleMusicEmbed = ({ link }: { link: string }) => { 2 | const convertedUrl = link.replace("music.apple.com", "embed.music.apple.com"); 3 | const isSongLink = /\?i=\d+$/.test(convertedUrl); 4 | 5 | return ( 6 | 3 | ); 4 | 5 | export default NostrNestsEmbed; 6 | -------------------------------------------------------------------------------- /packages/app/src/Components/Embed/SoundCloudEmded.tsx: -------------------------------------------------------------------------------- 1 | const SoundCloudEmbed = ({ link }: { link: string }) => { 2 | return ( 3 |