├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md ├── SECURITY.md ├── actions │ └── docker │ │ └── action.yml └── workflows │ ├── cd.yml │ └── ci.yml ├── .gitignore ├── .husky └── pre-commit ├── .npmrc ├── .nvmrc ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── apps ├── api │ ├── .env.example │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── env.d.ts │ ├── package.json │ ├── src │ │ ├── helpers │ │ │ ├── did │ │ │ │ └── resolverAbi.ts │ │ │ ├── oembed │ │ │ │ ├── constructIframe.ts │ │ │ │ ├── extractOgTags.ts │ │ │ │ └── regex.ts │ │ │ └── tower │ │ │ │ └── checkEventExistence.ts │ │ ├── index.ts │ │ ├── middlewares.ts │ │ └── routes │ │ │ ├── allowed-tokens.ts │ │ │ ├── avatar.ts │ │ │ ├── curated.ts │ │ │ ├── did.ts │ │ │ ├── gateway.ts │ │ │ ├── metadata.ts │ │ │ ├── oembed.ts │ │ │ ├── rates.ts │ │ │ ├── stats.ts │ │ │ ├── sts.ts │ │ │ ├── tail.ts │ │ │ ├── toggles.ts │ │ │ ├── tower.ts │ │ │ ├── trails.ts │ │ │ └── verified.ts │ └── tsconfig.json ├── contracts │ ├── .env.example │ ├── .gitignore │ ├── README.md │ ├── contracts │ │ └── TapePermissionlessCreator.sol │ ├── env.d.ts │ ├── hardhat.config.ts │ ├── package.json │ ├── scripts │ │ └── deploy.ts │ └── tsconfig.json ├── cron │ ├── .env.example │ ├── Dockerfile │ ├── README.md │ ├── env.d.ts │ ├── package.json │ ├── src │ │ ├── index.ts │ │ └── services │ │ │ ├── backup │ │ │ ├── events.ts │ │ │ └── trails.ts │ │ │ ├── cleanup.ts │ │ │ ├── curate.ts │ │ │ ├── stats.ts │ │ │ ├── tower.ts │ │ │ ├── trails.ts │ │ │ └── wake.ts │ └── tsconfig.json ├── embed │ ├── next.config.js │ ├── package.json │ ├── postcss.config.mjs │ ├── src │ │ ├── app │ │ │ ├── [pubId] │ │ │ │ └── page.tsx │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ └── layout.tsx │ │ ├── components │ │ │ ├── Custom404.tsx │ │ │ ├── Publication.tsx │ │ │ ├── TopOverlay.tsx │ │ │ └── Video.tsx │ │ └── tower.ts │ ├── tailwind.config.ts │ └── tsconfig.json ├── mobile │ ├── .gitignore │ ├── app.json │ ├── app │ │ ├── (protected) │ │ │ ├── (feed) │ │ │ │ ├── _layout.tsx │ │ │ │ └── index.tsx │ │ │ ├── +not-found.tsx │ │ │ ├── _layout.tsx │ │ │ ├── create.tsx │ │ │ └── watch │ │ │ │ └── [id].tsx │ │ └── _layout.tsx │ ├── assets │ │ ├── fonts │ │ │ ├── sans-b.ttf │ │ │ ├── sans-m.ttf │ │ │ ├── sans-sb.ttf │ │ │ ├── sans.ttf │ │ │ └── serif.ttf │ │ └── images │ │ │ ├── adaptive-icon.png │ │ │ ├── auth-el.png │ │ │ ├── icon.png │ │ │ └── splash.png │ ├── babel.config.js │ ├── components │ │ ├── auth │ │ │ ├── background.tsx │ │ │ ├── instructions.tsx │ │ │ ├── profile.tsx │ │ │ ├── queries.ts │ │ │ ├── scan.tsx │ │ │ └── screen.tsx │ │ ├── create │ │ │ ├── controls.tsx │ │ │ └── screen.tsx │ │ ├── feed │ │ │ ├── actions.tsx │ │ │ ├── header.tsx │ │ │ ├── item.tsx │ │ │ ├── list.tsx │ │ │ ├── media.tsx │ │ │ ├── queries.ts │ │ │ └── video.tsx │ │ ├── providers │ │ │ ├── net-info.tsx │ │ │ └── react-query.tsx │ │ ├── shared │ │ │ ├── edge-gradient.tsx │ │ │ └── external-link.tsx │ │ ├── ui │ │ │ ├── animated-button.tsx │ │ │ └── render-markdown.tsx │ │ └── watch │ │ │ ├── publication.tsx │ │ │ └── queries.ts │ ├── eas.json │ ├── helpers │ │ ├── colors.ts │ │ ├── date-time.ts │ │ ├── execute.ts │ │ ├── haptics.ts │ │ ├── normalize-font.ts │ │ ├── refresh.ts │ │ └── style-atoms.ts │ ├── metro.config.js │ ├── package.json │ ├── store │ │ ├── auth.tsx │ │ ├── device.tsx │ │ └── profile.tsx │ └── tsconfig.json ├── og │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── next.config.mjs │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── favicon.ico │ │ │ ├── layout.tsx │ │ │ ├── u │ │ │ │ └── [handle] │ │ │ │ │ └── page.tsx │ │ │ └── watch │ │ │ │ └── [id] │ │ │ │ └── page.tsx │ │ ├── common.ts │ │ └── other-metadata.ts │ └── tsconfig.json ├── web-vite │ ├── index.html │ ├── package.json │ ├── public │ │ ├── fonts │ │ │ ├── mono.woff2 │ │ │ ├── sans.woff2 │ │ │ └── serif.woff2 │ │ └── images │ │ │ └── hero.webp │ ├── src │ │ ├── components │ │ │ ├── create │ │ │ │ ├── advanced.tsx │ │ │ │ ├── category.tsx │ │ │ │ ├── details.tsx │ │ │ │ ├── drag-and-drop.ts │ │ │ │ ├── drop-zone.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── preview.tsx │ │ │ │ └── suggested-thumbnails.tsx │ │ │ ├── embed │ │ │ │ ├── page.tsx │ │ │ │ ├── post.tsx │ │ │ │ └── top.tsx │ │ │ ├── explore │ │ │ │ └── page.tsx │ │ │ ├── feed │ │ │ │ ├── byte.tsx │ │ │ │ └── page.tsx │ │ │ ├── following │ │ │ │ └── page.tsx │ │ │ ├── home │ │ │ │ ├── about.tsx │ │ │ │ ├── bytes.tsx │ │ │ │ ├── curated.tsx │ │ │ │ ├── feed.tsx │ │ │ │ ├── hero.tsx │ │ │ │ ├── invite.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── trending.tsx │ │ │ │ └── users.tsx │ │ │ ├── mod │ │ │ │ ├── page.tsx │ │ │ │ └── posts.tsx │ │ │ ├── open │ │ │ │ ├── middle-grid-item.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── queries.ts │ │ │ ├── privacy │ │ │ │ └── page.tsx │ │ │ ├── settings │ │ │ │ ├── me.tsx │ │ │ │ └── sidebar.tsx │ │ │ ├── shared │ │ │ │ ├── auth │ │ │ │ │ ├── authenticate.tsx │ │ │ │ │ ├── connect-wallet.tsx │ │ │ │ │ ├── create-account.tsx │ │ │ │ │ ├── frame.tsx │ │ │ │ │ └── links.tsx │ │ │ │ ├── floating-nav.tsx │ │ │ │ ├── footer │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── links.tsx │ │ │ │ │ ├── network.tsx │ │ │ │ │ ├── socials.tsx │ │ │ │ │ └── status.tsx │ │ │ │ ├── header │ │ │ │ │ ├── accounts.tsx │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── left.tsx │ │ │ │ │ ├── logo.tsx │ │ │ │ │ ├── notifications │ │ │ │ │ │ ├── followed.tsx │ │ │ │ │ │ └── index.tsx │ │ │ │ │ ├── right.tsx │ │ │ │ │ └── user-menu.tsx │ │ │ │ ├── horizontal-view.tsx │ │ │ │ ├── search │ │ │ │ │ ├── index.tsx │ │ │ │ │ ├── input.tsx │ │ │ │ │ └── results.tsx │ │ │ │ ├── shimmers │ │ │ │ │ └── button.tsx │ │ │ │ ├── tape-svg.tsx │ │ │ │ ├── video-card.tsx │ │ │ │ └── virtualized.tsx │ │ │ ├── sign-in │ │ │ │ └── page.tsx │ │ │ ├── sign-up │ │ │ │ └── page.tsx │ │ │ ├── terms │ │ │ │ └── page.tsx │ │ │ ├── u │ │ │ │ ├── actions.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── profile.tsx │ │ │ ├── watch │ │ │ │ ├── account.tsx │ │ │ │ ├── actions.tsx │ │ │ │ ├── comments.tsx │ │ │ │ ├── content.tsx │ │ │ │ ├── creator.tsx │ │ │ │ ├── page.tsx │ │ │ │ ├── stats-and-actions.tsx │ │ │ │ └── stats.tsx │ │ │ └── winder │ │ │ │ ├── content.tsx │ │ │ │ ├── header.tsx │ │ │ │ ├── intro-animation.tsx │ │ │ │ ├── intro.tsx │ │ │ │ ├── map.tsx │ │ │ │ ├── page.tsx │ │ │ │ └── sidebar.tsx │ │ ├── helpers │ │ │ ├── category.ts │ │ │ ├── date-time.ts │ │ │ ├── execute.ts │ │ │ ├── format-number.ts │ │ │ ├── generate-thumbnails.ts │ │ │ ├── metadata.ts │ │ │ ├── parse-jwt.ts │ │ │ ├── refresh-tokens.ts │ │ │ ├── sanitize-storage-url.ts │ │ │ └── upload.tsx │ │ ├── main.css │ │ ├── main.tsx │ │ ├── providers │ │ │ ├── animations.tsx │ │ │ ├── dev-only.tsx │ │ │ ├── index.tsx │ │ │ ├── log.tsx │ │ │ ├── react-query.tsx │ │ │ ├── sw-provider.tsx │ │ │ └── wallet.tsx │ │ ├── queries │ │ │ ├── account.tsx │ │ │ ├── auth.ts │ │ │ ├── comment.tsx │ │ │ ├── notification.tsx │ │ │ ├── post.tsx │ │ │ └── search.tsx │ │ ├── routeTree.gen.ts │ │ ├── routes │ │ │ ├── __root.tsx │ │ │ ├── _auth-hoc.tsx │ │ │ ├── _auth-hoc │ │ │ │ ├── sign-in.tsx │ │ │ │ └── sign-up.tsx │ │ │ ├── _layout-hoc.tsx │ │ │ ├── _layout-hoc │ │ │ │ ├── _home-hoc.tsx │ │ │ │ ├── _home-hoc │ │ │ │ │ ├── explore.tsx │ │ │ │ │ ├── following.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── _settings-hoc.tsx │ │ │ │ ├── _settings-hoc │ │ │ │ │ └── settings │ │ │ │ │ │ └── me.tsx │ │ │ │ ├── create │ │ │ │ │ └── index.tsx │ │ │ │ ├── feed │ │ │ │ │ └── index.tsx │ │ │ │ ├── mod │ │ │ │ │ └── index.tsx │ │ │ │ ├── privacy │ │ │ │ │ └── index.tsx │ │ │ │ ├── terms │ │ │ │ │ └── index.tsx │ │ │ │ ├── u │ │ │ │ │ └── $handle.tsx │ │ │ │ └── watch │ │ │ │ │ └── $postId.tsx │ │ │ ├── embed │ │ │ │ └── $postId.tsx │ │ │ ├── open │ │ │ │ └── index.tsx │ │ │ └── winder │ │ │ │ └── index.tsx │ │ ├── store │ │ │ ├── cookie.tsx │ │ │ ├── post.tsx │ │ │ └── scroll.tsx │ │ ├── vite-env.d.ts │ │ └── worker │ │ │ ├── build.mjs │ │ │ ├── cache.ts │ │ │ ├── events.ts │ │ │ └── sw.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.node.json │ ├── tsr.config.json │ └── vite.config.ts └── web │ ├── next.config.js │ ├── package.json │ ├── postcss.config.js │ ├── public │ ├── favicon.ico │ ├── manifest.json │ ├── robots.txt │ └── sitemap.xml │ ├── src │ ├── components │ │ ├── Bytes │ │ │ ├── BottomOverlay.tsx │ │ │ ├── ByteActions.tsx │ │ │ ├── ByteComments.tsx │ │ │ ├── ByteVideo.tsx │ │ │ ├── SliderPlugin.tsx │ │ │ ├── TopOverlay.tsx │ │ │ └── index.tsx │ │ ├── Common │ │ │ ├── Alert.tsx │ │ │ ├── Badge.tsx │ │ │ ├── CategoryFilters.tsx │ │ │ ├── CollectorsList.tsx │ │ │ ├── EmbedMedia.tsx │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── FollowActions.tsx │ │ │ ├── FullPageLoader.tsx │ │ │ ├── HorizontalScroller.tsx │ │ │ ├── HoverableProfile.tsx │ │ │ ├── InterweaveContent.tsx │ │ │ ├── Layout.tsx │ │ │ ├── Links │ │ │ │ ├── AddressExplorerLink.tsx │ │ │ │ ├── ArweaveExplorerLink.tsx │ │ │ │ ├── HashExplorerLink.tsx │ │ │ │ ├── IPFSLink.tsx │ │ │ │ └── TokenExplorerLink.tsx │ │ │ ├── Logo.tsx │ │ │ ├── MetaTags.tsx │ │ │ ├── MirrorPublication.tsx │ │ │ ├── MirroredList.tsx │ │ │ ├── MobileBottomNav.tsx │ │ │ ├── Navbar.tsx │ │ │ ├── ProfileSuspended.tsx │ │ │ ├── Providers │ │ │ │ ├── CuratedProfilesProvider.tsx │ │ │ │ ├── ServiceWorkerProvider.tsx │ │ │ │ ├── SubscriptionProvider.tsx │ │ │ │ ├── ThemeProvider.tsx │ │ │ │ ├── TogglesProvider.tsx │ │ │ │ ├── Web3Provider.tsx │ │ │ │ └── index.tsx │ │ │ ├── Publication │ │ │ │ ├── PublicationActions.tsx │ │ │ │ ├── PublicationComments.tsx │ │ │ │ ├── PublicationOptions.tsx │ │ │ │ └── PublicationReaction.tsx │ │ │ ├── Search │ │ │ │ ├── GlobalSearch.tsx │ │ │ │ ├── Profiles.tsx │ │ │ │ └── Publications.tsx │ │ │ ├── TapeMenu.tsx │ │ │ ├── UserMenu.tsx │ │ │ ├── VideoCard │ │ │ │ ├── QueuedVideo.tsx │ │ │ │ ├── Share.tsx │ │ │ │ ├── ThumbnailImage.tsx │ │ │ │ ├── ThumbnailOverlays.tsx │ │ │ │ └── index.tsx │ │ │ └── matchers │ │ │ │ ├── EmailMatcher.tsx │ │ │ │ ├── HashtagMatcher.tsx │ │ │ │ ├── MentionMatcher.tsx │ │ │ │ ├── TimeMatcher.tsx │ │ │ │ ├── UrlMatcher.tsx │ │ │ │ └── utils.ts │ │ ├── Create │ │ │ ├── ChooseThumbnail.tsx │ │ │ ├── CollectModule │ │ │ │ ├── ChargeQuestion.tsx │ │ │ │ ├── CollectDuration.tsx │ │ │ │ ├── EditionSize.tsx │ │ │ │ ├── FeeCollectForm.tsx │ │ │ │ ├── PermissionQuestion.tsx │ │ │ │ ├── Splits.tsx │ │ │ │ └── index.tsx │ │ │ ├── Details.tsx │ │ │ ├── DropZone.tsx │ │ │ ├── IrysInfo.tsx │ │ │ ├── MediaCategory.tsx │ │ │ ├── MediaLicense.tsx │ │ │ ├── OpenAction │ │ │ │ └── index.tsx │ │ │ ├── ReferenceModule │ │ │ │ └── index.tsx │ │ │ ├── SelectedMedia.tsx │ │ │ ├── UploadMethod.tsx │ │ │ ├── filereader.d.ts │ │ │ └── index.tsx │ │ ├── Explore │ │ │ ├── Category │ │ │ │ └── index.tsx │ │ │ ├── Feed.tsx │ │ │ ├── Hashtag │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ ├── Feed │ │ │ └── index.tsx │ │ ├── Home │ │ │ ├── Feed.tsx │ │ │ ├── GitcoinAlert.tsx │ │ │ ├── LatestBytes.tsx │ │ │ ├── LensManagerAlert.tsx │ │ │ ├── Timeline.tsx │ │ │ ├── TopSection.tsx │ │ │ ├── WelcomeAlert.tsx │ │ │ ├── ZorbAlert.tsx │ │ │ └── index.tsx │ │ ├── Login │ │ │ ├── Authenticate.tsx │ │ │ ├── BackgroundComets.tsx │ │ │ ├── CardBorders.tsx │ │ │ ├── Connectors.tsx │ │ │ └── Signup.tsx │ │ ├── Mod │ │ │ ├── Recents.tsx │ │ │ ├── Signup.tsx │ │ │ └── index.tsx │ │ ├── Notifications │ │ │ ├── Acted.tsx │ │ │ ├── Commented.tsx │ │ │ ├── Filter.tsx │ │ │ ├── Followed.tsx │ │ │ ├── Mentioned.tsx │ │ │ ├── Mirrored.tsx │ │ │ ├── Quoted.tsx │ │ │ ├── Reactions.tsx │ │ │ └── index.tsx │ │ ├── Profile │ │ │ ├── BasicInfo │ │ │ │ ├── Follow.tsx │ │ │ │ ├── Stats │ │ │ │ │ ├── Followers.tsx │ │ │ │ │ ├── Following.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── SuperFollow.tsx │ │ │ │ ├── UnFollow.tsx │ │ │ │ └── index.tsx │ │ │ ├── Bookmarks.tsx │ │ │ ├── Cover.tsx │ │ │ ├── CoverLinks.tsx │ │ │ ├── Mutual │ │ │ │ ├── Bubbles.tsx │ │ │ │ └── MutualFollowers.tsx │ │ │ ├── Tabs │ │ │ │ ├── PinnedVideo.tsx │ │ │ │ ├── ProfileBytes.tsx │ │ │ │ ├── ProfileVideos.tsx │ │ │ │ └── index.tsx │ │ │ └── index.tsx │ │ ├── Report │ │ │ ├── Profile.tsx │ │ │ └── Publication.tsx │ │ ├── Settings │ │ │ ├── Allowance │ │ │ │ ├── Modules │ │ │ │ │ └── index.tsx │ │ │ │ └── index.tsx │ │ │ ├── BasicInfo.tsx │ │ │ ├── Blocked │ │ │ │ ├── List.tsx │ │ │ │ └── index.tsx │ │ │ ├── DangerZone │ │ │ │ ├── Delete.tsx │ │ │ │ ├── Guardian.tsx │ │ │ │ └── index.tsx │ │ │ ├── Follow │ │ │ │ ├── FeeFollow.tsx │ │ │ │ ├── RevertFollow.tsx │ │ │ │ └── index.tsx │ │ │ ├── Handles │ │ │ │ ├── List.tsx │ │ │ │ └── index.tsx │ │ │ ├── Manager │ │ │ │ ├── LensManager │ │ │ │ │ ├── ToggleLensManager.tsx │ │ │ │ │ └── index.tsx │ │ │ │ ├── Managers.tsx │ │ │ │ ├── Managing.tsx │ │ │ │ └── index.tsx │ │ │ ├── ProfileInterests │ │ │ │ ├── Topics.tsx │ │ │ │ └── index.tsx │ │ │ ├── Sessions │ │ │ │ ├── List.tsx │ │ │ │ └── index.tsx │ │ │ ├── SettingsSidebar.tsx │ │ │ └── index.tsx │ │ ├── Shimmers │ │ │ ├── BubblesShimmer.tsx │ │ │ ├── ButtonShimmer.tsx │ │ │ ├── CategoriesShimmer.tsx │ │ │ ├── CategoryItemShimmer.tsx │ │ │ ├── ChannelCardShimmer.tsx │ │ │ ├── CommentItemShimmer.tsx │ │ │ ├── CommentsShimmer.tsx │ │ │ ├── LatestBytesShimmer.tsx │ │ │ ├── NotificationsShimmer.tsx │ │ │ ├── OpenActionsShimmer.tsx │ │ │ ├── PinnedVideoShimmer.tsx │ │ │ ├── ProfilePageShimmer.tsx │ │ │ ├── SettingsShimmer.tsx │ │ │ ├── SquareButtonShimmer.tsx │ │ │ ├── SuggestedShimmer.tsx │ │ │ ├── ThumbnailsShimmer.tsx │ │ │ ├── TimelineShimmer.tsx │ │ │ ├── VideoCardShimmer.tsx │ │ │ └── WatchShimmer.tsx │ │ ├── Thanks │ │ │ └── index.tsx │ │ ├── UIElements │ │ │ ├── Confirm.tsx │ │ │ ├── CountDown.tsx │ │ │ ├── EmojiPicker.tsx │ │ │ ├── InputMentions.tsx │ │ │ ├── NoDataFound.tsx │ │ │ ├── ProfileSuggestion.tsx │ │ │ └── SignalWaveGraphic.tsx │ │ └── Watch │ │ │ ├── Comments │ │ │ ├── AudioComment.tsx │ │ │ ├── CommentMedia.tsx │ │ │ ├── CommentOptions.tsx │ │ │ ├── CommentReplies.tsx │ │ │ ├── CommentsFilter.tsx │ │ │ ├── NewComment.tsx │ │ │ ├── NonRelevantComments.tsx │ │ │ ├── QueuedComment.tsx │ │ │ ├── RenderComment.tsx │ │ │ └── VideoComment.tsx │ │ │ ├── OpenActions │ │ │ ├── BalanceAlert.tsx │ │ │ ├── Collect │ │ │ │ └── index.tsx │ │ │ ├── PermissionAlert.tsx │ │ │ ├── Unknown │ │ │ │ ├── Tip │ │ │ │ │ └── index.tsx │ │ │ │ └── index.tsx │ │ │ ├── index.tsx │ │ │ └── verified-contracts.tsx │ │ │ ├── SuggestedVideoCard.tsx │ │ │ ├── SuggestedVideos.tsx │ │ │ ├── TipForm.tsx │ │ │ ├── Video.tsx │ │ │ ├── VideoMeta.tsx │ │ │ └── index.tsx │ ├── hooks │ │ ├── useDid.ts │ │ ├── useHandleWrongNetwork.ts │ │ ├── usePendingTxn.ts │ │ └── useSw.ts │ ├── lib │ │ ├── authLink.ts │ │ ├── createIdbStorage.ts │ │ ├── formatTime.ts │ │ ├── getCollectModuleInput.ts │ │ ├── getCollectModuleOutput.ts │ │ ├── getCurrentSession.ts │ │ └── store │ │ │ ├── auth.ts │ │ │ ├── comment.ts │ │ │ ├── idb │ │ │ ├── collect.ts │ │ │ ├── curated.ts │ │ │ ├── profile.ts │ │ │ ├── toggles.ts │ │ │ ├── tokens.ts │ │ │ └── verified.ts │ │ │ ├── index.ts │ │ │ ├── nonce.ts │ │ │ ├── notification.ts │ │ │ └── persist.ts │ ├── pages │ │ ├── 404.tsx │ │ ├── 500.tsx │ │ ├── _app.tsx │ │ ├── _document.tsx │ │ ├── bookmarks.tsx │ │ ├── bytes │ │ │ ├── [id].tsx │ │ │ └── index.tsx │ │ ├── create.tsx │ │ ├── explore │ │ │ ├── [category].tsx │ │ │ ├── hashtag │ │ │ │ └── [hashtag].tsx │ │ │ └── index.tsx │ │ ├── feed.tsx │ │ ├── index.tsx │ │ ├── login.tsx │ │ ├── mod.tsx │ │ ├── notifications.tsx │ │ ├── privacy.tsx │ │ ├── profile │ │ │ └── [id].tsx │ │ ├── settings │ │ │ ├── allowance.tsx │ │ │ ├── blocked.tsx │ │ │ ├── danger.tsx │ │ │ ├── follow.tsx │ │ │ ├── handles.tsx │ │ │ ├── index.tsx │ │ │ ├── interests.tsx │ │ │ ├── manager.tsx │ │ │ └── sessions.tsx │ │ ├── terms.tsx │ │ ├── thanks.tsx │ │ ├── u │ │ │ └── [[...handle]].tsx │ │ └── watch │ │ │ └── [id].tsx │ └── styles │ │ └── index.css │ ├── tailwind.config.js │ └── tsconfig.json ├── biome.json ├── package.json ├── packages ├── abis │ ├── index.ts │ ├── package.json │ ├── src │ │ ├── LensHubProxy.ts │ │ ├── LensPermissionlessCreator.ts │ │ └── TapeSignupProxy.ts │ └── tsconfig.json ├── browser │ ├── font.ts │ ├── font │ │ ├── Satoshi-Bold.woff2 │ │ ├── Satoshi-Medium.woff2 │ │ └── Satoshi-Regular.woff2 │ ├── functions │ │ ├── fingerprint.ts │ │ ├── generateVideoThumbnails.ts │ │ ├── getFileFromDataURL.ts │ │ ├── getToastOptions.ts │ │ ├── getUserLocale.ts │ │ ├── livepeer.ts │ │ └── tw.ts │ ├── hooks │ │ ├── uploadToIPFS.ts │ │ ├── useAverageColor.ts │ │ ├── useCopyToClipboard.ts │ │ ├── useDebounce.ts │ │ ├── useDragAndDrop.ts │ │ ├── useHorizontalScroll.ts │ │ ├── useIsMounted.ts │ │ └── useOutsideClick.ts │ ├── index.ts │ ├── package.json │ ├── tsconfig.json │ └── worker │ │ ├── build.mjs │ │ └── sw.ts ├── constants │ ├── auth-routes.ts │ ├── categories.ts │ ├── endpoints.ts │ ├── feature-flags.ts │ ├── general.ts │ ├── index.ts │ ├── misused.ts │ ├── package.json │ ├── regex.ts │ ├── suspended.ts │ └── tsconfig.json ├── generic │ ├── events.ts │ ├── functions │ │ ├── canUploadedToIpfs.ts │ │ ├── checkIsBytesVideo.ts │ │ ├── checkLensManagerPermissions.ts │ │ ├── formatBytes.ts │ │ ├── formatMB.ts │ │ ├── formatNumber.ts │ │ ├── get-attachments-data.ts │ │ ├── get-profile-cover.ts │ │ ├── get-profile-picture.ts │ │ ├── get-profile.ts │ │ ├── get-publication-data.ts │ │ ├── get-sharable-link.ts │ │ ├── get-thumbnail-url.ts │ │ ├── getCategoryName.ts │ │ ├── getFromAttributes.ts │ │ ├── getIsFeatureEnabled.ts │ │ ├── getIsProfileOwner.ts │ │ ├── getIsSensitiveContent.ts │ │ ├── getIsSuspendedProfile.ts │ │ ├── getLennyPicture.ts │ │ ├── getMetadataCid.ts │ │ ├── getPublication.ts │ │ ├── getPublicationMediaUrl.ts │ │ ├── getShouldUploadVideo.ts │ │ ├── getSignature.ts │ │ ├── getUploadedMediaType.ts │ │ ├── image-cdn.ts │ │ ├── isOpenActionAllowed.ts │ │ ├── isWatchable.ts │ │ ├── omitKey.ts │ │ ├── parse-jwt.ts │ │ ├── resolveDid.ts │ │ ├── sanitizeDStorageUrl.ts │ │ ├── sanitizeProfileInterests.ts │ │ ├── shortenAddress.ts │ │ ├── splitNumber.ts │ │ ├── trim-new-lines.ts │ │ ├── trimify.ts │ │ ├── truncate.ts │ │ └── uploadToAr.ts │ ├── index.ts │ ├── logger.ts │ ├── package.json │ └── tsconfig.json ├── indexer │ ├── codegen.ts │ ├── custom-types.ts │ ├── documents │ │ ├── fragments │ │ │ ├── account-manager-permissions.graphql │ │ │ ├── account │ │ │ │ ├── account-fields.graphql │ │ │ │ ├── account-follow-rule-fields.graphql │ │ │ │ ├── account-metadata-fields.graphql │ │ │ │ ├── logged-in-account-operations-fields.graphql │ │ │ │ └── username-fields.graphql │ │ │ ├── any-key-value-fields.graphql │ │ │ ├── app-fields.graphql │ │ │ ├── boolean-value-fields.graphql │ │ │ ├── erc20-amount-fields.graphql │ │ │ ├── erc20-fields.graphql │ │ │ ├── metadata-attribute-fields.graphql │ │ │ ├── network-address-fields.graphql │ │ │ ├── notifications │ │ │ │ ├── comment-notification-fields.graphql │ │ │ │ ├── follow-notification-fields.graphql │ │ │ │ ├── mention-notification-fields.graphql │ │ │ │ ├── quote-notification-fields.graphql │ │ │ │ ├── reaction-notification-fields.graphql │ │ │ │ └── repost-notification-fields.graphql │ │ │ ├── post │ │ │ │ ├── collect │ │ │ │ │ ├── pay-to-collect-config-fields.graphql │ │ │ │ │ ├── simple-collect-action-fields.graphql │ │ │ │ │ └── unknown-action-fields.graphql │ │ │ │ ├── logged-in-post-operations-fields.graphql │ │ │ │ ├── metadata │ │ │ │ │ ├── media │ │ │ │ │ │ ├── media-audio-fields.graphql │ │ │ │ │ │ ├── media-fields.graphql │ │ │ │ │ │ ├── media-image-fields.graphql │ │ │ │ │ │ └── media-video-fields.graphql │ │ │ │ │ └── video-metadata-fields.graphql │ │ │ │ ├── post-action-fields.graphql │ │ │ │ ├── post-base-fields.graphql │ │ │ │ ├── post-fields.graphql │ │ │ │ ├── post-metadata-fields.graphql │ │ │ │ ├── post-stats-fields.graphql │ │ │ │ └── repost-fields.graphql │ │ │ ├── self-funded-transaction-request-fields.graphql │ │ │ └── sponsored-transaction-request-fields.graphql │ │ ├── mutations │ │ │ ├── account │ │ │ │ └── create-account.graphql │ │ │ ├── auth │ │ │ │ ├── authenticate.graphql │ │ │ │ ├── challenge.graphql │ │ │ │ └── refresh.graphql │ │ │ └── post │ │ │ │ ├── add-reaction.graphql │ │ │ │ ├── bookmark-post.graphql │ │ │ │ ├── create-post.graphql │ │ │ │ ├── delete-post.graphql │ │ │ │ ├── edit-post.graphql │ │ │ │ ├── report-post.graphql │ │ │ │ ├── repost.graphql │ │ │ │ ├── undo-bookmark.graphql │ │ │ │ └── undo-reaction.graphql │ │ └── queries │ │ │ ├── account │ │ │ ├── account-managers.graphql │ │ │ ├── account-stats.graphql │ │ │ ├── account.graphql │ │ │ ├── accounts-available.graphql │ │ │ ├── accounts-blocked.graphql │ │ │ ├── accounts.graphql │ │ │ ├── authenticated-sessions.graphql │ │ │ ├── followers-you-know.graphql │ │ │ ├── followers.graphql │ │ │ ├── following.graphql │ │ │ ├── last-logged-in-account.graphql │ │ │ ├── me.graphql │ │ │ ├── notifications.graphql │ │ │ └── usernames.graphql │ │ │ ├── post │ │ │ ├── post-references.graphql │ │ │ ├── post.graphql │ │ │ └── posts.graphql │ │ │ └── search.graphql │ ├── gql │ │ ├── generated │ │ │ ├── fragment-masking.ts │ │ │ ├── gql.ts │ │ │ ├── graphql.ts │ │ │ └── index.ts │ │ └── index.ts │ ├── package.json │ └── tsconfig.json ├── lens │ ├── apollo │ │ ├── cache.ts │ │ ├── cursorBasedPagination.ts │ │ └── index.ts │ ├── codegen.ts │ ├── custom-types.ts │ ├── documents │ │ ├── fragments │ │ │ ├── amount-fields.graphql │ │ │ ├── any-publication-metadata-fields.graphql │ │ │ ├── comment-base-fields.graphql │ │ │ ├── comment-fields.graphql │ │ │ ├── erc20-fields.graphql │ │ │ ├── fiat-amount-fields.graphql │ │ │ ├── follow-module-fields.graphql │ │ │ ├── handle-info-fields.graphql │ │ │ ├── image-set-fields.graphql │ │ │ ├── metadata-attribute-fields.graphql │ │ │ ├── mirror-fields.graphql │ │ │ ├── network-address-fields.graphql │ │ │ ├── open-action-modules-fields.graphql │ │ │ ├── post-fields.graphql │ │ │ ├── primary-publication-fields.graphql │ │ │ ├── profile-fields.graphql │ │ │ ├── profile-metadata-fields.graphql │ │ │ ├── profile-operations-fields.graphql │ │ │ ├── profile-stats-fields.graphql │ │ │ ├── publication-metadata │ │ │ │ ├── article-metadata-v3-fields.graphql │ │ │ │ ├── audio-metadata-v3-fields.graphql │ │ │ │ ├── checkin-metadata-v3-fields.graphql │ │ │ │ ├── image-metadata-v3-fields.graphql │ │ │ │ ├── link-metadata-v3-fields.graphql │ │ │ │ ├── live-stream-metadata-v3-fields.graphql │ │ │ │ ├── media-fields │ │ │ │ │ ├── publication-metadata-media-audio-fields.graphql │ │ │ │ │ ├── publication-metadata-media-fields.graphql │ │ │ │ │ ├── publication-metadata-media-image-fields.graphql │ │ │ │ │ └── publication-metadata-media-video-fields.graphql │ │ │ │ ├── mint-metadata-v3-fields.graphql │ │ │ │ ├── text-only-metadata-v3-fields.graphql │ │ │ │ └── video-metadata-v3-fields.graphql │ │ │ ├── publication-operation-fields.graphql │ │ │ ├── publication-stats-fields.graphql │ │ │ ├── quote-base-fields.graphql │ │ │ └── quote-fields.graphql │ │ ├── mutations │ │ │ ├── Authenticate.graphql │ │ │ ├── Broadcast.graphql │ │ │ ├── momoka │ │ │ │ ├── Momoka.graphql │ │ │ │ ├── create-momoka-comment-typed-data.graphql │ │ │ │ ├── create-momoka-mirror-typed-data.graphql │ │ │ │ └── create-momoka-post-typed-data.graphql │ │ │ ├── on-chain │ │ │ │ ├── create-change-profile-managers-typed-data.graphql │ │ │ │ ├── create-onchain-comment-typed-data.graphql │ │ │ │ ├── create-onchain-mirror-typed-data.graphql │ │ │ │ ├── create-onchain-post-typed-data.graphql │ │ │ │ ├── create-onchain-set-profile-metadata-typed-data.graphql │ │ │ │ └── on-chain.graphql │ │ │ ├── profile │ │ │ │ ├── Block.graphql │ │ │ │ ├── Follow.graphql │ │ │ │ ├── Unblock.graphql │ │ │ │ ├── Unfollow.graphql │ │ │ │ ├── add-profile-interests.graphql │ │ │ │ ├── create-profile-with-handle.graphql │ │ │ │ ├── link-handle-to-profile.graphql │ │ │ │ ├── remove-profile-interests.graphql │ │ │ │ ├── set-follow-module.graphql │ │ │ │ ├── set-profile-metadata.graphql │ │ │ │ └── unlink-handle-from-profile.graphql │ │ │ └── publication │ │ │ │ ├── act-on-open-action.graphql │ │ │ │ ├── add-publication-bookmark.graphql │ │ │ │ ├── add-publication-not-interested.graphql │ │ │ │ ├── add-reaction.graphql │ │ │ │ ├── hide-publication.graphql │ │ │ │ ├── legacy-collect.graphql │ │ │ │ ├── remove-publication-bookmark.graphql │ │ │ │ ├── remove-reaction.graphql │ │ │ │ ├── report-profile.graphql │ │ │ │ ├── report-publication.graphql │ │ │ │ └── undo-publication-not-interested.graphql │ │ ├── queries │ │ │ ├── Challenge.graphql │ │ │ ├── Followers.graphql │ │ │ ├── Following.graphql │ │ │ ├── Notifications.graphql │ │ │ ├── Profile.graphql │ │ │ ├── Profiles.graphql │ │ │ ├── Publication.graphql │ │ │ ├── Publications.graphql │ │ │ ├── approved-authentications.graphql │ │ │ ├── approved-module-allowance-amount.graphql │ │ │ ├── current-profile.graphql │ │ │ ├── explore-publications.graphql │ │ │ ├── feed-highlights.graphql │ │ │ ├── generate-lens-apirelay-address.graphql │ │ │ ├── generate-module-currency-approval-data.graphql │ │ │ ├── handle-to-address.graphql │ │ │ ├── has-publication-indexed.graphql │ │ │ ├── latest-notification-id.graphql │ │ │ ├── lens-transaction-status.graphql │ │ │ ├── mod-explore-publications.graphql │ │ │ ├── module-metadata.graphql │ │ │ ├── mutual-followers.graphql │ │ │ ├── owned-handles.graphql │ │ │ ├── profile-feed.graphql │ │ │ ├── profile-follow-module.graphql │ │ │ ├── profile-interests-options.graphql │ │ │ ├── profile-managers.graphql │ │ │ ├── profiles-managed.graphql │ │ │ ├── publication-bookmarks.graphql │ │ │ ├── revenue-from-publication.graphql │ │ │ ├── revenue-from-publications.graphql │ │ │ ├── revoke-authentication.graphql │ │ │ ├── search-profiles.graphql │ │ │ ├── search-publications.graphql │ │ │ ├── tx-id-to-tx-hash.graphql │ │ │ ├── who-acted-on-publication.graphql │ │ │ ├── who-have-blocked.graphql │ │ │ └── who-reacted-publication.graphql │ │ └── subscriptions │ │ │ ├── authorization-record-revoked.graphql │ │ │ ├── new-notification.graphql │ │ │ └── user-sig-nonces.graphql │ ├── generated.ts │ ├── package.json │ └── tsconfig.json ├── server │ ├── .env.example │ ├── .gitignore │ ├── db │ │ ├── clickhouse │ │ │ ├── client.ts │ │ │ └── schema.sql │ │ ├── indexer │ │ │ └── client.ts │ │ ├── redis │ │ │ └── client.ts │ │ └── tape │ │ │ ├── client.ts │ │ │ ├── migrations │ │ │ ├── 20240720140713_init │ │ │ │ └── migration.sql │ │ │ ├── 20240723044050_curated_profile │ │ │ │ └── migration.sql │ │ │ ├── 20240723105833_unique_profile_id │ │ │ │ └── migration.sql │ │ │ ├── 20240809041517_add_profile │ │ │ │ └── migration.sql │ │ │ ├── 20240809042300_update_default_time │ │ │ │ └── migration.sql │ │ │ ├── 20240809051310_drop_tables │ │ │ │ └── migration.sql │ │ │ ├── 20241004061051_stats │ │ │ │ └── migration.sql │ │ │ ├── 20241004061452_stats_optional │ │ │ │ └── migration.sql │ │ │ ├── 20241004062254_platform_stats │ │ │ │ └── migration.sql │ │ │ └── migration_lock.toml │ │ │ ├── schema.prisma │ │ │ └── seeds │ │ │ └── execute.sql │ ├── env.d.ts │ ├── index.ts │ ├── package.json │ └── tsconfig.json ├── tsconfig │ ├── base.json │ ├── nextjs.json │ ├── package.json │ └── react.json ├── ui │ ├── index.ts │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── Accordion.tsx │ │ ├── Badge.tsx │ │ ├── Button.tsx │ │ ├── Callout.tsx │ │ ├── Checkbox.tsx │ │ ├── Dropdown.tsx │ │ ├── HoverCard.tsx │ │ ├── Input.tsx │ │ ├── LoadingBorder.tsx │ │ ├── Modal.tsx │ │ ├── Popover.tsx │ │ ├── RangeSlider.tsx │ │ ├── Select.tsx │ │ ├── Spinner.tsx │ │ ├── Switch.tsx │ │ ├── Tabs.tsx │ │ ├── TextArea.tsx │ │ ├── Tooltip.tsx │ │ ├── icons │ │ │ ├── AddImageOutline.tsx │ │ │ ├── BellOutline.tsx │ │ │ ├── BookmarkOutline.tsx │ │ │ ├── BytesOutline.tsx │ │ │ ├── CheckOutline.tsx │ │ │ ├── ChevronDownOutline.tsx │ │ │ ├── ChevronLeftOutline.tsx │ │ │ ├── ChevronRightOutline.tsx │ │ │ ├── ChevronUpOutline.tsx │ │ │ ├── CodeOutline.tsx │ │ │ ├── CogOutline.tsx │ │ │ ├── CollectOutline.tsx │ │ │ ├── CommentOutline.tsx │ │ │ ├── CopyOutline.tsx │ │ │ ├── DisconnectOutline.tsx │ │ │ ├── EmojiOutline.tsx │ │ │ ├── ExploreOutline.tsx │ │ │ ├── ExternalOutline.tsx │ │ │ ├── FeedOutline.tsx │ │ │ ├── FireOutline.tsx │ │ │ ├── FlagOutline.tsx │ │ │ ├── FollowOutline.tsx │ │ │ ├── FollowingOutline.tsx │ │ │ ├── ForbiddenOutline.tsx │ │ │ ├── GlobeOutline.tsx │ │ │ ├── GraphOutline.tsx │ │ │ ├── HandWaveOutline.tsx │ │ │ ├── HashtagOutline.tsx │ │ │ ├── HeartFilled.tsx │ │ │ ├── HeartOutline.tsx │ │ │ ├── HomeOutline.tsx │ │ │ ├── InfoOutline.tsx │ │ │ ├── InfoSolid.tsx │ │ │ ├── InterestsOutline.tsx │ │ │ ├── KeyOutline.tsx │ │ │ ├── LinkOutline.tsx │ │ │ ├── LocationOutline.tsx │ │ │ ├── LockOutline.tsx │ │ │ ├── MentionOutline.tsx │ │ │ ├── MirrorOutline.tsx │ │ │ ├── MoonOutline.tsx │ │ │ ├── MusicOutline.tsx │ │ │ ├── NewVideoOutline.tsx │ │ │ ├── PauseOutline.tsx │ │ │ ├── PinOutline.tsx │ │ │ ├── PlayOutline.tsx │ │ │ ├── PlusOutline.tsx │ │ │ ├── ProfileBanOutline.tsx │ │ │ ├── ProfileManagerOutline.tsx │ │ │ ├── QuoteOutline.tsx │ │ │ ├── RefreshOutline.tsx │ │ │ ├── ReplyOutline.tsx │ │ │ ├── RoadmapOutline.tsx │ │ │ ├── SearchOutline.tsx │ │ │ ├── ShareOutline.tsx │ │ │ ├── SignOutline.tsx │ │ │ ├── SortOutline.tsx │ │ │ ├── SplitOutline.tsx │ │ │ ├── StopOutline.tsx │ │ │ ├── StreamOutline.tsx │ │ │ ├── SubscribeOutline.tsx │ │ │ ├── SunOutline.tsx │ │ │ ├── SwitchProfileOutline.tsx │ │ │ ├── TagOutline.tsx │ │ │ ├── ThreeDotsOutline.tsx │ │ │ ├── TimesOutline.tsx │ │ │ ├── TipOutline.tsx │ │ │ ├── TrashOutline.tsx │ │ │ ├── UploadOutline.tsx │ │ │ ├── UserOutline.tsx │ │ │ ├── VerifiedSolid.tsx │ │ │ ├── VideoOutline.tsx │ │ │ ├── WalletOutline.tsx │ │ │ ├── WarningOutline.tsx │ │ │ └── index.ts │ │ └── players │ │ │ └── video │ │ │ ├── Player.tsx │ │ │ ├── SensitiveWarning.tsx │ │ │ └── index.tsx │ ├── tailwind-preset.ts │ ├── tailwind.config.js │ └── tsconfig.json └── winder │ ├── index.ts │ ├── package.json │ ├── src │ ├── _components │ │ ├── alert.tsx │ │ ├── animated-number.tsx │ │ ├── audio-player.tsx │ │ ├── avatar.tsx │ │ ├── badge.tsx │ │ ├── button.tsx │ │ ├── dialog.tsx │ │ ├── dropdown-menu.tsx │ │ ├── empty-state.tsx │ │ ├── input.tsx │ │ ├── morph.tsx │ │ ├── popover.tsx │ │ ├── scroll-area.tsx │ │ ├── select.tsx │ │ ├── show-more.tsx │ │ ├── spinner.tsx │ │ ├── switch.tsx │ │ ├── tabs.tsx │ │ ├── textarea.tsx │ │ ├── theme-switcher.tsx │ │ ├── toast.tsx │ │ ├── tooltip.tsx │ │ └── video-player.tsx │ ├── icons.ts │ ├── theme.tsx │ ├── tw.ts │ └── winder.css │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── scripts ├── clean-branches ├── clean-packages ├── sync-branches └── update-dependencies /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @sasicodes 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: tapexyz 2 | custom: ['https://tape.xyz/donate'] 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Request a feature for Tape 3 | title: "[Feature Request] " 4 | labels: [needs review, triage] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: Thanks for taking the time to file a feature request! Please fill out this form as completely as possible. 9 | - type: textarea 10 | attributes: 11 | label: Describe the feature you'd like to request 12 | placeholder: Clearly describe what you want to see in Tape. 13 | validations: 14 | required: true 15 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | pnpm run biome:check 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | node-linker=hoisted 2 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 20 2 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "biomejs.biome", 4 | "bradlc.vscode-tailwindcss", 5 | "Prisma.prisma", 6 | "ms-azuretools.vscode-docker" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /apps/api/.env.example: -------------------------------------------------------------------------------- 1 | EVER_ACCESS_KEY= 2 | EVER_ACCESS_SECRET= 3 | LOGTAIL_API_KEY= 4 | WALLET_PRIVATE_KEY= #pk without 0x 5 | TAPE_DATABASE_URL= 6 | INDEXER_DATABASE_URL= 7 | REDIS_URL= 8 | CLICKHOUSE_URL="" 9 | CLICKHOUSE_PASSWORD="" 10 | -------------------------------------------------------------------------------- /apps/api/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | 4 | # Change them to your taste: 5 | package-lock.json 6 | yarn.lock 7 | pnpm-lock.yaml -------------------------------------------------------------------------------- /apps/api/README.md: -------------------------------------------------------------------------------- 1 | # Api Server 2 | 3 | # Run 4 | 5 | ``` 6 | pnpm dev 7 | ``` 8 | 9 | # Migrations 10 | 11 | ``` 12 | cd apps/api 13 | ``` 14 | 15 | ```sh 16 | pnpm db:migrate 17 | ``` 18 | -------------------------------------------------------------------------------- /apps/api/env.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | interface ProcessEnv { 3 | EVER_ACCESS_KEY: string; 4 | EVER_ACCESS_SECRET: string; 5 | LOGTAIL_API_KEY: string; 6 | WALLET_PRIVATE_KEY: string; 7 | TAPE_DATABASE_URL: string; 8 | INDEXER_DATABASE_URL: string; 9 | REDIS_URL: string; 10 | CLICKHOUSE_URL: string; 11 | CLICKHOUSE_PASSWORD: string; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /apps/api/src/helpers/did/resolverAbi.ts: -------------------------------------------------------------------------------- 1 | export const resolverAbi = [ 2 | { 3 | inputs: [{ internalType: "contract ENS", name: "_ens", type: "address" }], 4 | stateMutability: "nonpayable", 5 | type: "constructor" 6 | }, 7 | { 8 | inputs: [ 9 | { internalType: "address[]", name: "addresses", type: "address[]" } 10 | ], 11 | name: "getNames", 12 | outputs: [{ internalType: "string[]", name: "r", type: "string[]" }], 13 | stateMutability: "view", 14 | type: "function" 15 | } 16 | ]; 17 | -------------------------------------------------------------------------------- /apps/api/src/helpers/oembed/regex.ts: -------------------------------------------------------------------------------- 1 | export const COMMON_REGEX = { 2 | TAPE_WATCH: 3 | /^https?:\/\/tape\.xyz\/watch\/([\dA-Za-z-]+)(\?si=[\dA-Za-z]+)?$/, 4 | YOUTUBE_WATCH: 5 | /^https?:\/\/(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)[\w-]+(?:\?.*)?$/, 6 | VIMEO_WATCH: 7 | /^https?:\/\/(?:www\.|player\.)?vimeo\.com\/(?:video\/)?[\d]+(?:\?.*)?$/ 8 | }; 9 | -------------------------------------------------------------------------------- /apps/api/src/helpers/tower/checkEventExistence.ts: -------------------------------------------------------------------------------- 1 | export const checkEventExistence = ( 2 | obj: Record<string, unknown>, 3 | event: string 4 | ): boolean => { 5 | for (const key in obj) { 6 | const value = obj[key]; 7 | if (typeof value === "object" && value !== null) { 8 | if (checkEventExistence(value as Record<string, unknown>, event)) { 9 | return true; 10 | } 11 | } else if (value === event) { 12 | return true; 13 | } 14 | } 15 | return false; 16 | }; 17 | -------------------------------------------------------------------------------- /apps/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "target": "ESNext", 6 | "module": "ESNext", 7 | "moduleResolution": "node", 8 | "noEmit": false, 9 | "lib": ["esnext"], 10 | "jsx": "react-jsx", 11 | "types": ["node"], 12 | "jsxImportSource": "hono/jsx", 13 | "paths": { 14 | "@/*": ["./src/*"] 15 | } 16 | }, 17 | "exclude": ["node_modules", "dist"] 18 | } 19 | -------------------------------------------------------------------------------- /apps/contracts/.env.example: -------------------------------------------------------------------------------- 1 | POLYGONSCAN_API_KEY="" 2 | ALCHEMY_API_KEY="" 3 | PRIVATE_KEY="" 4 | -------------------------------------------------------------------------------- /apps/contracts/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | 4 | # Hardhat files 5 | /cache 6 | /artifacts 7 | 8 | # TypeChain files 9 | /typechain 10 | /typechain-types 11 | 12 | # solidity-coverage files 13 | /coverage 14 | /coverage.json 15 | 16 | .openzeppelin 17 | -------------------------------------------------------------------------------- /apps/contracts/env.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | interface ProcessEnv { 3 | POLYGONSCAN_API_KEY: string; 4 | ALCHEMY_API_KEY: string; 5 | PRIVATE_KEY: string; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /apps/contracts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tape.xyz/contracts", 3 | "version": "0.0.0", 4 | "author": "sasicodes", 5 | "license": "AGPL-3.0", 6 | "scripts": { 7 | "typecheck": "tsc --pretty --noEmit" 8 | }, 9 | "devDependencies": { 10 | "@nomicfoundation/hardhat-ethers": "^3.0.8", 11 | "@nomicfoundation/hardhat-toolbox": "^5.0.0", 12 | "@nomicfoundation/hardhat-verify": "^2.0.12", 13 | "@openzeppelin/contracts": "^5.2.0", 14 | "@openzeppelin/contracts-upgradeable": "^5.2.0", 15 | "@openzeppelin/hardhat-upgrades": "^3.9.0", 16 | "@tape.xyz/tsconfig": "workspace:*", 17 | "dotenv": "^16.4.7", 18 | "ethers": "^6.13.5", 19 | "hardhat": "^2.22.18", 20 | "ts-node": "^10.9.2", 21 | "typescript": "^5.7.3" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/contracts/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json" 3 | } 4 | -------------------------------------------------------------------------------- /apps/cron/.env.example: -------------------------------------------------------------------------------- 1 | REDIS_URL="" 2 | TAPE_DATABASE_URL="" 3 | INDEXER_DATABASE_URL="" 4 | CLICKHOUSE_URL="" 5 | CLICKHOUSE_PASSWORD="" 6 | S3_ACCESS_KEY_ID="" 7 | S3_SECRET_ACCESS_KEY="" 8 | S3_BUCKET_URL="" 9 | EVER_ACCESS_KEY="" 10 | EVER_ACCESS_SECRET="" 11 | -------------------------------------------------------------------------------- /apps/cron/README.md: -------------------------------------------------------------------------------- 1 | # Cron 2 | 3 | # Run 4 | 5 | ``` 6 | pnpm dev 7 | ``` 8 | -------------------------------------------------------------------------------- /apps/cron/env.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | interface ProcessEnv { 3 | REDIS_URL: string; 4 | TAPE_DATABASE_URL: string; 5 | INDEXER_DATABASE_URL: string; 6 | CLICKHOUSE_URL: string; 7 | CLICKHOUSE_PASSWORD: string; 8 | S3_ACCESS_KEY_ID: string; 9 | S3_SECRET_ACCESS_KEY: string; 10 | S3_BUCKET_URL: string; 11 | EVER_ACCESS_KEY: string; 12 | EVER_ACCESS_SECRET: string; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /apps/cron/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tape.xyz/cron", 3 | "version": "0.0.0", 4 | "main": "dist/index.js", 5 | "private": true, 6 | "scripts": { 7 | "dev:cron": "tsx --watch src/index.ts", 8 | "build": "tsc", 9 | "start": "tsx src/index.ts", 10 | "typecheck": "tsc --pretty --noEmit" 11 | }, 12 | "license": "AGPL-3.0", 13 | "dependencies": { 14 | "@aws-sdk/client-s3": "3.799.0", 15 | "node-cron": "^3.0.3" 16 | }, 17 | "devDependencies": { 18 | "@tape.xyz/constants": "workspace:*", 19 | "@tape.xyz/server": "workspace:*", 20 | "@tape.xyz/tsconfig": "workspace:*", 21 | "@types/node": "^22.13.1", 22 | "@types/node-cron": "^3.0.11", 23 | "tsx": "^4.19.2", 24 | "typescript": "^5.7.3" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /apps/cron/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json", 3 | "compilerOptions": { 4 | "outDir": "./dist" 5 | }, 6 | "exclude": ["node_modules", "dist"] 7 | } 8 | -------------------------------------------------------------------------------- /apps/embed/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | transpilePackages: [ 4 | "@tape.xyz/lens", 5 | "@tape.xyz/browser", 6 | "@tape.xyz/generic", 7 | "@tape.xyz/tsconfig", 8 | "@tape.xyz/ui" 9 | ], 10 | reactStrictMode: true, 11 | headers() { 12 | return [ 13 | { 14 | source: "/(.*)", 15 | headers: [{ key: "Cache-Control", value: "public, max-age=31536000" }] 16 | } 17 | ]; 18 | } 19 | }; 20 | 21 | module.exports = nextConfig; 22 | -------------------------------------------------------------------------------- /apps/embed/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {} 5 | } 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /apps/embed/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/embed/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/embed/src/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | body { 6 | @apply min-h-screen bg-white antialiased; 7 | } 8 | .livepeer-aspect-ratio-container, 9 | .livepeer-contents-container { 10 | height: inherit !important; 11 | } 12 | 13 | button:active { 14 | animation: button-pop 0.25s ease-out; 15 | } 16 | 17 | @keyframes button-pop { 18 | 0% { 19 | transform: scale(0.9); 20 | } 21 | 40% { 22 | transform: scale(1.02); 23 | } 24 | to { 25 | transform: scale(1); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/embed/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import "./globals.css"; 2 | 3 | import { TAPE_APP_DESCRIPTION, TAPE_APP_NAME } from "@tape.xyz/constants"; 4 | import type { Metadata } from "next"; 5 | import type React from "react"; 6 | 7 | export const metadata: Metadata = { 8 | title: TAPE_APP_NAME, 9 | description: TAPE_APP_DESCRIPTION 10 | }; 11 | 12 | const RootLayout = ({ children }: { children: React.ReactNode }) => { 13 | return ( 14 | <html lang="en"> 15 | <body>{children}</body> 16 | </html> 17 | ); 18 | }; 19 | 20 | export default RootLayout; 21 | -------------------------------------------------------------------------------- /apps/embed/src/components/Custom404.tsx: -------------------------------------------------------------------------------- 1 | import { STATIC_ASSETS, TAPE_APP_NAME } from "@tape.xyz/constants"; 2 | 3 | const Custom404 = () => { 4 | return ( 5 | <div className="flex h-[calc(100vh-8rem)] flex-col items-center justify-center space-y-4 text-center"> 6 | <div className="mb-10"> 7 | <img 8 | src={`${STATIC_ASSETS}/images/illustrations/404.gif`} 9 | draggable={false} 10 | height={200} 11 | width={200} 12 | alt={TAPE_APP_NAME} 13 | /> 14 | </div> 15 | <h1 className="font-bold text-4xl">404</h1> 16 | <div className="mb-6">This publication could not be found.</div> 17 | </div> 18 | ); 19 | }; 20 | 21 | export default Custom404; 22 | -------------------------------------------------------------------------------- /apps/embed/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | const base = require("@tape.xyz/ui/tailwind-preset"); 2 | import type { Config } from "tailwindcss"; 3 | 4 | const config: Config = { 5 | ...base, 6 | content: [ 7 | "./src/pages/**/*.{ts,tsx}", 8 | "./src/components/**/*.{ts,tsx}", 9 | "../../packages/ui/src/**/*.{ts,tsx}" 10 | ], 11 | plugins: [require("@tailwindcss/aspect-ratio")] 12 | }; 13 | 14 | export default config; 15 | -------------------------------------------------------------------------------- /apps/embed/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/nextjs.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["./src/*"] 7 | }, 8 | "isolatedModules": true, 9 | "plugins": [ 10 | { 11 | "name": "next" 12 | } 13 | ], 14 | "strictNullChecks": true 15 | }, 16 | "include": [ 17 | "next-env.d.ts", 18 | "**/*.ts", 19 | "**/*.tsx", 20 | "src/styles/index.css", 21 | ".next/types/**/*.ts" 22 | ], 23 | "exclude": ["node_modules"] 24 | } 25 | -------------------------------------------------------------------------------- /apps/mobile/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb 3 | # The following patterns were generated by expo-cli 4 | 5 | expo-env.d.ts 6 | # @end expo-cli -------------------------------------------------------------------------------- /apps/mobile/app/(protected)/(feed)/index.tsx: -------------------------------------------------------------------------------- 1 | import { List } from "@/components/feed/list"; 2 | import { Colors } from "@/helpers/colors"; 3 | import { StatusBar } from "expo-status-bar"; 4 | import { StyleSheet, View } from "react-native"; 5 | 6 | export default function HomeScreen() { 7 | return ( 8 | <View style={styles.container}> 9 | <StatusBar style="dark" key="feed" /> 10 | <List /> 11 | </View> 12 | ); 13 | } 14 | 15 | const styles = StyleSheet.create({ 16 | container: { 17 | flex: 1, 18 | backgroundColor: Colors.background 19 | } 20 | }); 21 | -------------------------------------------------------------------------------- /apps/mobile/app/(protected)/create.tsx: -------------------------------------------------------------------------------- 1 | import { CreateScreen } from "@/components/create/screen"; 2 | import { StatusBar } from "expo-status-bar"; 3 | import { StyleSheet, View } from "react-native"; 4 | 5 | export default function CreateModal() { 6 | return ( 7 | <View style={styles.container}> 8 | <StatusBar hidden /> 9 | <CreateScreen /> 10 | </View> 11 | ); 12 | } 13 | 14 | const styles = StyleSheet.create({ 15 | container: { 16 | flex: 1 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /apps/mobile/assets/fonts/sans-b.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/fonts/sans-b.ttf -------------------------------------------------------------------------------- /apps/mobile/assets/fonts/sans-m.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/fonts/sans-m.ttf -------------------------------------------------------------------------------- /apps/mobile/assets/fonts/sans-sb.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/fonts/sans-sb.ttf -------------------------------------------------------------------------------- /apps/mobile/assets/fonts/sans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/fonts/sans.ttf -------------------------------------------------------------------------------- /apps/mobile/assets/fonts/serif.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/fonts/serif.ttf -------------------------------------------------------------------------------- /apps/mobile/assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /apps/mobile/assets/images/auth-el.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/images/auth-el.png -------------------------------------------------------------------------------- /apps/mobile/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/images/icon.png -------------------------------------------------------------------------------- /apps/mobile/assets/images/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/mobile/assets/images/splash.png -------------------------------------------------------------------------------- /apps/mobile/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = (api) => { 2 | api.cache(true); 3 | return { 4 | presets: ["babel-preset-expo"] 5 | }; 6 | }; 7 | -------------------------------------------------------------------------------- /apps/mobile/components/auth/queries.ts: -------------------------------------------------------------------------------- 1 | import { execute } from "@/helpers/execute"; 2 | import { queryOptions } from "@tanstack/react-query"; 3 | import { ProfileDocument } from "@tape.xyz/indexer"; 4 | 5 | export const profileByIdQuery = (forProfileId: string | null) => { 6 | return queryOptions({ 7 | queryKey: ["account", forProfileId], 8 | queryFn: () => 9 | execute(ProfileDocument, { 10 | request: { forProfileId } 11 | }), 12 | enabled: Boolean(forProfileId) 13 | }); 14 | }; 15 | -------------------------------------------------------------------------------- /apps/mobile/components/feed/queries.ts: -------------------------------------------------------------------------------- 1 | import { execute } from "@/helpers/execute"; 2 | import { infiniteQueryOptions } from "@tanstack/react-query"; 3 | import { FeedDocument, FeedEventItemType } from "@tape.xyz/indexer"; 4 | 5 | export const feedQuery = (profileId: string) => 6 | infiniteQueryOptions({ 7 | queryKey: ["feed", profileId], 8 | queryFn: ({ pageParam }) => 9 | execute(FeedDocument, { 10 | request: { 11 | where: { 12 | feedEventItemTypes: [FeedEventItemType.Post], 13 | for: profileId 14 | }, 15 | cursor: pageParam 16 | } 17 | }), 18 | enabled: Boolean(profileId), 19 | initialPageParam: null, 20 | getNextPageParam: (lastPage) => lastPage.feed.pageInfo.next 21 | }); 22 | -------------------------------------------------------------------------------- /apps/mobile/components/shared/external-link.tsx: -------------------------------------------------------------------------------- 1 | import { type Href, Link } from "expo-router"; 2 | import { openBrowserAsync } from "expo-web-browser"; 3 | import type { ComponentProps } from "react"; 4 | import { Platform } from "react-native"; 5 | 6 | type Props = Omit<ComponentProps<typeof Link>, "href"> & { href: string }; 7 | 8 | export const ExternalLink = ({ href, ...rest }: Props) => { 9 | return ( 10 | <Link 11 | target="_blank" 12 | {...rest} 13 | suppressHighlighting 14 | href={href as Href} 15 | onPress={async (event) => { 16 | if (Platform.OS !== "web") { 17 | event.preventDefault(); 18 | await openBrowserAsync(href); 19 | } 20 | }} 21 | /> 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /apps/mobile/eas.json: -------------------------------------------------------------------------------- 1 | { 2 | "cli": { 3 | "version": ">= 12.6.2", 4 | "appVersionSource": "remote" 5 | }, 6 | "build": { 7 | "development": { 8 | "distribution": "internal", 9 | "ios": { 10 | "credentialsSource": "remote" 11 | } 12 | }, 13 | "preview": { 14 | "distribution": "internal" 15 | }, 16 | "production": { 17 | "autoIncrement": true 18 | } 19 | }, 20 | "submit": { 21 | "production": {} 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/mobile/helpers/colors.ts: -------------------------------------------------------------------------------- 1 | export const Colors = { 2 | text: "#000000", 3 | textSecondary: "#22222280", 4 | white: "#ffffff", 5 | black: "#000000", 6 | buttonText: "#000000", 7 | background: "#F5F5F5", 8 | border: "#22222220", 9 | link: "#3F88C5" 10 | }; 11 | -------------------------------------------------------------------------------- /apps/mobile/helpers/haptics.ts: -------------------------------------------------------------------------------- 1 | import { ImpactFeedbackStyle, impactAsync } from "expo-haptics"; 2 | 3 | export const haptic = () => { 4 | impactAsync(ImpactFeedbackStyle.Soft); 5 | }; 6 | -------------------------------------------------------------------------------- /apps/mobile/helpers/normalize-font.ts: -------------------------------------------------------------------------------- 1 | import { Dimensions, PixelRatio, Platform } from "react-native"; 2 | 3 | export const { width: windowWidth, height: windowHeight } = 4 | Dimensions.get("window"); 5 | 6 | // based on iphone 5s's scale 7 | const scale = windowWidth / 320; 8 | 9 | const normalizeFont = (size: number) => { 10 | const newSize = size * scale; 11 | if (Platform.OS === "ios") { 12 | return Math.round(PixelRatio.roundToNearestPixel(newSize)); 13 | } 14 | return Math.round(PixelRatio.roundToNearestPixel(newSize)) - 2; 15 | }; 16 | 17 | export default normalizeFont; 18 | -------------------------------------------------------------------------------- /apps/mobile/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig } = require("expo/metro-config"); 2 | const { resolve } = require("node:path"); 3 | 4 | const projectRoot = __dirname; 5 | const monorepoRoot = resolve(projectRoot, "../.."); 6 | 7 | const config = getDefaultConfig(projectRoot); 8 | 9 | config.watchFolders = [monorepoRoot]; 10 | config.resolver.nodeModulesPaths = [ 11 | resolve(projectRoot, "node_modules"), 12 | resolve(monorepoRoot, "node_modules") 13 | ]; 14 | config.resolver.disableHierarchicalLookup = true; 15 | 16 | module.exports = config; 17 | -------------------------------------------------------------------------------- /apps/mobile/store/device.tsx: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | type State = { 4 | feedItemWidth: number; 5 | setFeedItemWidth: (feedItemWidth: number) => void; 6 | }; 7 | 8 | export const useDevice = create<State>()((set) => ({ 9 | feedItemWidth: 0, 10 | setFeedItemWidth: (feedItemWidth) => set({ feedItemWidth }) 11 | })); 12 | -------------------------------------------------------------------------------- /apps/mobile/store/profile.tsx: -------------------------------------------------------------------------------- 1 | import type { Profile } from "@tape.xyz/indexer"; 2 | import { create } from "zustand"; 3 | 4 | type State = { 5 | profile: Profile | null; 6 | setProfile: (profile: Profile) => void; 7 | }; 8 | 9 | export const useActiveProfile = create<State>()((set) => ({ 10 | profile: null, 11 | setProfile: (profile) => set({ profile }) 12 | })); 13 | -------------------------------------------------------------------------------- /apps/mobile/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": ["./*"] 7 | } 8 | }, 9 | "include": ["**/*.ts", "**/*.tsx", ".expo/types/**/*.ts", "expo-env.d.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /apps/og/.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | .env.example 4 | .openzeppelin 5 | artifacts 6 | cache 7 | coverage 8 | README.md 9 | .git 10 | .dockerignore 11 | -------------------------------------------------------------------------------- /apps/og/.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 | .yarn/install-state.gz 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | /out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | -------------------------------------------------------------------------------- /apps/og/next.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | output: "standalone", 4 | poweredByHeader: false 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /apps/og/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/og/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/og/src/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import type React from "react"; 3 | 4 | import common from "@/common"; 5 | 6 | export const metadata: Metadata = common; 7 | 8 | const RootLayout = ({ children }: { children: React.ReactNode }) => { 9 | return ( 10 | <html lang="en"> 11 | <body>{children}</body> 12 | </html> 13 | ); 14 | }; 15 | 16 | export default RootLayout; 17 | -------------------------------------------------------------------------------- /apps/og/src/common.ts: -------------------------------------------------------------------------------- 1 | import { 2 | OG_IMAGE, 3 | TAPE_APP_DESCRIPTION, 4 | TAPE_APP_NAME, 5 | TAPE_WEBSITE_URL 6 | } from "@tape.xyz/constants"; 7 | import type { Metadata } from "next"; 8 | 9 | const common: Metadata = { 10 | title: TAPE_APP_NAME, 11 | description: TAPE_APP_DESCRIPTION, 12 | metadataBase: new URL(TAPE_WEBSITE_URL), 13 | openGraph: { 14 | type: "website", 15 | siteName: TAPE_APP_NAME, 16 | images: [OG_IMAGE], 17 | title: TAPE_APP_NAME, 18 | description: TAPE_APP_DESCRIPTION, 19 | url: new URL(TAPE_WEBSITE_URL) 20 | }, 21 | twitter: { 22 | card: "summary_large_image", 23 | title: TAPE_APP_NAME, 24 | description: TAPE_APP_DESCRIPTION, 25 | images: [OG_IMAGE] 26 | } 27 | }; 28 | 29 | export default common; 30 | -------------------------------------------------------------------------------- /apps/og/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/nextjs.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "target": "es5", 6 | "lib": ["dom", "dom.iterable", "esnext"], 7 | "allowJs": true, 8 | "skipLibCheck": true, 9 | "strict": true, 10 | "noEmit": true, 11 | "esModuleInterop": true, 12 | "module": "esnext", 13 | "moduleResolution": "bundler", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "jsx": "preserve", 17 | "incremental": true, 18 | "plugins": [ 19 | { 20 | "name": "next" 21 | } 22 | ], 23 | "paths": { 24 | "@/*": ["./src/*"] 25 | } 26 | }, 27 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 28 | "exclude": ["node_modules"] 29 | } 30 | -------------------------------------------------------------------------------- /apps/web-vite/public/fonts/mono.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/web-vite/public/fonts/mono.woff2 -------------------------------------------------------------------------------- /apps/web-vite/public/fonts/sans.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/web-vite/public/fonts/sans.woff2 -------------------------------------------------------------------------------- /apps/web-vite/public/fonts/serif.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/web-vite/public/fonts/serif.woff2 -------------------------------------------------------------------------------- /apps/web-vite/public/images/hero.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/web-vite/public/images/hero.webp -------------------------------------------------------------------------------- /apps/web-vite/src/components/create/advanced.tsx: -------------------------------------------------------------------------------- 1 | import { ShowMore } from "@tape.xyz/winder"; 2 | 3 | export const Advanced = () => { 4 | return ( 5 | <div> 6 | <ShowMore content="Advanced options" onToggle={() => {}} /> 7 | </div> 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/create/drag-and-drop.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export const useDragAndDrop = () => { 4 | const [dragging, setDragging] = useState(false); 5 | 6 | const onDragOver = (e: React.SyntheticEvent) => { 7 | e.preventDefault(); 8 | setDragging(true); 9 | }; 10 | 11 | const onDragLeave = () => setDragging(false); 12 | 13 | return { 14 | dragging, 15 | setDragging, 16 | onDragOver, 17 | onDragLeave 18 | }; 19 | }; 20 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/create/page.tsx: -------------------------------------------------------------------------------- 1 | import { Details } from "./details"; 2 | import DropZone from "./drop-zone"; 3 | 4 | export const CreatePage = () => { 5 | return ( 6 | <div className="flex min-h-[calc(100vh-60px)] flex-col items-center rounded-card bg-theme"> 7 | <div className="flex w-full max-w-screen-lg flex-wrap gap-14 px-2 py-20 md:flex-nowrap"> 8 | <DropZone /> 9 | <Details /> 10 | </div> 11 | </div> 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/embed/page.tsx: -------------------------------------------------------------------------------- 1 | import { PostEmbed } from "./post"; 2 | 3 | export const EmbedPage = () => { 4 | return <PostEmbed />; 5 | }; 6 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/home/page.tsx: -------------------------------------------------------------------------------- 1 | import { About } from "./about"; 2 | import { Feed } from "./feed"; 3 | import { Hero } from "./hero"; 4 | import { Invite } from "./invite"; 5 | import { Users } from "./users"; 6 | 7 | export const HomePage = () => { 8 | return ( 9 | <div className="space-y-1.5"> 10 | <Hero /> 11 | <Invite /> 12 | <Feed /> 13 | <About /> 14 | <Users /> 15 | </div> 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/mod/page.tsx: -------------------------------------------------------------------------------- 1 | import { Posts } from "./posts"; 2 | 3 | export const ModPage = () => { 4 | return ( 5 | <div className="flex min-h-screen flex-col items-center 2xl:container"> 6 | <Posts /> 7 | </div> 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/settings/me.tsx: -------------------------------------------------------------------------------- 1 | export const Me = () => { 2 | return ( 3 | <div> 4 | <h6>Me</h6> 5 | <p className="text-muted">Basic Information about Me</p> 6 | </div> 7 | ); 8 | }; 9 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/shared/auth/links.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from "@tanstack/react-router"; 2 | 3 | export const Links = () => { 4 | return ( 5 | <div className="flex items-center space-x-2.5 text-muted text-xs [&>a]:transition-colors [&>a]:hover:text-primary"> 6 | <span>© {new Date().getFullYear()} Tape</span> 7 | <div className="h-3 w-[1px] rounded-sm bg-primary/10" /> 8 | <Link to="/privacy">Privacy</Link> 9 | <div className="h-3 w-[1px] rounded-sm bg-primary/10" /> 10 | <Link to="/terms">Terms</Link> 11 | </div> 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/shared/footer/index.tsx: -------------------------------------------------------------------------------- 1 | import { Logo } from "../header/logo"; 2 | import { Links } from "./links"; 3 | import { NetworkState } from "./network"; 4 | import { Socials } from "./socials"; 5 | import { Status } from "./status"; 6 | 7 | export const Footer = () => { 8 | return ( 9 | <footer className="flex flex-wrap justify-between gap-5 px-5 py-[26px] md:flex-nowrap"> 10 | <div className="flex w-1/3 gap-6"> 11 | <Logo /> 12 | <Socials /> 13 | </div> 14 | 15 | <Links /> 16 | 17 | <div className="hidden w-1/3 justify-end gap-6 lg:flex"> 18 | <NetworkState /> 19 | <Status /> 20 | </div> 21 | </footer> 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/shared/footer/status.tsx: -------------------------------------------------------------------------------- 1 | import { TAPE_STATUS_PAGE } from "@tape.xyz/constants"; 2 | import { Button } from "@tape.xyz/winder"; 3 | 4 | export const Status = () => { 5 | return ( 6 | <a href={TAPE_STATUS_PAGE} target="_blank" rel="noreferrer"> 7 | <Button variant="secondary"> 8 | <span className="flex items-center space-x-[10px]"> 9 | <span className="relative flex size-1.5"> 10 | <span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-[#2A59FF]" /> 11 | <span className="relative inline-flex size-1.5 rounded-full bg-[#2A59FF]" /> 12 | </span> 13 | <span>All systems normal</span> 14 | </span> 15 | </Button> 16 | </a> 17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/shared/header/index.tsx: -------------------------------------------------------------------------------- 1 | import { SearchTrigger } from "../search"; 2 | import { LeftSection } from "./left"; 3 | import { RightSection } from "./right"; 4 | 5 | export const Header = () => { 6 | return ( 7 | <header className="sticky inset-x-0 top-0 z-50 flex h-[52px] w-full items-center justify-between gap-1.5 px-5 py-2"> 8 | <LeftSection /> 9 | <SearchTrigger /> 10 | <RightSection /> 11 | </header> 12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/shared/header/logo.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from "@tanstack/react-router"; 2 | import { memo } from "react"; 3 | import { TapeSvg } from "../tape-svg"; 4 | 5 | export const Logo = memo(() => { 6 | return ( 7 | <Link 8 | to="/" 9 | onContextMenu={(e) => { 10 | e.preventDefault(); 11 | window.open("/winder#brand", "_blank"); 12 | }} 13 | > 14 | <div className="flex h-9 items-center rounded-custom border border-custom px-3.5 pt-2.5 pb-2"> 15 | <TapeSvg className="h-5 text-primary" /> 16 | </div> 17 | </Link> 18 | ); 19 | }); 20 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/shared/shimmers/button.tsx: -------------------------------------------------------------------------------- 1 | import { tw } from "@tape.xyz/winder"; 2 | 3 | export const ButtonShimmer = ({ 4 | className = "h-11" 5 | }: { className?: string }) => { 6 | return ( 7 | <div className="w-full animate-shimmer"> 8 | <div className={tw("w-full rounded-custom bg-secondary", className)} /> 9 | </div> 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/u/actions.tsx: -------------------------------------------------------------------------------- 1 | import { Button, DotsThreeVertical, UserPlus } from "@tape.xyz/winder"; 2 | 3 | export const Actions = () => { 4 | return ( 5 | <> 6 | <Button variant="secondary" size="icon"> 7 | <DotsThreeVertical className="size-5" weight="bold" /> 8 | </Button> 9 | <Button> 10 | <span className="inline-flex space-x-1.5"> 11 | <span>Follow</span> 12 | <UserPlus className="size-5" /> 13 | </span> 14 | </Button> 15 | </> 16 | ); 17 | }; 18 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/u/page.tsx: -------------------------------------------------------------------------------- 1 | import { Profile } from "./profile"; 2 | 3 | export const ProfilePage = () => { 4 | return ( 5 | <div className="flex min-h-screen flex-col overflow-hidden rounded-card bg-theme"> 6 | <Profile /> 7 | </div> 8 | ); 9 | }; 10 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/watch/creator.tsx: -------------------------------------------------------------------------------- 1 | import { Account } from "./account"; 2 | import { Comments } from "./comments"; 3 | 4 | export const CreatorAndComments = () => { 5 | return ( 6 | <div className="flex-1 space-y-5"> 7 | <Account /> 8 | <Comments /> 9 | </div> 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/watch/stats-and-actions.tsx: -------------------------------------------------------------------------------- 1 | import { Actions } from "./actions"; 2 | import { Stats } from "./stats"; 3 | 4 | export const StatsAndActions = () => { 5 | return ( 6 | <div className="flex flex-wrap items-center justify-between gap-5"> 7 | <Stats /> 8 | <Actions /> 9 | </div> 10 | ); 11 | }; 12 | -------------------------------------------------------------------------------- /apps/web-vite/src/components/winder/page.tsx: -------------------------------------------------------------------------------- 1 | import { Content } from "./content"; 2 | import { Header } from "./header"; 3 | import { Sidebar } from "./sidebar"; 4 | 5 | export const WinderPage = () => { 6 | return ( 7 | <div className="container mx-auto min-h-screen max-w-6xl overflow-x-hidden md:overflow-x-visible"> 8 | <Header /> 9 | <div className="min-w-[300px] md:grid md:grid-cols-[250px_1fr]"> 10 | <Sidebar /> 11 | <Content /> 12 | </div> 13 | </div> 14 | ); 15 | }; 16 | -------------------------------------------------------------------------------- /apps/web-vite/src/helpers/format-number.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @param bytes number of bytes 3 | * @returns formatted bytes (eg. 1000 bytes, 1 KB, 1 MB, 1 GB, 1 TB) 4 | */ 5 | export const formatBytes = (bytes: number) => { 6 | if (!bytes || bytes === 0) return "0 bytes"; 7 | if (bytes < 1_000) { 8 | return `${bytes} bytes`; 9 | } 10 | 11 | const mb = bytes / 1_000_000; 12 | if (mb < 1) { 13 | const kb = bytes / 1_000; 14 | return `${kb.toFixed(2)} KB`; 15 | } 16 | 17 | const gb = mb / 1_000; 18 | if (gb >= 1) { 19 | return `${gb.toFixed(2)} GB`; 20 | } 21 | 22 | return `${mb.toFixed(2)} MB`; 23 | }; 24 | -------------------------------------------------------------------------------- /apps/web-vite/src/helpers/parse-jwt.ts: -------------------------------------------------------------------------------- 1 | type ReturnType = { 2 | sub: string; 3 | iat: number; 4 | exp: number; 5 | }; 6 | 7 | const hasBuffer = typeof Buffer === "undefined"; 8 | const decoded = (str: string): string => 9 | hasBuffer ? atob(str) : Buffer.from(str, "base64").toString("binary"); 10 | 11 | export const parseJwt = (token: string): ReturnType => { 12 | try { 13 | const splited = token.split(".")[1] ?? ""; 14 | return JSON.parse(decoded(splited)); 15 | } catch { 16 | return { sub: "", iat: 0, exp: 0 }; 17 | } 18 | }; 19 | 20 | export const shouldRefreshTokens = (token = "") => { 21 | const { exp } = parseJwt(token); 22 | return Date.now() >= exp * 1000; 23 | }; 24 | -------------------------------------------------------------------------------- /apps/web-vite/src/main.css: -------------------------------------------------------------------------------- 1 | @import "@tape.xyz/winder/src/winder.css"; 2 | @source "../../../packages/winder/**/*.{ts,tsx}"; 3 | 4 | * { 5 | scrollbar-color: var(--scrollbar-thumb) transparent; 6 | } 7 | 8 | html { 9 | font-family: var(--tape-font-sans); 10 | @apply scroll-smooth bg-site antialiased text-primary; 11 | } 12 | 13 | button { 14 | @apply cursor-pointer; 15 | } 16 | -------------------------------------------------------------------------------- /apps/web-vite/src/providers/animations.tsx: -------------------------------------------------------------------------------- 1 | import { domMax } from "motion/react"; 2 | 3 | export default domMax; 4 | -------------------------------------------------------------------------------- /apps/web-vite/src/providers/log.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | export const Log = () => { 4 | const ref = useRef(false); 5 | useEffect(() => { 6 | if (ref.current) return; 7 | console.info(` 8 | ░▒▓███████▓▒░░▒▓██████▓▒░░▒▓███████▓▒░░▒▓██████▓▒░ 9 | ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ 10 | ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ 11 | ░▒▓█▓▒░ ░▒▓████████▓▒░▒▓███████▓▒░░▒▓█████▓▒░ 12 | ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ 13 | ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓█▓▒░ 14 | ░▒▓█▓▒░ ░▒▓█▓▒░░▒▓█▓▒░▒▓█▓▒░ ░▒▓██████▓▒░ 15 | 16 | We are open-source → https://github.com/tapexyz`); 17 | ref.current = true; 18 | }, []); 19 | return null; 20 | }; 21 | -------------------------------------------------------------------------------- /apps/web-vite/src/queries/notification.tsx: -------------------------------------------------------------------------------- 1 | import { execute } from "@/helpers/execute"; 2 | import { infiniteQueryOptions, useInfiniteQuery } from "@tanstack/react-query"; 3 | import { NotificationsDocument } from "@tape.xyz/indexer"; 4 | 5 | export const notificationsQuery = infiniteQueryOptions({ 6 | queryKey: ["notifications"], 7 | queryFn: ({ pageParam }) => 8 | execute({ 9 | query: NotificationsDocument, 10 | variables: { 11 | request: { 12 | cursor: pageParam 13 | } 14 | } 15 | }), 16 | initialPageParam: null, 17 | getNextPageParam: (lastPage) => lastPage.notifications.pageInfo.next 18 | }); 19 | export const useNotificationsQuery = () => useInfiniteQuery(notificationsQuery); 20 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_auth-hoc/sign-in.tsx: -------------------------------------------------------------------------------- 1 | import { SignInPage } from "@/components/sign-in/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_auth-hoc/sign-in")({ 5 | component: SignInPage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_auth-hoc/sign-up.tsx: -------------------------------------------------------------------------------- 1 | import { SignUpPage } from "@/components/sign-up/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_auth-hoc/sign-up")({ 5 | component: SignUpPage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc.tsx: -------------------------------------------------------------------------------- 1 | import { Footer } from "@/components/shared/footer"; 2 | import { Header } from "@/components/shared/header"; 3 | import { meQuery } from "@/queries/account"; 4 | import { Outlet, createFileRoute } from "@tanstack/react-router"; 5 | 6 | export const Route = createFileRoute("/_layout-hoc")({ 7 | loader: ({ context }) => context.rqClient.ensureQueryData(meQuery), 8 | component: () => { 9 | return ( 10 | <main className="container mx-auto flex min-h-screen max-w-screen-2xl flex-col px-3"> 11 | <Header /> 12 | <Outlet /> 13 | <Footer /> 14 | </main> 15 | ); 16 | } 17 | }); 18 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/_home-hoc.tsx: -------------------------------------------------------------------------------- 1 | import { FloatingNav } from "@/components/shared/floating-nav"; 2 | import { Outlet, createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/_home-hoc")({ 5 | component: () => { 6 | return ( 7 | <> 8 | <Outlet /> 9 | <FloatingNav /> 10 | </> 11 | ); 12 | } 13 | }); 14 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/_home-hoc/explore.tsx: -------------------------------------------------------------------------------- 1 | import { ExplorePage } from "@/components/explore/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | const media = ["all", "videos", "livestreams", "audios"] as const; 5 | 6 | type ExploreSearchParams = { 7 | media: (typeof media)[number]; 8 | }; 9 | 10 | export const Route = createFileRoute("/_layout-hoc/_home-hoc/explore")({ 11 | validateSearch: (search: Record<string, unknown>): ExploreSearchParams => { 12 | return { 13 | media: media.includes(search.media as ExploreSearchParams["media"]) 14 | ? (search.media as ExploreSearchParams["media"]) 15 | : "all" 16 | }; 17 | }, 18 | component: ExplorePage 19 | }); 20 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/_home-hoc/following.tsx: -------------------------------------------------------------------------------- 1 | import { FollowingPage } from "@/components/following/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/_home-hoc/following")({ 5 | component: FollowingPage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/_home-hoc/index.tsx: -------------------------------------------------------------------------------- 1 | import { HomePage } from "@/components/home/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/_home-hoc/")({ 5 | component: HomePage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/_settings-hoc.tsx: -------------------------------------------------------------------------------- 1 | import { Sidebar } from "@/components/settings/sidebar"; 2 | import { Outlet, createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/_settings-hoc")({ 5 | component: () => { 6 | return ( 7 | <div className="flex min-h-[calc(100vh-60px)] flex-col items-center rounded-card bg-theme"> 8 | <div className="flex w-full max-w-screen-lg flex-1 gap-12 p-2 lg:py-10"> 9 | <Sidebar /> 10 | <Outlet /> 11 | </div> 12 | </div> 13 | ); 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/_settings-hoc/settings/me.tsx: -------------------------------------------------------------------------------- 1 | import { Me } from "@/components/settings/me"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/_settings-hoc/settings/me")({ 5 | component: Me 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/create/index.tsx: -------------------------------------------------------------------------------- 1 | import { CreatePage } from "@/components/create/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/create/")({ 5 | component: CreatePage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/feed/index.tsx: -------------------------------------------------------------------------------- 1 | import { FeedPage } from "@/components/feed/page"; 2 | import { bytesQuery } from "@/queries/post"; 3 | import { createFileRoute } from "@tanstack/react-router"; 4 | 5 | export const Route = createFileRoute("/_layout-hoc/feed/")({ 6 | loader: ({ context: { rqClient } }) => { 7 | return rqClient.ensureInfiniteQueryData(bytesQuery); 8 | }, 9 | component: FeedPage 10 | }); 11 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/mod/index.tsx: -------------------------------------------------------------------------------- 1 | import { ModPage } from "@/components/mod/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/mod/")({ 5 | component: ModPage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/privacy/index.tsx: -------------------------------------------------------------------------------- 1 | import { PrivacyPage } from "@/components/privacy/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/privacy/")({ 5 | component: PrivacyPage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/terms/index.tsx: -------------------------------------------------------------------------------- 1 | import { TermsPage } from "@/components/terms/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/_layout-hoc/terms/")({ 5 | component: TermsPage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/_layout-hoc/watch/$postId.tsx: -------------------------------------------------------------------------------- 1 | import { WatchPage } from "@/components/watch/page"; 2 | import { commentsQuery } from "@/queries/comment"; 3 | import { postQuery } from "@/queries/post"; 4 | import { createFileRoute } from "@tanstack/react-router"; 5 | import { Spinner } from "@tape.xyz/winder"; 6 | 7 | export const Route = createFileRoute("/_layout-hoc/watch/$postId")({ 8 | loader: ({ context: { rqClient }, params: { postId } }) => { 9 | rqClient.ensureQueryData(postQuery(postId)); 10 | return rqClient.ensureInfiniteQueryData(commentsQuery(postId)); 11 | }, 12 | pendingComponent: () => ( 13 | <div className="grid min-h-screen place-items-center"> 14 | <Spinner /> 15 | </div> 16 | ), 17 | component: WatchPage 18 | }); 19 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/embed/$postId.tsx: -------------------------------------------------------------------------------- 1 | import { EmbedPage } from "@/components/embed/page"; 2 | import { postQuery } from "@/queries/post"; 3 | import { createFileRoute } from "@tanstack/react-router"; 4 | 5 | type EmbedSearchParams = { 6 | t: number; 7 | loop: number; 8 | autoplay: number; 9 | }; 10 | 11 | export const Route = createFileRoute("/embed/$postId")({ 12 | loader: ({ context: { rqClient }, params: { postId } }) => { 13 | return rqClient.ensureQueryData(postQuery(postId)); 14 | }, 15 | validateSearch: (search: Record<string, unknown>): EmbedSearchParams => { 16 | return { 17 | t: Number(search.t) || 0, 18 | loop: Number(search.loop) || 0, 19 | autoplay: Number(search.autoplay) || 1 20 | }; 21 | }, 22 | component: EmbedPage 23 | }); 24 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/open/index.tsx: -------------------------------------------------------------------------------- 1 | import { OpenPage } from "@/components/open/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/open/")({ 5 | component: OpenPage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/routes/winder/index.tsx: -------------------------------------------------------------------------------- 1 | import { WinderPage } from "@/components/winder/page"; 2 | import { createFileRoute } from "@tanstack/react-router"; 3 | 4 | export const Route = createFileRoute("/winder/")({ 5 | component: WinderPage 6 | }); 7 | -------------------------------------------------------------------------------- /apps/web-vite/src/store/scroll.tsx: -------------------------------------------------------------------------------- 1 | import type { RefObject } from "react"; 2 | import { create } from "zustand"; 3 | 4 | interface ScrollState { 5 | scrollRef: RefObject<HTMLDivElement> | null; 6 | setScrollRef: (ref: RefObject<HTMLDivElement>) => void; 7 | } 8 | 9 | export const useScrollStore = create<ScrollState>((set) => ({ 10 | scrollRef: null, 11 | setScrollRef: (ref) => set({ scrollRef: ref }) 12 | })); 13 | -------------------------------------------------------------------------------- /apps/web-vite/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// <reference types="vite/client" /> 2 | -------------------------------------------------------------------------------- /apps/web-vite/src/worker/build.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | 3 | esbuild.build({ 4 | allowOverwrite: true, 5 | bundle: true, 6 | entryPoints: ["src/worker/sw.ts"], 7 | format: "esm", 8 | minify: true, 9 | outfile: "dist/sw.js", 10 | platform: "browser", 11 | target: "esnext", 12 | define: { "process.env.NODE_ENV": '"production"' } 13 | }); 14 | -------------------------------------------------------------------------------- /apps/web-vite/src/worker/sw.ts: -------------------------------------------------------------------------------- 1 | import { cacheNewAssets, deleteOldCaches } from "./cache"; 2 | import { pushEventToQueue } from "./events"; 3 | 4 | declare let self: ServiceWorkerGlobalScope; 5 | 6 | // Activate the new service worker immediately 7 | self.addEventListener("install", () => { 8 | self.skipWaiting(); 9 | }); 10 | 11 | // Take control of all clients (open tabs, etc.) immediately 12 | self.addEventListener("activate", (event: ExtendableEvent) => { 13 | event.waitUntil(deleteOldCaches().then(() => self.clients.claim())); 14 | }); 15 | 16 | self.addEventListener("fetch", (event) => { 17 | cacheNewAssets(event); 18 | }); 19 | 20 | self.addEventListener("message", (event) => { 21 | pushEventToQueue(event); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/web-vite/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/react.json", 3 | "compilerOptions": { 4 | "target": "ES2020", 5 | "useDefineForClassFields": true, 6 | "module": "ESNext", 7 | "moduleResolution": "bundler", 8 | "moduleDetection": "force", 9 | "allowImportingTsExtensions": true, 10 | "noEmit": true, 11 | "baseUrl": ".", 12 | "lib": ["DOM", "ESNext", "WebWorker"], 13 | "outDir": "dist", 14 | "paths": { 15 | "@/*": ["./src/*"] 16 | } 17 | }, 18 | "include": ["src"] 19 | } 20 | -------------------------------------------------------------------------------- /apps/web-vite/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [], 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["./src/*"] 7 | } 8 | }, 9 | "references": [ 10 | { "path": "./tsconfig.app.json" }, 11 | { "path": "./tsconfig.node.json" } 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /apps/web-vite/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json", 3 | "compilerOptions": { 4 | "target": "ES2022", 5 | "lib": ["ES2023"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | "moduleResolution": "bundler", 9 | "allowImportingTsExtensions": true, 10 | "isolatedModules": true, 11 | "moduleDetection": "force", 12 | "noEmit": true 13 | }, 14 | "include": ["vite.config.ts"] 15 | } 16 | -------------------------------------------------------------------------------- /apps/web-vite/tsr.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "autoCodeSplitting": true 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /apps/web/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/apps/web/public/favicon.ico -------------------------------------------------------------------------------- /apps/web/public/robots.txt: -------------------------------------------------------------------------------- 1 | Sitemap: https://tape.xyz/sitemap.xml 2 | 3 | User-agent: * 4 | Allow: /* 5 | 6 | Disallow: /api/* 7 | Disallow: /settings/* 8 | -------------------------------------------------------------------------------- /apps/web/public/sitemap.xml: -------------------------------------------------------------------------------- 1 | <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"> 2 | <sitemap> 3 | <loc>https://tape.xyz/sitemaps/profiles/1.xml</loc> 4 | </sitemap> 5 | <sitemap> 6 | <loc>https://tape.xyz/sitemaps/profiles/2.xml</loc> 7 | </sitemap> 8 | <sitemap> 9 | <loc>https://tape.xyz/sitemaps/profiles/3.xml</loc> 10 | </sitemap> 11 | <sitemap> 12 | <loc>https://tape.xyz/sitemaps/videos/1.xml</loc> 13 | </sitemap> 14 | <sitemap> 15 | <loc>https://tape.xyz/sitemaps/videos/2.xml</loc> 16 | </sitemap> 17 | </sitemapindex> 18 | -------------------------------------------------------------------------------- /apps/web/src/components/Bytes/TopOverlay.tsx: -------------------------------------------------------------------------------- 1 | import type { FC } from "react"; 2 | 3 | type Props = { 4 | onClickVideo: () => void; 5 | }; 6 | 7 | const TopOverlay: FC<Props> = ({ onClickVideo }) => { 8 | return ( 9 | <div 10 | role="button" 11 | tabIndex={0} 12 | onClick={() => onClickVideo()} 13 | onKeyDown={() => onClickVideo()} 14 | className="absolute inset-0 z-[1] w-full cursor-default outline-none" 15 | /> 16 | ); 17 | }; 18 | 19 | export default TopOverlay; 20 | -------------------------------------------------------------------------------- /apps/web/src/components/Common/Alert.tsx: -------------------------------------------------------------------------------- 1 | import { tw } from "@tape.xyz/browser"; 2 | import type { FC, ReactNode } from "react"; 3 | 4 | type Props = { 5 | children: ReactNode; 6 | className?: string; 7 | variant?: "warning" | "danger" | "success"; 8 | }; 9 | 10 | const Alert: FC<Props> = ({ children, variant = "warning", className }) => { 11 | return ( 12 | <div 13 | className={tw("flex items-center rounded-xl border p-4", className, { 14 | "border-yellow-500 border-opacity-50": variant === "warning", 15 | "border-red-500 border-opacity-50": variant === "danger", 16 | "border-green-500 border-opacity-50": variant === "success" 17 | })} 18 | > 19 | {children} 20 | </div> 21 | ); 22 | }; 23 | 24 | export default Alert; 25 | -------------------------------------------------------------------------------- /apps/web/src/components/Common/Links/AddressExplorerLink.tsx: -------------------------------------------------------------------------------- 1 | import { POLYGONSCAN_URL } from "@tape.xyz/constants"; 2 | import Link from "next/link"; 3 | import type { ReactElement } from "react"; 4 | 5 | const AddressExplorerLink = ({ 6 | address, 7 | children 8 | }: { 9 | address: string; 10 | children: ReactElement; 11 | }) => { 12 | return ( 13 | <Link 14 | href={`${POLYGONSCAN_URL}/address/${address}`} 15 | rel="noreferer noreferrer" 16 | target="_blank" 17 | > 18 | {children} 19 | </Link> 20 | ); 21 | }; 22 | 23 | export default AddressExplorerLink; 24 | -------------------------------------------------------------------------------- /apps/web/src/components/Common/Links/ArweaveExplorerLink.tsx: -------------------------------------------------------------------------------- 1 | import Link from "next/link"; 2 | import type { ReactElement } from "react"; 3 | 4 | const ArweaveExplorerLink = ({ 5 | txId, 6 | children 7 | }: { 8 | txId: string; 9 | children: ReactElement; 10 | }) => { 11 | return ( 12 | <Link 13 | href={`https://api.tape.xyz/gateway/ar/${txId}?pretty`} 14 | rel="noreferer noreferrer" 15 | target="_blank" 16 | > 17 | {children} 18 | </Link> 19 | ); 20 | }; 21 | 22 | export default ArweaveExplorerLink; 23 | -------------------------------------------------------------------------------- /apps/web/src/components/Common/Links/HashExplorerLink.tsx: -------------------------------------------------------------------------------- 1 | import { POLYGONSCAN_URL } from "@tape.xyz/constants"; 2 | import Link from "next/link"; 3 | import type { ReactElement } from "react"; 4 | 5 | const HashExplorerLink = ({ 6 | hash, 7 | children 8 | }: { 9 | hash: string; 10 | children: ReactElement; 11 | }) => { 12 | return ( 13 | <Link 14 | href={`${POLYGONSCAN_URL}/tx/${hash}`} 15 | rel="noreferer noreferrer" 16 | target="_blank" 17 | > 18 | {children} 19 | </Link> 20 | ); 21 | }; 22 | 23 | export default HashExplorerLink; 24 | -------------------------------------------------------------------------------- /apps/web/src/components/Common/Links/IPFSLink.tsx: -------------------------------------------------------------------------------- 1 | import { IPFS_GATEWAY_URL } from "@tape.xyz/constants"; 2 | import Link from "next/link"; 3 | import type { ReactElement } from "react"; 4 | 5 | const IPFSLink = ({ 6 | hash, 7 | children 8 | }: { 9 | hash: string; 10 | children: ReactElement; 11 | }) => { 12 | return ( 13 | <Link 14 | href={`${IPFS_GATEWAY_URL}/${hash}`} 15 | rel="noreferer noreferrer" 16 | target="_blank" 17 | > 18 | {children} 19 | </Link> 20 | ); 21 | }; 22 | 23 | export default IPFSLink; 24 | -------------------------------------------------------------------------------- /apps/web/src/components/Common/Links/TokenExplorerLink.tsx: -------------------------------------------------------------------------------- 1 | import { POLYGONSCAN_URL } from "@tape.xyz/constants"; 2 | import Link from "next/link"; 3 | import type { ReactElement } from "react"; 4 | 5 | const TokenExplorerLink = ({ 6 | address, 7 | children 8 | }: { 9 | address: string; 10 | children: ReactElement; 11 | }) => { 12 | return ( 13 | <Link 14 | href={`${POLYGONSCAN_URL}/token/${address}`} 15 | rel="noreferer noreferrer" 16 | target="_blank" 17 | > 18 | {children} 19 | </Link> 20 | ); 21 | }; 22 | 23 | export default TokenExplorerLink; 24 | -------------------------------------------------------------------------------- /apps/web/src/components/Common/Providers/ThemeProvider.tsx: -------------------------------------------------------------------------------- 1 | import { ThemeProvider as NextTheme } from "next-themes"; 2 | import type { FC, ReactNode } from "react"; 3 | 4 | type Props = { 5 | children: ReactNode; 6 | }; 7 | 8 | const ThemeProvider: FC<Props> = ({ children }) => { 9 | return ( 10 | <NextTheme 11 | defaultTheme="light" 12 | attribute="class" 13 | enableSystem 14 | enableColorScheme 15 | > 16 | {children} 17 | </NextTheme> 18 | ); 19 | }; 20 | 21 | export default ThemeProvider; 22 | -------------------------------------------------------------------------------- /apps/web/src/components/Common/matchers/HashtagMatcher.tsx: -------------------------------------------------------------------------------- 1 | import { Matcher } from "interweave"; 2 | import Link from "next/link"; 3 | import { createElement } from "react"; 4 | 5 | const Hashtag = ({ ...props }: any) => { 6 | return ( 7 | <Link href={`/explore/hashtag/${props.display?.slice(1)}`}> 8 | {props.display} 9 | </Link> 10 | ); 11 | }; 12 | 13 | export class HashtagMatcher extends Matcher { 14 | replaceWith(match: string, props: any) { 15 | return createElement(Hashtag, props, match); 16 | } 17 | 18 | asTag(): string { 19 | return "a"; 20 | } 21 | 22 | match(value: string) { 23 | return this.doMatch(value, /\B#[\w&-i̇]+/, (matches) => { 24 | return { 25 | display: matches[0] 26 | }; 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /apps/web/src/components/Create/filereader.d.ts: -------------------------------------------------------------------------------- 1 | declare module "filereader-stream"; 2 | -------------------------------------------------------------------------------- /apps/web/src/components/Home/index.tsx: -------------------------------------------------------------------------------- 1 | import { EVENTS } from "@tape.xyz/generic"; 2 | import type { NextPage } from "next"; 3 | import { useEffect } from "react"; 4 | 5 | import useSw from "@/hooks/useSw"; 6 | 7 | import Feed from "./Feed"; 8 | import TopSection from "./TopSection"; 9 | 10 | const Home: NextPage = () => { 11 | const { addEventToQueue } = useSw(); 12 | 13 | useEffect(() => { 14 | addEventToQueue(EVENTS.PAGEVIEW, { page: EVENTS.PAGE_VIEW.HOME }); 15 | }, []); 16 | 17 | return ( 18 | <div className="container mx-auto max-w-screen-ultrawide"> 19 | <TopSection /> 20 | <Feed /> 21 | </div> 22 | ); 23 | }; 24 | 25 | export default Home; 26 | -------------------------------------------------------------------------------- /apps/web/src/components/Profile/BasicInfo/Stats/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Profile } from "@tape.xyz/lens"; 2 | import type { FC } from "react"; 3 | 4 | import Followers from "./Followers"; 5 | import Following from "./Following"; 6 | 7 | type Props = { 8 | profile: Profile; 9 | }; 10 | 11 | const Stats: FC<Props> = ({ profile }) => { 12 | return ( 13 | <div className="flex gap-3"> 14 | <Followers stats={profile.stats} profileId={profile.id} /> 15 | <Following stats={profile.stats} profileId={profile.id} /> 16 | </div> 17 | ); 18 | }; 19 | 20 | export default Stats; 21 | -------------------------------------------------------------------------------- /apps/web/src/components/Settings/Allowance/index.tsx: -------------------------------------------------------------------------------- 1 | import MetaTags from "@/components/Common/MetaTags"; 2 | 3 | import ModuleAllowance from "./Modules"; 4 | 5 | const Allowance = () => { 6 | return ( 7 | <> 8 | <MetaTags title="Allowance" /> 9 | <ModuleAllowance /> 10 | </> 11 | ); 12 | }; 13 | 14 | export default Allowance; 15 | -------------------------------------------------------------------------------- /apps/web/src/components/Settings/Blocked/index.tsx: -------------------------------------------------------------------------------- 1 | import MetaTags from "@/components/Common/MetaTags"; 2 | 3 | import List from "./List"; 4 | 5 | const Blocked = () => { 6 | return ( 7 | <> 8 | <MetaTags title="Blocked Profiles" /> 9 | <div className="mb-5 space-y-2"> 10 | <h1 className="font-bold text-brand-400 text-xl">Blocked Profiles</h1> 11 | <p className="text opacity-80"> 12 | Here is a list of profiles that you have been blocked. You have the 13 | option to unblock them whenever you wish. 14 | </p> 15 | </div> 16 | <List /> 17 | </> 18 | ); 19 | }; 20 | 21 | export default Blocked; 22 | -------------------------------------------------------------------------------- /apps/web/src/components/Settings/Follow/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Profile } from "@tape.xyz/lens"; 2 | 3 | import MetaTags from "@/components/Common/MetaTags"; 4 | 5 | import FeeFollow from "./FeeFollow"; 6 | import RevertFollow from "./RevertFollow"; 7 | 8 | type Props = { 9 | profile: Profile; 10 | }; 11 | 12 | const FollowSettings = ({ profile }: Props) => { 13 | return ( 14 | <> 15 | <MetaTags title="Follow Settings" /> 16 | <div className="space-y-4"> 17 | <FeeFollow profile={profile} /> 18 | <RevertFollow profile={profile} /> 19 | </div> 20 | </> 21 | ); 22 | }; 23 | 24 | export default FollowSettings; 25 | -------------------------------------------------------------------------------- /apps/web/src/components/Settings/Handles/index.tsx: -------------------------------------------------------------------------------- 1 | import MetaTags from "@/components/Common/MetaTags"; 2 | 3 | import List from "./List"; 4 | 5 | const Handles = () => { 6 | return ( 7 | <div> 8 | <MetaTags title="Owned Handles" /> 9 | <div className="mb-5 space-y-2"> 10 | <h1 className="font-bold text-brand-400 text-xl">Owned Handles</h1> 11 | <p className="text opacity-80"> 12 | List of handles with profile owned by the connected address. 13 | </p> 14 | </div> 15 | <List /> 16 | </div> 17 | ); 18 | }; 19 | 20 | export default Handles; 21 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/ButtonShimmer.tsx: -------------------------------------------------------------------------------- 1 | import { tw } from "@tape.xyz/browser"; 2 | 3 | const ButtonShimmer = ({ className = "h-10" }: { className?: string }) => { 4 | return ( 5 | <div className="w-full animate-shimmer"> 6 | <div 7 | className={tw( 8 | "w-full rounded-lg bg-gray-200 dark:bg-gray-800", 9 | className 10 | )} 11 | /> 12 | </div> 13 | ); 14 | }; 15 | 16 | export default ButtonShimmer; 17 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/CategoriesShimmer.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | 3 | import CategoryItemShimmer from "./CategoryItemShimmer"; 4 | 5 | const CategoriesShimmer = () => { 6 | const cards = useMemo(() => Array(14).fill(1), []); 7 | 8 | return ( 9 | <div className="my-1 hidden gap-4 sm:grid-cols-2 md:grid md:grid-cols-4 lg:grid-cols-7"> 10 | {cards.map((i, idx) => ( 11 | <CategoryItemShimmer key={`${i}_${idx}`} /> 12 | ))} 13 | </div> 14 | ); 15 | }; 16 | 17 | export default CategoriesShimmer; 18 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/CategoryItemShimmer.tsx: -------------------------------------------------------------------------------- 1 | const CategoryItemShimmer = () => { 2 | return ( 3 | <div className="animate-shimmer"> 4 | <div className="rounded-lg bg-gray-200 p-9 dark:bg-gray-800" /> 5 | </div> 6 | ); 7 | }; 8 | 9 | export default CategoryItemShimmer; 10 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/CommentsShimmer.tsx: -------------------------------------------------------------------------------- 1 | import CommentItemShimmer from "./CommentItemShimmer"; 2 | 3 | const CommentsShimmer = () => { 4 | return ( 5 | <div className="space-y-2"> 6 | <CommentItemShimmer /> 7 | <CommentItemShimmer /> 8 | </div> 9 | ); 10 | }; 11 | 12 | export default CommentsShimmer; 13 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/LatestBytesShimmer.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | 3 | const LatestBytesShimmer = ({ count = 15 }: { count?: number }) => { 4 | const cards = useMemo(() => Array(count).fill(1), [count]); 5 | return ( 6 | <div className="animate-shimmer"> 7 | <div className="no-scrollbar relative mb-8 flex items-start space-x-4 overflow-x-auto scroll-smooth"> 8 | {cards.map((i, idx) => ( 9 | <div key={`${i}_${idx}`} className="space-y-1.5"> 10 | <div className="aspect-[9/16] h-[350px] ultrawide:h-[400px] ultrawide:w-[260px] w-[220px] rounded-large bg-gray-200 dark:bg-gray-800" /> 11 | </div> 12 | ))} 13 | </div> 14 | </div> 15 | ); 16 | }; 17 | 18 | export default LatestBytesShimmer; 19 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/SettingsShimmer.tsx: -------------------------------------------------------------------------------- 1 | import { Spinner } from "@tape.xyz/ui"; 2 | 3 | const SettingsShimmer = () => { 4 | return ( 5 | <div className="grid h-[80vh] place-content-center"> 6 | <Spinner /> 7 | </div> 8 | ); 9 | }; 10 | 11 | export default SettingsShimmer; 12 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/SquareButtonShimmer.tsx: -------------------------------------------------------------------------------- 1 | const SquareButtonShimmer = () => { 2 | return ( 3 | <div className="flex self-center"> 4 | <div className="flex animate-shimmer"> 5 | <div className="rounded-lg bg-gray-200 p-3.5 md:rounded-xl md:p-[18px] dark:bg-gray-800" /> 6 | </div> 7 | </div> 8 | ); 9 | }; 10 | 11 | export default SquareButtonShimmer; 12 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/SuggestedShimmer.tsx: -------------------------------------------------------------------------------- 1 | const SuggestedShimmer = () => { 2 | return ( 3 | <div className="w-full rounded-md"> 4 | <div className="flex animate-shimmer space-x-2"> 5 | <div className="h-24 w-44 rounded-small bg-gray-200 dark:bg-gray-800" /> 6 | <div className="flex flex-1 flex-col space-y-2 py-1"> 7 | <div className="h-4 w-full rounded bg-gray-200 dark:bg-gray-800" /> 8 | <div className="h-3 w-1/2 rounded bg-gray-200 dark:bg-gray-800" /> 9 | <div className="h-3 w-1/2 rounded bg-gray-200 dark:bg-gray-800" /> 10 | </div> 11 | </div> 12 | </div> 13 | ); 14 | }; 15 | 16 | export default SuggestedShimmer; 17 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/ThumbnailsShimmer.tsx: -------------------------------------------------------------------------------- 1 | import { useMemo } from "react"; 2 | 3 | import { THUMBNAIL_GENERATE_COUNT } from "@/components/Create/ChooseThumbnail"; 4 | 5 | const ThumbnailsShimmer = () => { 6 | const thumbnails = useMemo(() => Array(THUMBNAIL_GENERATE_COUNT).fill(1), []); 7 | 8 | return ( 9 | <> 10 | {thumbnails.map((e, i) => ( 11 | <div 12 | key={`${e}_${i}`} 13 | className="tape-border aspect-[16/9] h-full w-full animate-shimmer rounded-md" 14 | > 15 | <div className="h-full rounded-md bg-gray-200 dark:bg-gray-800" /> 16 | </div> 17 | ))} 18 | </> 19 | ); 20 | }; 21 | 22 | export default ThumbnailsShimmer; 23 | -------------------------------------------------------------------------------- /apps/web/src/components/Shimmers/TimelineShimmer.tsx: -------------------------------------------------------------------------------- 1 | import { tw } from "@tape.xyz/browser"; 2 | import { useMemo } from "react"; 3 | 4 | import VideoCardShimmer from "./VideoCardShimmer"; 5 | 6 | const TimelineShimmer = ({ 7 | className, 8 | count = 15 9 | }: { 10 | className?: string; 11 | count?: number; 12 | }) => { 13 | const cards = useMemo(() => Array(count).fill(1), [count]); 14 | return ( 15 | <div 16 | className={tw( 17 | "grid-col-1 grid desktop:grid-cols-4 tablet:grid-cols-3 ultrawide:grid-cols-6 gap-x-4 gap-y-2 md:gap-y-6", 18 | className 19 | )} 20 | > 21 | {cards.map((i, idx) => ( 22 | <VideoCardShimmer key={`${i}_${idx}`} /> 23 | ))} 24 | </div> 25 | ); 26 | }; 27 | 28 | export default TimelineShimmer; 29 | -------------------------------------------------------------------------------- /apps/web/src/components/Watch/Comments/AudioComment.tsx: -------------------------------------------------------------------------------- 1 | import { sanitizeDStorageUrl } from "@tape.xyz/generic"; 2 | import type { FC } from "react"; 3 | 4 | type Props = { 5 | uri: string; 6 | }; 7 | 8 | const AudioComment: FC<Props> = ({ uri }) => { 9 | return ( 10 | <div className="my-2"> 11 | <audio controls controlsList="nodownload noplaybackrate"> 12 | <source src={sanitizeDStorageUrl(uri)} type="audio/mpeg" /> 13 | Your browser does not support the audio element. 14 | <track kind="captions" /> 15 | </audio> 16 | </div> 17 | ); 18 | }; 19 | export default AudioComment; 20 | -------------------------------------------------------------------------------- /apps/web/src/components/Watch/OpenActions/verified-contracts.tsx: -------------------------------------------------------------------------------- 1 | import { IS_MAINNET } from "@tape.xyz/constants"; 2 | 3 | export const VERIFIED_UNKNOWN_OPEN_ACTION_CONTRACTS = { 4 | TIP: IS_MAINNET 5 | ? "0x22cb67432C101a9b6fE0F9ab542c8ADD5DD48153" 6 | : "0x6111e258a6d00d805DcF1249900895c7aA0cD186" 7 | }; 8 | -------------------------------------------------------------------------------- /apps/web/src/hooks/useSw.ts: -------------------------------------------------------------------------------- 1 | import { createContext, useContext } from "react"; 2 | 3 | type ServiceWorkerContextType = { 4 | addEventToQueue: (name: string, properties?: Record<string, unknown>) => void; 5 | }; 6 | 7 | export const ServiceWorkerContext = createContext< 8 | ServiceWorkerContextType | undefined 9 | >(undefined); 10 | 11 | const useSw = () => { 12 | const context = useContext(ServiceWorkerContext); 13 | if (!context) { 14 | throw new Error("[SW] useSw must be used within a ServiceWorkerProvider"); 15 | } 16 | return context; 17 | }; 18 | 19 | export default useSw; 20 | -------------------------------------------------------------------------------- /apps/web/src/lib/getCurrentSession.ts: -------------------------------------------------------------------------------- 1 | import { parseJwt } from "@tape.xyz/generic"; 2 | 3 | import { hydrateAuthTokens } from "./store/auth"; 4 | 5 | const getCurrentSession = () => { 6 | const { refreshToken } = hydrateAuthTokens(); 7 | 8 | const currentSession = parseJwt(refreshToken || ""); 9 | 10 | return { 11 | profileId: currentSession?.id, 12 | authorizationId: currentSession?.authorizationId 13 | }; 14 | }; 15 | 16 | export default getCurrentSession; 17 | -------------------------------------------------------------------------------- /apps/web/src/lib/store/comment.ts: -------------------------------------------------------------------------------- 1 | import { CustomCommentsFilterEnum } from "@tape.xyz/lens/custom-types"; 2 | import { create } from "zustand"; 3 | 4 | interface CommentState { 5 | selectedCommentFilter: CustomCommentsFilterEnum; 6 | setSelectedCommentFilter: (filter: CustomCommentsFilterEnum) => void; 7 | } 8 | 9 | const useCommentStore = create<CommentState>((set) => ({ 10 | selectedCommentFilter: CustomCommentsFilterEnum.RELEVANT_COMMENTS, 11 | setSelectedCommentFilter: (selectedCommentFilter) => 12 | set({ selectedCommentFilter }) 13 | })); 14 | 15 | export default useCommentStore; 16 | -------------------------------------------------------------------------------- /apps/web/src/lib/store/idb/curated.ts: -------------------------------------------------------------------------------- 1 | import { LocalIDBStore } from "@tape.xyz/lens/custom-types"; 2 | import { create } from "zustand"; 3 | import { persist } from "zustand/middleware"; 4 | 5 | import createIdbStorage from "@/lib/createIdbStorage"; 6 | 7 | interface State { 8 | curatedProfiles: string[]; 9 | setCuratedProfiles: (curatedProfiles: string[]) => void; 10 | } 11 | 12 | const useCuratedProfiles = create( 13 | persist<State>( 14 | (set) => ({ 15 | curatedProfiles: [], 16 | setCuratedProfiles: (curatedProfiles) => set({ curatedProfiles }) 17 | }), 18 | { 19 | name: LocalIDBStore.ALLOWED_TOKENS_STORE, 20 | storage: createIdbStorage() 21 | } 22 | ) 23 | ); 24 | 25 | export default useCuratedProfiles; 26 | -------------------------------------------------------------------------------- /apps/web/src/lib/store/idb/verified.ts: -------------------------------------------------------------------------------- 1 | import { LocalIDBStore } from "@tape.xyz/lens/custom-types"; 2 | import { create } from "zustand"; 3 | import { persist } from "zustand/middleware"; 4 | 5 | import createIdbStorage from "@/lib/createIdbStorage"; 6 | 7 | interface State { 8 | verifiedProfiles: string[]; 9 | setVerifiedProfiles: (verifiedProfiles: string[]) => void; 10 | } 11 | 12 | const useVerifiedStore = create( 13 | persist<State>( 14 | (set) => ({ 15 | verifiedProfiles: [], 16 | setVerifiedProfiles: (verifiedProfiles) => set({ verifiedProfiles }) 17 | }), 18 | { 19 | name: LocalIDBStore.VERIFIED_STORE, 20 | storage: createIdbStorage() 21 | } 22 | ) 23 | ); 24 | 25 | export default useVerifiedStore; 26 | -------------------------------------------------------------------------------- /apps/web/src/lib/store/nonce.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface NonceState { 4 | lensHubOnchainSigNonce: number; 5 | setLensHubOnchainSigNonce: (nonce: number) => void; 6 | } 7 | 8 | const useNonceStore = create<NonceState>((set) => ({ 9 | lensHubOnchainSigNonce: 0, 10 | setLensHubOnchainSigNonce: (nonce: number) => 11 | set({ lensHubOnchainSigNonce: nonce }) 12 | })); 13 | 14 | export default useNonceStore; 15 | -------------------------------------------------------------------------------- /apps/web/src/lib/store/notification.ts: -------------------------------------------------------------------------------- 1 | import { create } from "zustand"; 2 | 3 | interface NotificationState { 4 | hasNewNotification: boolean; 5 | setHasNewNotification: (value: boolean) => void; 6 | } 7 | 8 | const useNotificationStore = create<NotificationState>((set) => ({ 9 | hasNewNotification: false, 10 | setHasNewNotification: (hasNewNotification) => set({ hasNewNotification }) 11 | })); 12 | 13 | export default useNotificationStore; 14 | -------------------------------------------------------------------------------- /apps/web/src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import "../styles/index.css"; 2 | 3 | import { tapeFont } from "@tape.xyz/browser/font"; 4 | import type { AppProps } from "next/app"; 5 | 6 | import Providers from "@/components/Common/Providers"; 7 | 8 | const App = ({ Component, pageProps }: AppProps) => { 9 | return ( 10 | <Providers> 11 | <style jsx global>{` 12 | html { 13 | font-family: ${tapeFont.style.fontFamily}; 14 | } 15 | `}</style> 16 | <Component {...pageProps} /> 17 | </Providers> 18 | ); 19 | }; 20 | 21 | export default App; 22 | -------------------------------------------------------------------------------- /apps/web/src/pages/bookmarks.tsx: -------------------------------------------------------------------------------- 1 | import Bookmarks from "@/components/Profile/Bookmarks"; 2 | 3 | export default Bookmarks; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/bytes/[id].tsx: -------------------------------------------------------------------------------- 1 | import Bytes from "@/components/Bytes"; 2 | 3 | export default Bytes; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/bytes/index.tsx: -------------------------------------------------------------------------------- 1 | import Bytes from "@/components/Bytes"; 2 | 3 | export default Bytes; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/create.tsx: -------------------------------------------------------------------------------- 1 | import CreateSteps from "@/components/Create"; 2 | 3 | export default CreateSteps; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/explore/[category].tsx: -------------------------------------------------------------------------------- 1 | import ExploreCategory from "@/components/Explore/Category"; 2 | 3 | export default ExploreCategory; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/explore/hashtag/[hashtag].tsx: -------------------------------------------------------------------------------- 1 | import ExploreHashtag from "@/components/Explore/Hashtag"; 2 | 3 | export default ExploreHashtag; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/explore/index.tsx: -------------------------------------------------------------------------------- 1 | import Explore from "@/components/Explore"; 2 | 3 | export default Explore; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/feed.tsx: -------------------------------------------------------------------------------- 1 | import Feed from "@/components/Feed"; 2 | 3 | export default Feed; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import Home from "@/components/Home"; 2 | 3 | export default Home; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/mod.tsx: -------------------------------------------------------------------------------- 1 | import Mod from "@/components/Mod"; 2 | 3 | export default Mod; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/notifications.tsx: -------------------------------------------------------------------------------- 1 | import Notifications from "@/components/Notifications"; 2 | 3 | const notifications = () => { 4 | return <Notifications />; 5 | }; 6 | 7 | export default notifications; 8 | -------------------------------------------------------------------------------- /apps/web/src/pages/profile/[id].tsx: -------------------------------------------------------------------------------- 1 | import Profile from "@/components/Profile"; 2 | 3 | export default Profile; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/allowance.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/blocked.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/danger.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/follow.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/handles.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/index.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/interests.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/manager.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/settings/sessions.tsx: -------------------------------------------------------------------------------- 1 | import Settings from "@/components/Settings"; 2 | 3 | export default Settings; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/thanks.tsx: -------------------------------------------------------------------------------- 1 | import Thanks from "@/components/Thanks"; 2 | 3 | export default Thanks; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/u/[[...handle]].tsx: -------------------------------------------------------------------------------- 1 | import ViewProfile from "@/components/Profile"; 2 | 3 | export default ViewProfile; 4 | -------------------------------------------------------------------------------- /apps/web/src/pages/watch/[id].tsx: -------------------------------------------------------------------------------- 1 | import VideoDetails from "@/components/Watch"; 2 | 3 | export default VideoDetails; 4 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const base = require("@tape.xyz/ui/tailwind-preset"); 2 | 3 | /** @type {import ('tailwindcss').Config} */ 4 | module.exports = { 5 | ...base, 6 | content: ["./src/**/*.{ts,tsx}", "../../packages/ui/src/**/*.{ts,tsx}"], 7 | plugins: [ 8 | require("@tailwindcss/aspect-ratio"), 9 | require("@tailwindcss/typography") 10 | ] 11 | }; 12 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/nextjs.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "noEmit": true, 6 | "isolatedModules": true, 7 | "strictNullChecks": true, 8 | "paths": { 9 | "@/*": ["./src/*"] 10 | }, 11 | "plugins": [ 12 | { 13 | "name": "next" 14 | } 15 | ] 16 | }, 17 | "include": [ 18 | "next-env.d.ts", 19 | "**/*.ts", 20 | "**/*.tsx", 21 | "src/styles/index.css", 22 | ".next/types/**/*.ts" 23 | ], 24 | "exclude": ["node_modules"] 25 | } 26 | -------------------------------------------------------------------------------- /packages/abis/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./src/LensHubProxy"; 2 | export * from "./src/LensPermissionlessCreator"; 3 | export * from "./src/TapeSignupProxy"; 4 | -------------------------------------------------------------------------------- /packages/abis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tape.xyz/abis", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "AGPL-3.0", 6 | "main": "index.ts", 7 | "scripts": { 8 | "typecheck": "tsc --pretty --noEmit" 9 | }, 10 | "dependencies": { 11 | "typescript": "^5.7.3" 12 | }, 13 | "devDependencies": { 14 | "@tape.xyz/tsconfig": "workspace:*", 15 | "@types/node": "^22.13.1" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/abis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/browser/font.ts: -------------------------------------------------------------------------------- 1 | import localFont from "next/font/local"; 2 | 3 | export const tapeFont = localFont({ 4 | src: [ 5 | { 6 | path: "./font/Satoshi-Regular.woff2", 7 | weight: "400" 8 | }, 9 | { 10 | path: "./font/Satoshi-Medium.woff2", 11 | weight: "500" 12 | }, 13 | { 14 | path: "./font/Satoshi-Bold.woff2", 15 | weight: "700" 16 | } 17 | ], 18 | fallback: ["system-ui", "sans-serif"], 19 | preload: true, 20 | variable: "--font-tape" 21 | }); 22 | -------------------------------------------------------------------------------- /packages/browser/font/Satoshi-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/packages/browser/font/Satoshi-Bold.woff2 -------------------------------------------------------------------------------- /packages/browser/font/Satoshi-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/packages/browser/font/Satoshi-Medium.woff2 -------------------------------------------------------------------------------- /packages/browser/font/Satoshi-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tapexyz/tape/84bd954b5f90570d0c1cc7a40e95e6f1cb62c0d1/packages/browser/font/Satoshi-Regular.woff2 -------------------------------------------------------------------------------- /packages/browser/functions/fingerprint.ts: -------------------------------------------------------------------------------- 1 | import { getCurrentBrowserFingerPrint } from "@rajesh896/broprint.js"; 2 | import { LocalStore } from "@tape.xyz/lens/custom-types"; 3 | 4 | const getFingerprint = async () => { 5 | const fingerprint = await getCurrentBrowserFingerPrint(); 6 | return fingerprint; 7 | }; 8 | 9 | export const setFingerprint = async () => { 10 | const storedFingerprint = localStorage.getItem(LocalStore.TAPE_FINGERPRINT); 11 | if (!storedFingerprint) { 12 | const fingerprint = await getFingerprint(); 13 | if (fingerprint) { 14 | localStorage.setItem(LocalStore.TAPE_FINGERPRINT, fingerprint); 15 | } 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /packages/browser/functions/getUserLocale.ts: -------------------------------------------------------------------------------- 1 | export const getUserLocale = () => { 2 | const locale = navigator?.languages?.length 3 | ? navigator.languages[0] 4 | : navigator.language; 5 | 6 | return locale || "en"; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/browser/functions/tw.ts: -------------------------------------------------------------------------------- 1 | import type { ClassValue } from "clsx"; 2 | import { clsx } from "clsx"; 3 | import { twMerge } from "tailwind-merge"; 4 | 5 | export const tw = (...inputs: ClassValue[]) => { 6 | return twMerge(clsx(inputs)); 7 | }; 8 | -------------------------------------------------------------------------------- /packages/browser/hooks/useAverageColor.ts: -------------------------------------------------------------------------------- 1 | import { FastAverageColor } from "fast-average-color"; 2 | import { useCallback, useEffect, useState } from "react"; 3 | 4 | const fac = new FastAverageColor(); 5 | 6 | export const useAverageColor = (src: string, check: boolean) => { 7 | const [color, setColor] = useState(""); 8 | 9 | const getColors = useCallback(() => { 10 | if (!check || !src) { 11 | return; 12 | } 13 | return fac 14 | .getColorAsync(src) 15 | .then((color) => { 16 | setColor(color.hex); 17 | }) 18 | .catch(() => {}); 19 | }, [src, check]); 20 | 21 | useEffect(() => { 22 | getColors(); 23 | }, [getColors]); 24 | 25 | return { color }; 26 | }; 27 | -------------------------------------------------------------------------------- /packages/browser/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from "react"; 2 | 3 | export function useDebounce<T>(value: T, delay?: number): T { 4 | const [debouncedValue, setDebouncedValue] = useState<T>(value); 5 | 6 | useEffect(() => { 7 | const timer = setTimeout(() => setDebouncedValue(value), delay || 500); 8 | 9 | return () => { 10 | clearTimeout(timer); 11 | }; 12 | }, [value, delay]); 13 | 14 | return debouncedValue; 15 | } 16 | -------------------------------------------------------------------------------- /packages/browser/hooks/useDragAndDrop.ts: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | export const useDragAndDrop = () => { 4 | const [dragOver, setDragOver] = useState(false); 5 | const [fileDropError, setFileDropError] = useState(""); 6 | 7 | const onDragOver = (e: React.SyntheticEvent) => { 8 | e.preventDefault(); 9 | setDragOver(true); 10 | }; 11 | 12 | const onDragLeave = () => setDragOver(false); 13 | 14 | return { 15 | dragOver, 16 | setDragOver, 17 | onDragOver, 18 | onDragLeave, 19 | fileDropError, 20 | setFileDropError 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /packages/browser/hooks/useHorizontalScroll.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from "react"; 2 | 3 | export const useHorizontalScroll = () => { 4 | const elRef = useRef<HTMLDivElement>(null); 5 | useEffect(() => { 6 | const el = elRef.current; 7 | if (!el) { 8 | return; 9 | } 10 | const handleWheelEvent = (e: any) => { 11 | if (e.deltaY === 0) { 12 | return; 13 | } 14 | e.preventDefault(); 15 | el.scrollTo({ 16 | left: el.scrollLeft + e.deltaY, 17 | behavior: "smooth" 18 | }); 19 | }; 20 | el.addEventListener("wheel", handleWheelEvent); 21 | return () => el.removeEventListener("wheel", handleWheelEvent); 22 | }, []); 23 | return elRef; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/browser/hooks/useIsMounted.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useEffect, useRef } from "react"; 2 | 3 | export const useIsMounted = () => { 4 | const isMounted = useRef(false); 5 | 6 | useEffect(() => { 7 | isMounted.current = true; 8 | 9 | return () => { 10 | isMounted.current = false; 11 | }; 12 | }, []); 13 | 14 | return useCallback(() => isMounted.current, []); 15 | }; 16 | -------------------------------------------------------------------------------- /packages/browser/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./font"; 2 | export * from "./functions/fingerprint"; 3 | export * from "./functions/generateVideoThumbnails"; 4 | export * from "./functions/getFileFromDataURL"; 5 | export * from "./functions/getToastOptions"; 6 | export * from "./functions/getUserLocale"; 7 | export * from "./functions/livepeer"; 8 | export * from "./functions/tw"; 9 | export * from "./hooks/uploadToIPFS"; 10 | export * from "./hooks/useAverageColor"; 11 | export * from "./hooks/useCopyToClipboard"; 12 | export * from "./hooks/useDebounce"; 13 | export * from "./hooks/useDragAndDrop"; 14 | export * from "./hooks/useHorizontalScroll"; 15 | export * from "./hooks/useIsMounted"; 16 | export * from "./hooks/useOutsideClick"; 17 | -------------------------------------------------------------------------------- /packages/browser/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json", 3 | "compilerOptions": { 4 | "lib": ["DOM", "ESNext", "WebWorker"], 5 | "skipLibCheck": true 6 | }, 7 | "include": ["./worker/sw.ts"], 8 | "exclude": ["node_modules"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/browser/worker/build.mjs: -------------------------------------------------------------------------------- 1 | import esbuild from "esbuild"; 2 | 3 | esbuild.build({ 4 | allowOverwrite: true, 5 | bundle: true, 6 | entryPoints: ["worker/sw.ts"], 7 | format: "esm", 8 | minify: true, 9 | outfile: "../../apps/web/public/sw.js", 10 | platform: "browser", 11 | target: "es2020", 12 | define: { "process.env.NODE_ENV": '"production"' } 13 | }); 14 | -------------------------------------------------------------------------------- /packages/constants/auth-routes.ts: -------------------------------------------------------------------------------- 1 | // auth routes 2 | export const AUTH_ROUTES = [ 3 | "/create", 4 | "/settings", 5 | "/feed", 6 | "/settings/follow", 7 | "/settings/handles", 8 | "/settings/sessions", 9 | "/settings/allowance", 10 | "/settings/interests", 11 | "/settings/danger", 12 | "/settings/manager", 13 | "/settings/blocked", 14 | "/notifications" 15 | ]; 16 | 17 | export const OWNER_ONLY_ROUTES = ["/settings/danger", "/settings/manager"]; 18 | -------------------------------------------------------------------------------- /packages/constants/endpoints.ts: -------------------------------------------------------------------------------- 1 | export enum LensEndpoint { 2 | Mainnet = "https://api-v2.lens.dev", 3 | Staging = "https://api.staging.lens.dev/graphql", 4 | Testnet = "https://api.testnet.lens.dev/graphql" 5 | } 6 | -------------------------------------------------------------------------------- /packages/constants/feature-flags.ts: -------------------------------------------------------------------------------- 1 | import { IS_MAINNET } from "./general"; 2 | 3 | export enum FEATURE_FLAGS { 4 | POST_WITH_SOURCE_URL = "PostWithSource", 5 | TAPE_CONNECT = "TapeConnect" 6 | } 7 | 8 | type FeatureFlag = { 9 | flag: string; 10 | enabledFor: string[]; 11 | }; 12 | 13 | export const featureFlags: FeatureFlag[] = [ 14 | { 15 | flag: FEATURE_FLAGS.POST_WITH_SOURCE_URL, 16 | enabledFor: IS_MAINNET ? ["0x2d"] : [] 17 | }, 18 | { 19 | flag: FEATURE_FLAGS.TAPE_CONNECT, 20 | enabledFor: IS_MAINNET ? ["0x2d", "0xc001"] : [] 21 | } 22 | ]; 23 | -------------------------------------------------------------------------------- /packages/constants/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./auth-routes"; 2 | export * from "./categories"; 3 | export * from "./endpoints"; 4 | export * from "./feature-flags"; 5 | export * from "./general"; 6 | export * from "./misused"; 7 | export * from "./regex"; 8 | export * from "./suspended"; 9 | -------------------------------------------------------------------------------- /packages/constants/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tape.xyz/constants", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "AGPL-3.0", 6 | "main": "index.ts", 7 | "scripts": { 8 | "typecheck": "tsc --pretty --noEmit" 9 | }, 10 | "devDependencies": { 11 | "@tape.xyz/tsconfig": "workspace:*", 12 | "@types/node": "^22.13.1", 13 | "typescript": "^5.7.3" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/constants/regex.ts: -------------------------------------------------------------------------------- 1 | export const COMMON_REGEX = { 2 | ZORA: /https:\/\/(?:testnet\.)?zora\.co\/collect\/(eth|oeth|base|zora|gor|ogor|basegor|zgor):(0x[\dA-Fa-f]{40})((?:\/(\d+))?|$)/, 3 | MENTION_MATCHER_REGEX: /(@[a-zA-Z0-9-_.]+(?:\/[a-zA-Z0-9-_.]+)?)/, 4 | HANDLE: /^[\dA-Za-z]\w{4,25}$/g, 5 | TAPE_WATCH: 6 | /^https?:\/\/tape\.xyz\/watch\/([\dA-Za-z-]+)(\?si=[\dA-Za-z]+)?$/, 7 | YOUTUBE_WATCH: 8 | /^https?:\/\/(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/|youtube\.com\/embed\/)[\w-]+(?:\?.*)?$/, 9 | VIMEO_WATCH: /^https?:\/\/(?:www\.)?vimeo\.com\/[\d]+(?:\?.*)?$/ 10 | }; 11 | -------------------------------------------------------------------------------- /packages/constants/suspended.ts: -------------------------------------------------------------------------------- 1 | import { IS_MAINNET } from "./general"; 2 | 3 | export const SUSPENDED_PROFILES = IS_MAINNET 4 | ? [ 5 | "0x084311", 6 | "0x03eee1", 7 | "0x6e64", 8 | "0x79f2", 9 | "0x4e24", 10 | "0x923a", 11 | "0x99", 12 | "0x9b5d", 13 | "0x9cb4", 14 | "0x8441", 15 | "0x0dc2", 16 | "0x02a0b4" 17 | ] 18 | : []; 19 | -------------------------------------------------------------------------------- /packages/constants/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/generic/functions/canUploadedToIpfs.ts: -------------------------------------------------------------------------------- 1 | import { IPFS_FREE_UPLOAD_LIMIT } from "@tape.xyz/constants"; 2 | import type { Profile } from "@tape.xyz/lens"; 3 | 4 | export const canUploadedToIpfs = ( 5 | bytes: number, 6 | activeProfile: Profile | null 7 | ) => { 8 | if (!activeProfile || bytes === null || bytes === undefined) { 9 | return false; 10 | } 11 | 12 | // Calculate the size of the file in megabytes 13 | const megaBytes = bytes / 1024 ** 2; 14 | 15 | // Check if the file size is within the allowed limit 16 | return megaBytes < IPFS_FREE_UPLOAD_LIMIT; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/generic/functions/checkIsBytesVideo.ts: -------------------------------------------------------------------------------- 1 | export const checkIsBytesVideo = (durationInSeconds: number) => { 2 | const durationInMinutes = durationInSeconds / 60; 3 | if (durationInMinutes < 2) { 4 | return true; 5 | } 6 | return false; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/generic/functions/checkLensManagerPermissions.ts: -------------------------------------------------------------------------------- 1 | import type { Profile } from "@tape.xyz/lens"; 2 | 3 | export const checkLensManagerPermissions = ( 4 | profile: Profile | null 5 | ): { 6 | canBroadcast: boolean; 7 | canUseLensManager: boolean; 8 | } => { 9 | if (!profile) { 10 | return { canBroadcast: false, canUseLensManager: false }; 11 | } 12 | const canUseLensManager = profile?.signless && profile?.sponsor; 13 | const canBroadcast = profile?.sponsor; 14 | return { canBroadcast, canUseLensManager }; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/generic/functions/formatBytes.ts: -------------------------------------------------------------------------------- 1 | export const formatBytes = (bytes: number) => { 2 | if (bytes && bytes > 0) { 3 | const sizes = ["Bytes", "KB", "MB", "GB", "TB"]; 4 | const i = Math.min( 5 | Number.parseInt( 6 | Math.floor(Math.log(bytes) / Math.log(1024)).toString(), 7 | 10 8 | ), 9 | sizes.length - 1 10 | ); 11 | return `${Math.round(bytes / 1024 ** i)} ${sizes[i]}`; 12 | } 13 | return "n/a"; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/generic/functions/formatMB.ts: -------------------------------------------------------------------------------- 1 | export const formatMB = (sizeInMB: number) => { 2 | if (sizeInMB >= 1024) { 3 | // Convert MB to GB if size is greater than or equal to 1 GB 4 | const sizeInGB = sizeInMB / 1024; 5 | return `${Math.floor(sizeInGB)} GB`; 6 | } 7 | return `${sizeInMB} MB`; 8 | }; 9 | -------------------------------------------------------------------------------- /packages/generic/functions/formatNumber.ts: -------------------------------------------------------------------------------- 1 | export const formatNumber = (num: number) => { 2 | let numberToFormat = num; 3 | if (numberToFormat < 0) { 4 | numberToFormat = Math.abs(num); 5 | } 6 | if (numberToFormat > 999 && numberToFormat < 1000000) { 7 | return `${(numberToFormat / 1000).toPrecision(3)}k`; 8 | } 9 | if (numberToFormat > 1000000) { 10 | return `${(numberToFormat / 1000000).toPrecision(3)}m`; 11 | } 12 | if (numberToFormat < 1000) { 13 | return numberToFormat; 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /packages/generic/functions/get-profile-cover.ts: -------------------------------------------------------------------------------- 1 | import type { ProfileMetadata } from "@tape.xyz/lens"; 2 | 3 | export const getProfileCoverPicture = ( 4 | metadata: ProfileMetadata, 5 | withFallback = false 6 | ): string => { 7 | return metadata?.coverPicture?.optimized?.uri 8 | ? metadata.coverPicture.optimized.uri 9 | : withFallback 10 | ? "ipfs://bafkreihn5v4hpuxgcysnpb4pgcerkmhwddxq65qswmit6j4nj44btyzdou" //`${STATIC_ASSETS}/images/fallback-cover.svg` 11 | : ""; 12 | }; 13 | -------------------------------------------------------------------------------- /packages/generic/functions/getCategoryName.ts: -------------------------------------------------------------------------------- 1 | import { TAPE_MEDIA_CATEGORIES } from "@tape.xyz/constants"; 2 | 3 | export const getCategoryName = (tag: string) => { 4 | if (!tag) { 5 | return null; 6 | } 7 | return TAPE_MEDIA_CATEGORIES.find((c) => c.tag === tag)?.name; 8 | }; 9 | 10 | export const getCategoryByTag = (tag: string) => { 11 | return TAPE_MEDIA_CATEGORIES.find( 12 | (c) => c.tag === tag 13 | ) as (typeof TAPE_MEDIA_CATEGORIES)[0]; 14 | }; 15 | -------------------------------------------------------------------------------- /packages/generic/functions/getFromAttributes.ts: -------------------------------------------------------------------------------- 1 | import type { MetadataAttribute } from "@tape.xyz/lens"; 2 | 3 | // key available only profile metadata 4 | export const getValueFromKeyInAttributes = ( 5 | attributes: MetadataAttribute[] | null | undefined, 6 | key: string 7 | ) => { 8 | return attributes?.find((el) => el.key === key)?.value ?? ""; 9 | }; 10 | -------------------------------------------------------------------------------- /packages/generic/functions/getIsFeatureEnabled.ts: -------------------------------------------------------------------------------- 1 | import type { FEATURE_FLAGS } from "@tape.xyz/constants"; 2 | import { featureFlags } from "@tape.xyz/constants"; 3 | 4 | export const getIsFeatureEnabled = (flag: FEATURE_FLAGS, profileId: string) => { 5 | if (!profileId) { 6 | return false; 7 | } 8 | const feature = featureFlags.find((f) => f.flag === flag); 9 | return feature?.enabledFor.includes(profileId); 10 | }; 11 | -------------------------------------------------------------------------------- /packages/generic/functions/getIsProfileOwner.ts: -------------------------------------------------------------------------------- 1 | import type { Profile } from "@tape.xyz/lens"; 2 | 3 | import { getProfile } from "./get-profile"; 4 | 5 | export const getIsProfileOwner = ( 6 | profile: Profile, 7 | address: string | undefined 8 | ) => { 9 | return getProfile(profile)?.address?.toLowerCase() === address?.toLowerCase(); 10 | }; 11 | -------------------------------------------------------------------------------- /packages/generic/functions/getIsSensitiveContent.ts: -------------------------------------------------------------------------------- 1 | import type { PublicationMetadata } from "@tape.xyz/lens"; 2 | 3 | export const getIsSensitiveContent = ( 4 | metadata: PublicationMetadata | null 5 | ): boolean => { 6 | return ( 7 | Boolean(metadata?.attributes?.find((el) => el.value === "sensitive")) || 8 | Boolean(metadata?.contentWarning) 9 | ); 10 | }; 11 | -------------------------------------------------------------------------------- /packages/generic/functions/getIsSuspendedProfile.ts: -------------------------------------------------------------------------------- 1 | import { SUSPENDED_PROFILES } from "@tape.xyz/constants"; 2 | 3 | export const getIsSuspendedProfile = (profileId: string): boolean => { 4 | return SUSPENDED_PROFILES.includes(profileId); 5 | }; 6 | -------------------------------------------------------------------------------- /packages/generic/functions/getLennyPicture.ts: -------------------------------------------------------------------------------- 1 | import { IS_MAINNET, WORKER_AVATAR_URL } from "@tape.xyz/constants"; 2 | 3 | export const getLennyPicture = (profileId: string) => { 4 | return IS_MAINNET 5 | ? `${WORKER_AVATAR_URL}/${profileId}` 6 | : `https://cdn.stamp.fyi/avatar/${profileId}?s=300`; 7 | }; 8 | -------------------------------------------------------------------------------- /packages/generic/functions/getMetadataCid.ts: -------------------------------------------------------------------------------- 1 | import type { AnyPublication } from "@tape.xyz/lens"; 2 | 3 | import { getPublication } from "./getPublication"; 4 | 5 | export const getMetadataCid = (publication: AnyPublication): string => { 6 | const target = getPublication(publication); 7 | const hash = target.metadata.rawURI.split("/").pop(); 8 | return hash ?? ""; 9 | }; 10 | -------------------------------------------------------------------------------- /packages/generic/functions/getShouldUploadVideo.ts: -------------------------------------------------------------------------------- 1 | import type { PrimaryPublication, VideoMetadataV3 } from "@tape.xyz/lens"; 2 | 3 | export const getShouldUploadVideo = (video: PrimaryPublication): boolean => { 4 | const metadata = video.metadata as VideoMetadataV3; 5 | // Define the time threshold for video optimization (4 hours ago) 6 | const fourHoursAgo = new Date(Date.now() - 4 * 60 * 60 * 1000); 7 | const createdAt = new Date(video.createdAt); 8 | 9 | // Check if the video's URI is not optimized (not ending with .m3u8) 10 | const isNotOptimized = 11 | !metadata.asset.video.optimized?.uri?.endsWith(".m3u8"); 12 | 13 | // Return true if the video is older than 4 hours and not optimized 14 | return createdAt < fourHoursAgo && isNotOptimized; 15 | }; 16 | -------------------------------------------------------------------------------- /packages/generic/functions/getUploadedMediaType.ts: -------------------------------------------------------------------------------- 1 | import { MediaVideoMimeType } from "@lens-protocol/metadata"; 2 | 3 | export const getUploadedMediaType = (mimeType: string): MediaVideoMimeType => { 4 | switch (mimeType) { 5 | case "video/mp4": 6 | return MediaVideoMimeType.MP4; 7 | case "video/mpeg": 8 | return MediaVideoMimeType.MPEG; 9 | case "video/webm": 10 | return MediaVideoMimeType.WEBM; 11 | case "video/quicktime": 12 | return MediaVideoMimeType.QUICKTIME; 13 | case "video/mov": 14 | return MediaVideoMimeType.MOV; 15 | default: 16 | return MediaVideoMimeType.MP4; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /packages/generic/functions/image-cdn.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IMAGEKIT_URL, 3 | IMAGE_TRANSFORMATIONS, 4 | IS_PRODUCTION 5 | } from "@tape.xyz/constants"; 6 | 7 | export const imageCdn = ( 8 | url: string, 9 | type?: keyof typeof IMAGE_TRANSFORMATIONS 10 | ): string => { 11 | if (!url) { 12 | return url; 13 | } 14 | 15 | return IS_PRODUCTION 16 | ? type 17 | ? `${IMAGEKIT_URL}/${IMAGE_TRANSFORMATIONS[type]}/${url}` 18 | : `${IMAGEKIT_URL}/${url}` 19 | : url; 20 | }; 21 | -------------------------------------------------------------------------------- /packages/generic/functions/isWatchable.ts: -------------------------------------------------------------------------------- 1 | import type { MirrorablePublication } from "@tape.xyz/lens"; 2 | 3 | export const isWatchable = (publication: MirrorablePublication) => { 4 | const canWatch = 5 | publication && 6 | (publication.metadata.__typename === "VideoMetadataV3" || 7 | publication.metadata.__typename === "LiveStreamMetadataV3") && 8 | !publication?.isHidden; 9 | 10 | return canWatch; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/generic/functions/omitKey.ts: -------------------------------------------------------------------------------- 1 | export const omitKey = (object: { [key: string]: any }, key: string) => { 2 | const cloned = { ...object }; // cloning to fix a reference issue recently after v13 3 | delete cloned[key]; 4 | return cloned; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/generic/functions/parse-jwt.ts: -------------------------------------------------------------------------------- 1 | type ReturnType = { 2 | id: string; 3 | role: string; 4 | authorizationId: string; 5 | iat: number; 6 | exp: number; 7 | }; 8 | 9 | const hasBuffer = typeof Buffer === "undefined"; 10 | const decoded = (str: string): string => 11 | hasBuffer ? atob(str) : Buffer.from(str, "base64").toString("binary"); 12 | 13 | export const parseJwt = (token: string): ReturnType => { 14 | try { 15 | const splited = token.split(".")[1] ?? ""; 16 | return JSON.parse(decoded(splited)); 17 | } catch { 18 | return { id: "", role: "", authorizationId: "", iat: 0, exp: 0 }; 19 | } 20 | }; 21 | 22 | export const isTokenExpired = (token: string) => { 23 | const { exp } = parseJwt(token); 24 | return Date.now() >= exp * 1000; 25 | }; 26 | -------------------------------------------------------------------------------- /packages/generic/functions/resolveDid.ts: -------------------------------------------------------------------------------- 1 | import { WORKER_DID_URL } from "@tape.xyz/constants"; 2 | import axios from "axios"; 3 | 4 | export const resolveDid = async (addresses: string[]) => { 5 | try { 6 | const response = await axios.post(WORKER_DID_URL, { 7 | addresses: addresses.map((address) => address.split("/")[0]) 8 | }); 9 | return response.data.dids; 10 | } catch { 11 | return []; 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /packages/generic/functions/sanitizeDStorageUrl.ts: -------------------------------------------------------------------------------- 1 | import { IPFS_GATEWAY_URL, IRYS_GATEWAY_URL } from "@tape.xyz/constants"; 2 | 3 | export const sanitizeDStorageUrl = (url: string) => { 4 | const ipfsGateway = `${IPFS_GATEWAY_URL}/`; 5 | const irysGateway = `${IRYS_GATEWAY_URL}/`; 6 | if (!url) { 7 | return url; 8 | } 9 | 10 | return url 11 | .replace(/^Qm[1-9A-Za-z]{44}/gm, `${ipfsGateway}/${url}`) 12 | .replace("https://ipfs.io/ipfs/", ipfsGateway) 13 | .replace("https://ipfs.infura.io/ipfs/", ipfsGateway) 14 | .replace("https://gateway.pinata.cloud/ipfs/", ipfsGateway) 15 | .replace("https://gw.ipfs-lens.dev/ipfs/", ipfsGateway) 16 | .replace("ipfs://ipfs/", ipfsGateway) 17 | .replace("ar://", irysGateway) 18 | .replace("ipfs://", ipfsGateway); 19 | }; 20 | -------------------------------------------------------------------------------- /packages/generic/functions/shortenAddress.ts: -------------------------------------------------------------------------------- 1 | export const shortenAddress = (address: string, chars = 4): string => { 2 | return `${address?.substring(0, chars + 2)}...${address.substring( 3 | 42 - chars 4 | )}`; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/generic/functions/splitNumber.ts: -------------------------------------------------------------------------------- 1 | export const splitNumber = (num = 1, parts = 1) => { 2 | const n = Math.floor(num / parts); 3 | const numbers: number[] = []; 4 | for (let i = 0; i < parts; i++) { 5 | numbers.push(n); 6 | } 7 | if (numbers.reduce((a, b) => a + b, 0) === num) { 8 | return numbers; 9 | } 10 | for (let i = 0; i < parts; i++) { 11 | numbers[i]!++; 12 | if (numbers.reduce((a, b) => a + b, 0) === num) { 13 | return numbers; 14 | } 15 | } 16 | return numbers; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/generic/functions/trim-new-lines.ts: -------------------------------------------------------------------------------- 1 | export const trimNewLines = (text: string) => { 2 | if (!text) { 3 | return text; 4 | } 5 | return text.replaceAll("\n", ""); 6 | }; 7 | -------------------------------------------------------------------------------- /packages/generic/functions/trimify.ts: -------------------------------------------------------------------------------- 1 | export const trimify = (value: string): string => value?.trim(); 2 | -------------------------------------------------------------------------------- /packages/generic/functions/truncate.ts: -------------------------------------------------------------------------------- 1 | export const truncate = (str: string, max: number, suffix = "...") => { 2 | if (!str) { 3 | return ""; 4 | } 5 | return str.length < max 6 | ? str 7 | : `${str.substring( 8 | 0, 9 | str.substring(0, max - suffix.length).lastIndexOf(" ") 10 | )}${suffix}`; 11 | }; 12 | -------------------------------------------------------------------------------- /packages/generic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tape.xyz/generic", 3 | "version": "0.0.0", 4 | "license": "AGPL-3.0", 5 | "main": "index.ts", 6 | "scripts": { 7 | "typecheck": "tsc --pretty --noEmit" 8 | }, 9 | "dependencies": { 10 | "@lens-protocol/metadata": "^1.2.0", 11 | "axios": "^1.8.4", 12 | "viem": "^2.23.14" 13 | }, 14 | "devDependencies": { 15 | "@tape.xyz/constants": "workspace:*", 16 | "@tape.xyz/lens": "workspace:*", 17 | "@tape.xyz/tsconfig": "workspace:*", 18 | "@types/node": "^22.13.1", 19 | "typescript": "^5.7.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/generic/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/indexer/codegen.ts: -------------------------------------------------------------------------------- 1 | import type { CodegenConfig } from "@graphql-codegen/cli"; 2 | import { LensEndpoint } from "@tape.xyz/constants"; 3 | 4 | const config: CodegenConfig = { 5 | schema: LensEndpoint.Testnet, 6 | documents: "documents/**/*.graphql", 7 | generates: { 8 | "gql/generated/": { 9 | preset: "client", 10 | config: { 11 | documentMode: "string" 12 | } 13 | } 14 | }, 15 | hooks: { 16 | afterAllFileWrite: ["biome format --write ."] 17 | } 18 | }; 19 | 20 | export default config; 21 | -------------------------------------------------------------------------------- /packages/indexer/custom-types.ts: -------------------------------------------------------------------------------- 1 | export enum AUTH_CHALLENGE_TYPE { 2 | ONBOARDING_USER = "ONBOARDING_USER", 3 | ACCOUNT_OWNER = "ACCOUNT_OWNER", 4 | ACCOUNT_MANAGER = "ACCOUNT_MANAGER" 5 | } 6 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/account-manager-permissions.graphql: -------------------------------------------------------------------------------- 1 | fragment AccountManagerPermissions on AccountManagerPermissions { 2 | canExecuteTransactions 3 | canSetMetadataUri 4 | canTransferNative 5 | canTransferTokens 6 | } 7 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/account/account-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment AccountFields on Account { 2 | owner 3 | address 4 | createdAt 5 | rules { 6 | anyOf { 7 | ...AccountFollowRuleFields 8 | } 9 | required { 10 | ...AccountFollowRuleFields 11 | } 12 | } 13 | metadata { 14 | ...AccountMetadataFields 15 | } 16 | username(request: { autoResolve: true }) { 17 | ...UsernameFields 18 | } 19 | operations { 20 | ...LoggedInAccountOperationsFields 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/account/account-follow-rule-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment AccountFollowRuleFields on AccountFollowRule { 2 | id 3 | type 4 | address 5 | config { 6 | ...AnyKeyValueFields 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/account/account-metadata-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment AccountMetadataFields on AccountMetadata { 2 | id 3 | name 4 | bio 5 | picture 6 | coverPicture 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/account/logged-in-account-operations-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment LoggedInAccountOperationsFields on LoggedInAccountOperations { 2 | id 3 | isFollowedByMe 4 | isFollowingMe 5 | isMutedByMe 6 | isBlockedByMe 7 | } 8 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/account/username-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment UsernameFields on Username { 2 | namespace 3 | localName 4 | linkedTo 5 | value 6 | } 7 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/any-key-value-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment AnyKeyValueFields on AnyKeyValue { 2 | ... on AddressKeyValue { 3 | key 4 | address 5 | } 6 | ... on BigDecimalKeyValue { 7 | key 8 | bigDecimal 9 | } 10 | ... on StringKeyValue { 11 | key 12 | string 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/app-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment AppFields on App { 2 | address 3 | defaultFeedAddress 4 | graphAddress 5 | namespaceAddress 6 | sponsorshipAddress 7 | treasuryAddress 8 | createdAt 9 | metadata { 10 | description 11 | developer 12 | logo 13 | name 14 | platforms 15 | privacyPolicy 16 | termsOfService 17 | url 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/boolean-value-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment BooleanValueFields on BooleanValue { 2 | onChain 3 | optimistic 4 | } 5 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/erc20-amount-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment Erc20AmountFields on Erc20Amount { 2 | asset { 3 | ...Erc20Fields 4 | } 5 | value 6 | } 7 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/erc20-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment Erc20Fields on Erc20 { 2 | contract { 3 | address 4 | chainId 5 | } 6 | decimals 7 | name 8 | symbol 9 | } 10 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/metadata-attribute-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MetadataAttributeFields on MetadataAttribute { 2 | type 3 | key 4 | value 5 | } 6 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/network-address-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment NetworkAddressFields on NetworkAddress { 2 | address 3 | chainId 4 | } 5 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/notifications/comment-notification-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment CommentNotificationFields on CommentNotification { 2 | __typename 3 | id 4 | comment { 5 | ...PostFields 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/notifications/follow-notification-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment FollowNotificationFields on FollowNotification { 2 | __typename 3 | id 4 | followers { 5 | account { 6 | ...AccountFields 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/notifications/mention-notification-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MentionNotificationFields on MentionNotification { 2 | __typename 3 | id 4 | post { 5 | ...PostFields 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/notifications/quote-notification-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment QuoteNotificationFields on QuoteNotification { 2 | __typename 3 | id 4 | quote { 5 | ...PostFields 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/notifications/reaction-notification-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment ReactionNotificationFields on ReactionNotification { 2 | __typename 3 | id 4 | post { 5 | ...PostFields 6 | } 7 | reactions { 8 | account { 9 | ...AccountFields 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/notifications/repost-notification-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment RepostNotificationFields on RepostNotification { 2 | __typename 3 | id 4 | post { 5 | ...PostFields 6 | } 7 | reposts { 8 | account { 9 | ...AccountFields 10 | } 11 | repostedAt 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/collect/pay-to-collect-config-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PayToCollectConfigFields on PayToCollectConfig { 2 | referralShare 3 | recipients { 4 | address 5 | percent 6 | } 7 | amount { 8 | ...Erc20AmountFields 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/collect/simple-collect-action-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment SimpleCollectActionFields on SimpleCollectAction { 2 | address 3 | collectLimit 4 | isImmutable 5 | endsAt 6 | payToCollect { 7 | ...PayToCollectConfigFields 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/collect/unknown-action-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment UnknownPostActionFields on UnknownPostAction { 2 | __typename 3 | } 4 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/logged-in-post-operations-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment LoggedInPostOperationsFields on LoggedInPostOperations { 2 | id 3 | hasBookmarked 4 | hasReacted 5 | hasSimpleCollected 6 | hasTipped 7 | isNotInterested 8 | hasCommented { 9 | ...BooleanValueFields 10 | } 11 | hasQuoted { 12 | ...BooleanValueFields 13 | } 14 | hasReposted { 15 | ...BooleanValueFields 16 | } 17 | canRepost { 18 | __typename 19 | } 20 | canQuote { 21 | __typename 22 | } 23 | canComment { 24 | __typename 25 | } 26 | simpleCollectCount 27 | postTipCount 28 | } 29 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/metadata/media/media-audio-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MediaAudioFields on MediaAudio { 2 | artist 3 | item 4 | cover 5 | license 6 | } 7 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/metadata/media/media-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MediaFields on AnyMedia { 2 | ... on MediaVideo { 3 | ...MediaVideoFields 4 | } 5 | ... on MediaImage { 6 | ...MediaImageFields 7 | } 8 | ... on MediaAudio { 9 | ...MediaAudioFields 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/metadata/media/media-image-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MediaImageFields on MediaImage { 2 | altTag 3 | attributes { 4 | ...MetadataAttributeFields 5 | } 6 | item 7 | license 8 | } 9 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/metadata/media/media-video-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MediaVideoFields on MediaVideo { 2 | altTag 3 | attributes { 4 | ...MetadataAttributeFields 5 | } 6 | cover 7 | duration 8 | item 9 | license 10 | } 11 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/metadata/video-metadata-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment VideoMetadataFields on VideoMetadata { 2 | __typename 3 | id 4 | title 5 | content 6 | tags 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | attachments { 11 | ...MediaFields 12 | } 13 | video { 14 | ...MediaVideoFields 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/post-action-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PostActionFields on PostAction { 2 | ... on SimpleCollectAction { 3 | ...SimpleCollectActionFields 4 | } 5 | ... on UnknownPostAction { 6 | ...UnknownPostActionFields 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/post-base-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PostBaseFields on Post { 2 | __typename 3 | id 4 | slug 5 | isEdited 6 | isDeleted 7 | timestamp 8 | author { 9 | ...AccountFields 10 | } 11 | feed { 12 | group { 13 | address 14 | metadata { 15 | name 16 | description 17 | icon 18 | coverPicture 19 | } 20 | } 21 | } 22 | app { 23 | ...AppFields 24 | } 25 | metadata { 26 | ...PostMetadataFields 27 | } 28 | actions { 29 | ...PostActionFields 30 | } 31 | stats { 32 | ...PostStatsFields 33 | } 34 | operations { 35 | ...LoggedInPostOperationsFields 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/post-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PostFields on Post { 2 | ...PostBaseFields 3 | root { 4 | ...PostBaseFields 5 | } 6 | commentOn { 7 | ...PostBaseFields 8 | } 9 | quoteOf { 10 | ...PostBaseFields 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/post-stats-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PostStatsFields on PostStats { 2 | bookmarks 3 | collects 4 | comments 5 | quotes 6 | reactions 7 | reposts 8 | } 9 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/post/repost-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment RepostFields on Repost { 2 | __typename 3 | id 4 | author { 5 | ...AccountFields 6 | } 7 | isDeleted 8 | timestamp 9 | repostOf { 10 | ...PostFields 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/self-funded-transaction-request-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment SelfFundedTransactionRequestFields on SelfFundedTransactionRequest { 2 | reason 3 | raw { 4 | chainId 5 | data 6 | from 7 | gasLimit 8 | maxFeePerGas 9 | maxPriorityFeePerGas 10 | nonce 11 | to 12 | type 13 | value 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/indexer/documents/fragments/sponsored-transaction-request-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment SponsoredTransactionRequestFields on SponsoredTransactionRequest { 2 | reason 3 | raw { 4 | chainId 5 | data 6 | from 7 | gasLimit 8 | maxFeePerGas 9 | maxPriorityFeePerGas 10 | nonce 11 | to 12 | type 13 | value 14 | customData { 15 | customSignature 16 | factoryDeps 17 | gasPerPubdata 18 | paymasterParams { 19 | paymaster 20 | paymasterInput 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/account/create-account.graphql: -------------------------------------------------------------------------------- 1 | mutation CreateAccountWithUsername( 2 | $request: CreateAccountWithUsernameRequest! 3 | ) { 4 | createAccountWithUsername(request: $request) { 5 | ... on UsernameTaken { 6 | reason 7 | } 8 | ... on CreateAccountResponse { 9 | hash 10 | } 11 | ... on NamespaceOperationValidationFailed { 12 | reason 13 | } 14 | ... on SelfFundedTransactionRequest { 15 | reason 16 | } 17 | ... on SponsoredTransactionRequest { 18 | reason 19 | } 20 | ... on TransactionWillFail { 21 | reason 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/auth/authenticate.graphql: -------------------------------------------------------------------------------- 1 | mutation Authenticate($request: SignedAuthChallenge!) { 2 | authenticate(request: $request) { 3 | ... on AuthenticationTokens { 4 | __typename 5 | accessToken 6 | refreshToken 7 | idToken 8 | } 9 | ... on ExpiredChallengeError { 10 | __typename 11 | reason 12 | } 13 | ... on ForbiddenError { 14 | __typename 15 | reason 16 | } 17 | ... on WrongSignerError { 18 | __typename 19 | reason 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/auth/challenge.graphql: -------------------------------------------------------------------------------- 1 | mutation Challenge($request: ChallengeRequest!) { 2 | challenge(request: $request) { 3 | id 4 | text 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/auth/refresh.graphql: -------------------------------------------------------------------------------- 1 | mutation Refresh($request: RefreshRequest!) { 2 | refresh(request: $request) { 3 | ... on AuthenticationTokens { 4 | __typename 5 | accessToken 6 | refreshToken 7 | idToken 8 | } 9 | ... on ForbiddenError { 10 | __typename 11 | reason 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/add-reaction.graphql: -------------------------------------------------------------------------------- 1 | mutation AddReaction($request: AddReactionRequest!) { 2 | addReaction(request: $request) { 3 | ... on AddReactionResponse { 4 | success 5 | } 6 | ... on AddReactionFailure { 7 | reason 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/bookmark-post.graphql: -------------------------------------------------------------------------------- 1 | mutation BookmarkPost($request: BookmarkPostRequest!) { 2 | bookmarkPost(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/create-post.graphql: -------------------------------------------------------------------------------- 1 | mutation CreatePost($request: CreatePostRequest!) { 2 | post(request: $request) { 3 | ... on PostResponse { 4 | hash 5 | } 6 | ... on SelfFundedTransactionRequest { 7 | ...SelfFundedTransactionRequestFields 8 | } 9 | ... on SponsoredTransactionRequest { 10 | ...SponsoredTransactionRequestFields 11 | } 12 | ... on TransactionWillFail { 13 | reason 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/delete-post.graphql: -------------------------------------------------------------------------------- 1 | mutation DeletePost($request: DeletePostRequest!) { 2 | deletePost(request: $request) { 3 | ... on DeletePostResponse { 4 | hash 5 | } 6 | ... on SelfFundedTransactionRequest { 7 | ...SelfFundedTransactionRequestFields 8 | } 9 | ... on SponsoredTransactionRequest { 10 | ...SponsoredTransactionRequestFields 11 | } 12 | ... on TransactionWillFail { 13 | reason 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/edit-post.graphql: -------------------------------------------------------------------------------- 1 | mutation EditPost($request: EditPostRequest!) { 2 | editPost(request: $request) { 3 | ... on PostResponse { 4 | hash 5 | } 6 | ... on SelfFundedTransactionRequest { 7 | ...SelfFundedTransactionRequestFields 8 | } 9 | ... on SponsoredTransactionRequest { 10 | ...SponsoredTransactionRequestFields 11 | } 12 | ... on TransactionWillFail { 13 | reason 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/report-post.graphql: -------------------------------------------------------------------------------- 1 | mutation ReportPost($request: ReportPostRequest!) { 2 | reportPost(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/repost.graphql: -------------------------------------------------------------------------------- 1 | mutation Repost($request: CreateRepostRequest!) { 2 | repost(request: $request) { 3 | ... on PostResponse { 4 | hash 5 | } 6 | ... on SelfFundedTransactionRequest { 7 | ...SelfFundedTransactionRequestFields 8 | } 9 | ... on SponsoredTransactionRequest { 10 | ...SponsoredTransactionRequestFields 11 | } 12 | ... on TransactionWillFail { 13 | reason 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/undo-bookmark.graphql: -------------------------------------------------------------------------------- 1 | mutation UndoBookmarkPost($request: BookmarkPostRequest!) { 2 | undoBookmarkPost(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/indexer/documents/mutations/post/undo-reaction.graphql: -------------------------------------------------------------------------------- 1 | mutation UndoReaction($request: UndoReactionRequest!) { 2 | undoReaction(request: $request) { 3 | ... on UndoReactionResponse { 4 | success 5 | } 6 | ... on UndoReactionFailure { 7 | reason 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/account-managers.graphql: -------------------------------------------------------------------------------- 1 | query AccountManagers($request: AccountManagersRequest!) { 2 | accountManagers(request: $request) { 3 | items { 4 | manager 5 | isLensManager 6 | permissions { 7 | ...AccountManagerPermissions 8 | } 9 | addedAt 10 | } 11 | pageInfo { 12 | next 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/account-stats.graphql: -------------------------------------------------------------------------------- 1 | query AccountStats($request: AccountStatsRequest!) { 2 | accountStats(request: $request) { 3 | feedStats { 4 | posts 5 | comments 6 | reposts 7 | quotes 8 | reacted 9 | reactions 10 | collects 11 | } 12 | graphFollowStats { 13 | followers 14 | following 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/account.graphql: -------------------------------------------------------------------------------- 1 | query Account($request: AccountRequest!) { 2 | account(request: $request) { 3 | ...AccountFields 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/accounts-available.graphql: -------------------------------------------------------------------------------- 1 | query AccountsAvailable( 2 | $accountsAvailableRequest: AccountsAvailableRequest! 3 | $lastLoggedInAccountRequest: LastLoggedInAccountRequest! 4 | ) { 5 | lastLoggedInAccount(request: $lastLoggedInAccountRequest) { 6 | ...AccountFields 7 | } 8 | accountsAvailable(request: $accountsAvailableRequest) { 9 | items { 10 | ... on AccountManaged { 11 | __typename 12 | account { 13 | ...AccountFields 14 | } 15 | } 16 | ... on AccountOwned { 17 | __typename 18 | account { 19 | ...AccountFields 20 | } 21 | } 22 | } 23 | pageInfo { 24 | next 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/accounts-blocked.graphql: -------------------------------------------------------------------------------- 1 | query AccountsBlocked($request: AccountsBlockedRequest!) { 2 | accountsBlocked(request: $request) { 3 | items { 4 | account { 5 | ...AccountFields 6 | } 7 | blockedAt 8 | } 9 | pageInfo { 10 | next 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/accounts.graphql: -------------------------------------------------------------------------------- 1 | query Accounts($request: AccountsRequest!) { 2 | accounts(request: $request) { 3 | items { 4 | ...AccountFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/authenticated-sessions.graphql: -------------------------------------------------------------------------------- 1 | query AuthenticatedSessions($request: AuthenticatedSessionsRequest!) { 2 | authenticatedSessions(request: $request) { 3 | items { 4 | authenticationId 5 | app 6 | browser 7 | device 8 | os 9 | origin 10 | signer 11 | createdAt 12 | updatedAt 13 | } 14 | pageInfo { 15 | next 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/followers-you-know.graphql: -------------------------------------------------------------------------------- 1 | query FollowersYouKnow($request: FollowersYouKnowRequest!) { 2 | followersYouKnow(request: $request) { 3 | items { 4 | follower { 5 | ...AccountFields 6 | } 7 | followedOn 8 | } 9 | pageInfo { 10 | next 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/followers.graphql: -------------------------------------------------------------------------------- 1 | query Followers($request: FollowersRequest!) { 2 | followers(request: $request) { 3 | items { 4 | follower { 5 | ...AccountFields 6 | } 7 | followedOn 8 | } 9 | pageInfo { 10 | next 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/following.graphql: -------------------------------------------------------------------------------- 1 | query Following($request: FollowingRequest!) { 2 | following(request: $request) { 3 | items { 4 | following { 5 | ...AccountFields 6 | } 7 | followedOn 8 | } 9 | pageInfo { 10 | next 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/last-logged-in-account.graphql: -------------------------------------------------------------------------------- 1 | query LastLoggedInAccount($request: LastLoggedInAccountRequest!) { 2 | lastLoggedInAccount(request: $request) { 3 | address 4 | owner 5 | score 6 | metadata { 7 | ...AccountMetadataFields 8 | } 9 | username { 10 | ...UsernameFields 11 | } 12 | operations { 13 | ...LoggedInAccountOperationsFields 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/me.graphql: -------------------------------------------------------------------------------- 1 | query Me { 2 | me { 3 | loggedInAs { 4 | ... on AccountManaged { 5 | account { 6 | ...AccountFields 7 | } 8 | addedAt 9 | } 10 | ... on AccountOwned { 11 | account { 12 | ...AccountFields 13 | } 14 | addedAt 15 | } 16 | } 17 | isSignless 18 | isSponsored 19 | appLoggedIn 20 | limit { 21 | window 22 | allowanceLeft 23 | allowanceUsed 24 | allowance 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/notifications.graphql: -------------------------------------------------------------------------------- 1 | query Notifications($request: NotificationRequest!) { 2 | notifications(request: $request) { 3 | items { 4 | ... on CommentNotification { 5 | ...CommentNotificationFields 6 | } 7 | ... on FollowNotification { 8 | ...FollowNotificationFields 9 | } 10 | ... on MentionNotification { 11 | ...MentionNotificationFields 12 | } 13 | ... on QuoteNotification { 14 | ...QuoteNotificationFields 15 | } 16 | ... on ReactionNotification { 17 | ...ReactionNotificationFields 18 | } 19 | ... on RepostNotification { 20 | ...RepostNotificationFields 21 | } 22 | } 23 | pageInfo { 24 | next 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/account/usernames.graphql: -------------------------------------------------------------------------------- 1 | query Usernames($request: UsernamesRequest!) { 2 | usernames(request: $request) { 3 | items { 4 | ...UsernameFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/post/post-references.graphql: -------------------------------------------------------------------------------- 1 | query PostReferences($request: PostReferencesRequest!) { 2 | postReferences(request: $request) { 3 | items { 4 | ...PostFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/post/post.graphql: -------------------------------------------------------------------------------- 1 | query Post($request: PostRequest!) { 2 | post(request: $request) { 3 | ...PostFields 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/post/posts.graphql: -------------------------------------------------------------------------------- 1 | query Posts($request: PostsRequest!) { 2 | posts(request: $request) { 3 | items { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Repost { 8 | ...RepostFields 9 | } 10 | } 11 | pageInfo { 12 | next 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/indexer/documents/queries/search.graphql: -------------------------------------------------------------------------------- 1 | query Search($postsRequest: PostsRequest!, $accountsRequest: AccountsRequest!) { 2 | posts(request: $postsRequest) { 3 | items { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Repost { 8 | ...RepostFields 9 | } 10 | } 11 | } 12 | accounts(request: $accountsRequest) { 13 | items { 14 | ...AccountFields 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/indexer/gql/generated/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fragment-masking"; 2 | export * from "./gql"; -------------------------------------------------------------------------------- /packages/indexer/gql/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./generated"; 2 | export * from "./generated/graphql"; 3 | -------------------------------------------------------------------------------- /packages/indexer/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "moduleResolution": "Node" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/lens/codegen.ts: -------------------------------------------------------------------------------- 1 | import type { CodegenConfig } from "@graphql-codegen/cli"; 2 | import { LensEndpoint } from "@tape.xyz/constants"; 3 | 4 | const config: CodegenConfig = { 5 | overwrite: true, 6 | schema: LensEndpoint.Testnet, 7 | documents: "./documents/**/*.graphql", 8 | customFetch: "node-fetch", 9 | generates: { 10 | "generated.ts": { 11 | plugins: [ 12 | "typescript", 13 | "typescript-operations", 14 | "typescript-react-apollo", 15 | "fragment-matcher" 16 | ] 17 | } 18 | }, 19 | hooks: { 20 | afterAllFileWrite: ["biome format --write ."] 21 | } 22 | }; 23 | 24 | export default config; 25 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/amount-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment AmountFields on Amount { 2 | asset { 3 | ...Erc20Fields 4 | } 5 | value 6 | } 7 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/comment-base-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment CommentBaseFields on Comment { 2 | __typename 3 | id 4 | publishedOn { 5 | id 6 | } 7 | isHidden 8 | momoka { 9 | proof 10 | } 11 | txHash 12 | createdAt 13 | by { 14 | ...ProfileFields 15 | } 16 | stats { 17 | ...PublicationStatsFields 18 | } 19 | operations { 20 | ...PublicationOperationFields 21 | } 22 | metadata { 23 | ...AnyPublicationMetadataFields 24 | } 25 | openActionModules { 26 | ...OpenActionModulesFields 27 | } 28 | root { 29 | ...PostFields 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/comment-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment CommentFields on Comment { 2 | ...CommentBaseFields 3 | commentOn { 4 | ...PrimaryPublicationFields 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/erc20-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment Erc20Fields on Asset { 2 | ... on Erc20 { 3 | name 4 | symbol 5 | decimals 6 | contract { 7 | ...NetworkAddressFields 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/fiat-amount-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment FiatAmountFields on FiatAmount { 2 | asset { 3 | name 4 | symbol 5 | decimals 6 | } 7 | value 8 | } 9 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/follow-module-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment FollowModuleFields on FollowModule { 2 | ... on FeeFollowModuleSettings { 3 | type 4 | amount { 5 | ...AmountFields 6 | asFiat(request: { for: USD }) { 7 | ...FiatAmountFields 8 | } 9 | } 10 | recipient 11 | } 12 | ... on RevertFollowModuleSettings { 13 | type 14 | } 15 | ... on UnknownFollowModuleSettings { 16 | type 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/handle-info-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment HandleInfoFields on HandleInfo { 2 | id 3 | fullHandle 4 | localName 5 | ownedBy 6 | } 7 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/image-set-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment ImageSetFields on ImageSet { 2 | __typename 3 | raw { 4 | uri 5 | } 6 | optimized { 7 | uri 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/metadata-attribute-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MetadataAttributeFields on MetadataAttribute { 2 | type 3 | key 4 | value 5 | } 6 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/mirror-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MirrorFields on Mirror { 2 | id 3 | publishedOn { 4 | id 5 | } 6 | isHidden 7 | momoka { 8 | proof 9 | } 10 | txHash 11 | createdAt 12 | mirrorOn { 13 | ...PrimaryPublicationFields 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/network-address-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment NetworkAddressFields on NetworkAddress { 2 | address 3 | chainId 4 | } 5 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/post-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PostFields on Post { 2 | __typename 3 | id 4 | publishedOn { 5 | id 6 | } 7 | isHidden 8 | momoka { 9 | proof 10 | } 11 | txHash 12 | createdAt 13 | by { 14 | ...ProfileFields 15 | } 16 | stats { 17 | ...PublicationStatsFields 18 | } 19 | operations { 20 | ...PublicationOperationFields 21 | } 22 | metadata { 23 | ...AnyPublicationMetadataFields 24 | } 25 | openActionModules { 26 | ...OpenActionModulesFields 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/primary-publication-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PrimaryPublicationFields on PrimaryPublication { 2 | ... on Post { 3 | ...PostFields 4 | } 5 | ... on Comment { 6 | ...CommentBaseFields 7 | } 8 | ... on Quote { 9 | ...QuoteBaseFields 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/profile-metadata-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment ProfileMetadataFields on ProfileMetadata { 2 | displayName 3 | bio 4 | rawURI 5 | picture { 6 | ... on ImageSet { 7 | ...ImageSetFields 8 | } 9 | ... on NftImage { 10 | image { 11 | ...ImageSetFields 12 | } 13 | } 14 | } 15 | coverPicture { 16 | ...ImageSetFields 17 | } 18 | attributes { 19 | ...MetadataAttributeFields 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/profile-operations-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment ProfileOperationsFields on ProfileOperations { 2 | id 3 | isBlockedByMe { 4 | value 5 | } 6 | isFollowedByMe { 7 | value 8 | } 9 | isFollowingMe { 10 | value 11 | } 12 | canBlock 13 | canUnblock 14 | canFollow 15 | canUnfollow 16 | } 17 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/profile-stats-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment ProfileStatsFields on ProfileStats { 2 | id 3 | followers 4 | following 5 | comments 6 | posts 7 | mirrors 8 | quotes 9 | publications 10 | reactions 11 | reacted 12 | countOpenActions 13 | lensClassifierScore 14 | } 15 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/article-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment ArticleMetadataV3Fields on ArticleMetadataV3 { 2 | __typename 3 | id 4 | content 5 | tags 6 | attributes { 7 | ...MetadataAttributeFields 8 | } 9 | attachments { 10 | ...PublicationMetadataMediaFields 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/audio-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment AudioMetadataV3Fields on AudioMetadataV3 { 2 | __typename 3 | id 4 | rawURI 5 | tags 6 | contentWarning 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | asset { 11 | ...PublicationMetadataMediaAudioFields 12 | } 13 | attachments { 14 | ...PublicationMetadataMediaFields 15 | } 16 | title 17 | content 18 | } 19 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/checkin-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment CheckingInMetadataV3Fields on CheckingInMetadataV3 { 2 | __typename 3 | id 4 | location 5 | tags 6 | geographic { 7 | latitude 8 | longitude 9 | } 10 | attributes { 11 | ...MetadataAttributeFields 12 | } 13 | attachments { 14 | ...PublicationMetadataMediaFields 15 | } 16 | content 17 | } 18 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/image-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment ImageMetadataV3Fields on ImageMetadataV3 { 2 | __typename 3 | id 4 | rawURI 5 | tags 6 | contentWarning 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | attachments { 11 | ...PublicationMetadataMediaFields 12 | } 13 | asset { 14 | ...PublicationMetadataMediaImageFields 15 | } 16 | title 17 | content 18 | } 19 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/link-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment LinkMetadataV3Fields on LinkMetadataV3 { 2 | __typename 3 | id 4 | rawURI 5 | tags 6 | contentWarning 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | sharingLink 11 | attachments { 12 | ...PublicationMetadataMediaFields 13 | } 14 | content 15 | } 16 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/live-stream-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment LiveStreamMetadataV3Fields on LiveStreamMetadataV3 { 2 | __typename 3 | id 4 | rawURI 5 | tags 6 | contentWarning 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | startsAt 11 | endsAt 12 | playbackURL 13 | liveURL 14 | checkLiveAPI 15 | title 16 | content 17 | attachments { 18 | ...PublicationMetadataMediaFields 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/media-fields/publication-metadata-media-audio-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PublicationMetadataMediaAudioFields on PublicationMetadataMediaAudio { 2 | audio { 3 | raw { 4 | uri 5 | } 6 | optimized { 7 | uri 8 | } 9 | } 10 | cover { 11 | raw { 12 | uri 13 | } 14 | optimized { 15 | uri 16 | } 17 | } 18 | duration 19 | } 20 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/media-fields/publication-metadata-media-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PublicationMetadataMediaFields on PublicationMetadataMedia { 2 | ... on PublicationMetadataMediaVideo { 3 | __typename 4 | ...PublicationMetadataMediaVideoFields 5 | } 6 | ... on PublicationMetadataMediaImage { 7 | __typename 8 | ...PublicationMetadataMediaImageFields 9 | } 10 | ... on PublicationMetadataMediaAudio { 11 | __typename 12 | ...PublicationMetadataMediaAudioFields 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/media-fields/publication-metadata-media-image-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PublicationMetadataMediaImageFields on PublicationMetadataMediaImage { 2 | image { 3 | raw { 4 | uri 5 | } 6 | optimized { 7 | uri 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/media-fields/publication-metadata-media-video-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PublicationMetadataMediaVideoFields on PublicationMetadataMediaVideo { 2 | video { 3 | raw { 4 | uri 5 | } 6 | optimized { 7 | uri 8 | } 9 | } 10 | cover { 11 | raw { 12 | uri 13 | } 14 | optimized { 15 | uri 16 | } 17 | } 18 | duration 19 | } 20 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/mint-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment MintMetadataV3Fields on MintMetadataV3 { 2 | __typename 3 | id 4 | rawURI 5 | tags 6 | contentWarning 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | mintLink 11 | attachments { 12 | ...PublicationMetadataMediaFields 13 | } 14 | content 15 | } 16 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/text-only-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment TextOnlyMetadataV3Fields on TextOnlyMetadataV3 { 2 | __typename 3 | id 4 | rawURI 5 | tags 6 | contentWarning 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | content 11 | } 12 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-metadata/video-metadata-v3-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment VideoMetadataV3Fields on VideoMetadataV3 { 2 | __typename 3 | id 4 | rawURI 5 | tags 6 | contentWarning 7 | attributes { 8 | ...MetadataAttributeFields 9 | } 10 | asset { 11 | ...PublicationMetadataMediaVideoFields 12 | } 13 | attachments { 14 | ...PublicationMetadataMediaFields 15 | } 16 | title 17 | content 18 | isShortVideo 19 | } 20 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-operation-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PublicationOperationFields on PublicationOperations { 2 | isNotInterested 3 | hasBookmarked 4 | hasReported 5 | canAct 6 | hasActed { 7 | value 8 | isFinalisedOnchain 9 | } 10 | actedOn { 11 | ... on KnownCollectOpenActionResult { 12 | type 13 | } 14 | ... on UnknownOpenActionResult { 15 | address 16 | category 17 | initReturnData 18 | } 19 | } 20 | hasReacted(request: { type: UPVOTE }) 21 | canComment 22 | canMirror 23 | hasMirrored 24 | canDecrypt { 25 | result 26 | reasons 27 | extraDetails 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/publication-stats-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment PublicationStatsFields on PublicationStats { 2 | id 3 | comments 4 | mirrors 5 | quotes 6 | reactions(request: { type: UPVOTE }) 7 | countOpenActions 8 | } 9 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/quote-base-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment QuoteBaseFields on Quote { 2 | __typename 3 | id 4 | publishedOn { 5 | id 6 | } 7 | isHidden 8 | momoka { 9 | proof 10 | } 11 | txHash 12 | createdAt 13 | by { 14 | ...ProfileFields 15 | } 16 | stats { 17 | ...PublicationStatsFields 18 | } 19 | operations { 20 | ...PublicationOperationFields 21 | } 22 | metadata { 23 | ...AnyPublicationMetadataFields 24 | } 25 | openActionModules { 26 | ...OpenActionModulesFields 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/lens/documents/fragments/quote-fields.graphql: -------------------------------------------------------------------------------- 1 | fragment QuoteFields on Quote { 2 | ...QuoteBaseFields 3 | quoteOn { 4 | ...PrimaryPublicationFields 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/Authenticate.graphql: -------------------------------------------------------------------------------- 1 | mutation Authenticate($request: SignedAuthChallenge!) { 2 | authenticate(request: $request) { 3 | accessToken 4 | refreshToken 5 | identityToken 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/Broadcast.graphql: -------------------------------------------------------------------------------- 1 | mutation BroadcastOnchain($request: BroadcastRequest!) { 2 | broadcastOnchain(request: $request) { 3 | ... on RelaySuccess { 4 | __typename 5 | txHash 6 | txId 7 | } 8 | ... on RelayError { 9 | __typename 10 | reason 11 | } 12 | } 13 | } 14 | 15 | mutation BroadcastOnMomoka($request: BroadcastRequest!) { 16 | broadcastOnMomoka(request: $request) { 17 | ... on CreateMomokaPublicationResult { 18 | id 19 | proof 20 | momokaId 21 | } 22 | ... on RelayError { 23 | __typename 24 | reason 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/momoka/create-momoka-mirror-typed-data.graphql: -------------------------------------------------------------------------------- 1 | mutation CreateMomokaMirrorTypedData($request: MomokaMirrorRequest!) { 2 | createMomokaMirrorTypedData(request: $request) { 3 | id 4 | expiresAt 5 | typedData { 6 | types { 7 | Mirror { 8 | name 9 | type 10 | } 11 | } 12 | domain { 13 | name 14 | chainId 15 | version 16 | verifyingContract 17 | } 18 | value { 19 | nonce 20 | metadataURI 21 | deadline 22 | profileId 23 | pointedProfileId 24 | pointedPubId 25 | referrerProfileIds 26 | referrerPubIds 27 | referenceModuleData 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/momoka/create-momoka-post-typed-data.graphql: -------------------------------------------------------------------------------- 1 | mutation CreateMomokaPostTypedData($request: MomokaPostRequest!) { 2 | createMomokaPostTypedData(request: $request) { 3 | id 4 | expiresAt 5 | typedData { 6 | types { 7 | Post { 8 | name 9 | type 10 | } 11 | } 12 | domain { 13 | name 14 | chainId 15 | version 16 | verifyingContract 17 | } 18 | value { 19 | nonce 20 | deadline 21 | profileId 22 | contentURI 23 | actionModules 24 | actionModulesInitDatas 25 | referenceModule 26 | referenceModuleInitData 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/on-chain/create-onchain-set-profile-metadata-typed-data.graphql: -------------------------------------------------------------------------------- 1 | mutation CreateOnchainSetProfileMetadataTypedData( 2 | $options: TypedDataOptions 3 | $request: OnchainSetProfileMetadataRequest! 4 | ) { 5 | createOnchainSetProfileMetadataTypedData( 6 | options: $options 7 | request: $request 8 | ) { 9 | expiresAt 10 | id 11 | typedData { 12 | domain { 13 | name 14 | chainId 15 | version 16 | verifyingContract 17 | } 18 | types { 19 | SetProfileMetadataURI { 20 | name 21 | type 22 | } 23 | } 24 | value { 25 | nonce 26 | deadline 27 | profileId 28 | metadataURI 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/profile/add-profile-interests.graphql: -------------------------------------------------------------------------------- 1 | mutation AddProfileInterests($request: ProfileInterestsRequest!) { 2 | addProfileInterests(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/profile/create-profile-with-handle.graphql: -------------------------------------------------------------------------------- 1 | mutation CreateProfileWithHandle($request: CreateProfileWithHandleRequest!) { 2 | createProfileWithHandle(request: $request) { 3 | ... on RelaySuccess { 4 | txHash 5 | txId 6 | } 7 | ... on CreateProfileWithHandleErrorResult { 8 | reason 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/profile/remove-profile-interests.graphql: -------------------------------------------------------------------------------- 1 | mutation RemoveProfileInterests($request: ProfileInterestsRequest!) { 2 | removeProfileInterests(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/profile/set-profile-metadata.graphql: -------------------------------------------------------------------------------- 1 | mutation SetProfileMetadata($request: OnchainSetProfileMetadataRequest!) { 2 | setProfileMetadata(request: $request) { 3 | ... on RelaySuccess { 4 | txHash 5 | txId 6 | } 7 | ... on LensProfileManagerRelayError { 8 | reason 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/add-publication-bookmark.graphql: -------------------------------------------------------------------------------- 1 | mutation AddPublicationBookmark($request: PublicationBookmarkRequest!) { 2 | addPublicationBookmark(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/add-publication-not-interested.graphql: -------------------------------------------------------------------------------- 1 | mutation AddPublicationNotInterested( 2 | $request: PublicationNotInterestedRequest! 3 | ) { 4 | addPublicationNotInterested(request: $request) 5 | } 6 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/add-reaction.graphql: -------------------------------------------------------------------------------- 1 | mutation AddReaction($request: ReactionRequest!) { 2 | addReaction(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/hide-publication.graphql: -------------------------------------------------------------------------------- 1 | mutation HidePublication($request: HidePublicationRequest!) { 2 | hidePublication(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/remove-publication-bookmark.graphql: -------------------------------------------------------------------------------- 1 | mutation RemovePublicationBookmark($request: PublicationBookmarkRequest!) { 2 | removePublicationBookmark(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/remove-reaction.graphql: -------------------------------------------------------------------------------- 1 | mutation RemoveReaction($request: ReactionRequest!) { 2 | removeReaction(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/report-profile.graphql: -------------------------------------------------------------------------------- 1 | mutation ReportProfile($request: ReportProfileRequest!) { 2 | reportProfile(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/report-publication.graphql: -------------------------------------------------------------------------------- 1 | mutation ReportPublication($request: ReportPublicationRequest!) { 2 | reportPublication(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/mutations/publication/undo-publication-not-interested.graphql: -------------------------------------------------------------------------------- 1 | mutation UndoPublicationNotInterested( 2 | $request: PublicationNotInterestedRequest! 3 | ) { 4 | undoPublicationNotInterested(request: $request) 5 | } 6 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/Challenge.graphql: -------------------------------------------------------------------------------- 1 | query Challenge($request: ChallengeRequest!) { 2 | challenge(request: $request) { 3 | id 4 | text 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/Followers.graphql: -------------------------------------------------------------------------------- 1 | query Followers($request: FollowersRequest!) { 2 | followers(request: $request) { 3 | items { 4 | ...ProfileFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/Following.graphql: -------------------------------------------------------------------------------- 1 | query Following($request: FollowingRequest!) { 2 | following(request: $request) { 3 | items { 4 | ...ProfileFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/Profile.graphql: -------------------------------------------------------------------------------- 1 | query Profile($request: ProfileRequest!) { 2 | profile(request: $request) { 3 | ...ProfileFields 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/Profiles.graphql: -------------------------------------------------------------------------------- 1 | query Profiles($request: ProfilesRequest!) { 2 | profiles(request: $request) { 3 | items { 4 | ...ProfileFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/Publication.graphql: -------------------------------------------------------------------------------- 1 | query Publication($request: PublicationRequest!) { 2 | publication(request: $request) { 3 | ... on Post { 4 | ...PostFields 5 | } 6 | ... on Comment { 7 | ...CommentFields 8 | } 9 | ... on Mirror { 10 | ...MirrorFields 11 | } 12 | ... on Quote { 13 | ...QuoteFields 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/Publications.graphql: -------------------------------------------------------------------------------- 1 | query Publications($request: PublicationsRequest!) { 2 | publications(request: $request) { 3 | items { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Comment { 8 | ...CommentFields 9 | } 10 | ... on Mirror { 11 | ...MirrorFields 12 | } 13 | ... on Quote { 14 | ...QuoteFields 15 | } 16 | } 17 | pageInfo { 18 | next 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/approved-authentications.graphql: -------------------------------------------------------------------------------- 1 | query ApprovedAuthentications($request: ApprovedAuthenticationRequest!) { 2 | approvedAuthentications(request: $request) { 3 | items { 4 | authorizationId 5 | browser 6 | device 7 | os 8 | origin 9 | expiresAt 10 | createdAt 11 | updatedAt 12 | } 13 | pageInfo { 14 | next 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/approved-module-allowance-amount.graphql: -------------------------------------------------------------------------------- 1 | query ApprovedModuleAllowanceAmount( 2 | $request: ApprovedModuleAllowanceAmountRequest! 3 | ) { 4 | approvedModuleAllowanceAmount(request: $request) { 5 | allowance { 6 | value 7 | asset { 8 | ...Erc20Fields 9 | } 10 | asFiat(request: { for: USD }) { 11 | ...FiatAmountFields 12 | } 13 | } 14 | moduleContract { 15 | ...NetworkAddressFields 16 | } 17 | moduleName 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/current-profile.graphql: -------------------------------------------------------------------------------- 1 | query CurrentProfile($request: ProfileRequest!) { 2 | profile(request: $request) { 3 | ...ProfileFields 4 | } 5 | userSigNonces { 6 | lensHubOnchainSigNonce 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/explore-publications.graphql: -------------------------------------------------------------------------------- 1 | query ExplorePublications($request: ExplorePublicationRequest!) { 2 | explorePublications(request: $request) { 3 | items { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Quote { 8 | ...QuoteFields 9 | } 10 | } 11 | pageInfo { 12 | next 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/feed-highlights.graphql: -------------------------------------------------------------------------------- 1 | query FeedHighlights($request: FeedHighlightsRequest!) { 2 | feedHighlights(request: $request) { 3 | items { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Quote { 8 | ...QuoteFields 9 | } 10 | } 11 | pageInfo { 12 | next 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/generate-lens-apirelay-address.graphql: -------------------------------------------------------------------------------- 1 | query GenerateLensAPIRelayAddress { 2 | generateLensAPIRelayAddress 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/generate-module-currency-approval-data.graphql: -------------------------------------------------------------------------------- 1 | query GenerateModuleCurrencyApprovalData( 2 | $request: GenerateModuleCurrencyApprovalDataRequest! 3 | ) { 4 | generateModuleCurrencyApprovalData(request: $request) { 5 | to 6 | from 7 | data 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/handle-to-address.graphql: -------------------------------------------------------------------------------- 1 | query HandleToAddress($request: HandleToAddressRequest!) { 2 | handleToAddress(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/has-publication-indexed.graphql: -------------------------------------------------------------------------------- 1 | query HasPublicationIndexed($request: PublicationRequest!) { 2 | publication(request: $request) { 3 | ... on Post { 4 | id 5 | } 6 | ... on Comment { 7 | id 8 | } 9 | ... on Mirror { 10 | id 11 | } 12 | ... on Quote { 13 | id 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/latest-notification-id.graphql: -------------------------------------------------------------------------------- 1 | query LatestNotificationId($request: NotificationRequest!) { 2 | notifications(request: $request) { 3 | items { 4 | ... on ReactionNotification { 5 | id 6 | } 7 | ... on CommentNotification { 8 | id 9 | } 10 | ... on MirrorNotification { 11 | id 12 | } 13 | ... on QuoteNotification { 14 | id 15 | } 16 | ... on ActedNotification { 17 | id 18 | } 19 | ... on FollowNotification { 20 | id 21 | } 22 | ... on MentionNotification { 23 | id 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/lens-transaction-status.graphql: -------------------------------------------------------------------------------- 1 | query LensTransactionStatus($request: LensTransactionStatusRequest!) { 2 | lensTransactionStatus(request: $request) { 3 | status 4 | txHash 5 | reason 6 | extraInfo 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/mod-explore-publications.graphql: -------------------------------------------------------------------------------- 1 | query ModExplorePublications($request: ModExplorePublicationRequest!) { 2 | modExplorePublications(request: $request) { 3 | items { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Comment { 8 | ...CommentFields 9 | } 10 | ... on Quote { 11 | ...QuoteFields 12 | } 13 | } 14 | pageInfo { 15 | next 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/module-metadata.graphql: -------------------------------------------------------------------------------- 1 | query ModuleMetadata($request: ModuleMetadataRequest!) { 2 | moduleMetadata(request: $request) { 3 | metadata { 4 | attributes { 5 | key 6 | type 7 | value 8 | } 9 | authors 10 | description 11 | initializeCalldataABI 12 | initializeResultDataABI 13 | name 14 | processCalldataABI 15 | title 16 | } 17 | moduleType 18 | signlessApproved 19 | sponsoredApproved 20 | verified 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/mutual-followers.graphql: -------------------------------------------------------------------------------- 1 | query MutualFollowers($request: MutualFollowersRequest!) { 2 | mutualFollowers(request: $request) { 3 | items { 4 | ...ProfileFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/owned-handles.graphql: -------------------------------------------------------------------------------- 1 | query OwnedHandles($request: OwnedHandlesRequest!) { 2 | ownedHandles(request: $request) { 3 | items { 4 | id 5 | linkedTo { 6 | nftTokenId 7 | contract { 8 | address 9 | } 10 | } 11 | fullHandle 12 | } 13 | pageInfo { 14 | next 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/profile-feed.graphql: -------------------------------------------------------------------------------- 1 | query Feed($request: FeedRequest!) { 2 | feed(request: $request) { 3 | items { 4 | root { 5 | ... on Post { 6 | ...PostFields 7 | } 8 | ... on Comment { 9 | ...CommentFields 10 | } 11 | ... on Quote { 12 | ...QuoteFields 13 | } 14 | } 15 | } 16 | pageInfo { 17 | next 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/profile-follow-module.graphql: -------------------------------------------------------------------------------- 1 | query ProfileFollowModule($request: ProfileRequest!) { 2 | profile(request: $request) { 3 | followModule { 4 | ...FollowModuleFields 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/profile-interests-options.graphql: -------------------------------------------------------------------------------- 1 | query ProfileInterestsOptions($request: ProfileRequest!) { 2 | profileInterestsOptions 3 | profile(request: $request) { 4 | id 5 | interests 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/profile-managers.graphql: -------------------------------------------------------------------------------- 1 | query ProfileManagers($request: ProfileManagersRequest!) { 2 | profileManagers(request: $request) { 3 | items { 4 | address 5 | isLensManager 6 | } 7 | pageInfo { 8 | next 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/profiles-managed.graphql: -------------------------------------------------------------------------------- 1 | query ProfilesManaged( 2 | $request: ProfilesManagedRequest! 3 | $lastLoggedInProfileRequest: LastLoggedInProfileRequest! 4 | ) { 5 | profilesManaged(request: $request) { 6 | items { 7 | ...ProfileFields 8 | } 9 | pageInfo { 10 | next 11 | } 12 | } 13 | lastLoggedInProfile(request: $lastLoggedInProfileRequest) { 14 | ...ProfileFields 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/publication-bookmarks.graphql: -------------------------------------------------------------------------------- 1 | query PublicationBookmarks($request: PublicationBookmarksRequest!) { 2 | publicationBookmarks(request: $request) { 3 | items { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Comment { 8 | ...CommentFields 9 | } 10 | ... on Mirror { 11 | ...MirrorFields 12 | } 13 | ... on Quote { 14 | ...QuoteFields 15 | } 16 | } 17 | pageInfo { 18 | next 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/revenue-from-publication.graphql: -------------------------------------------------------------------------------- 1 | query RevenueFromPublication($request: RevenueFromPublicationRequest!) { 2 | revenueFromPublication(request: $request) { 3 | publication { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Comment { 8 | ...CommentFields 9 | } 10 | ... on Mirror { 11 | ...MirrorFields 12 | } 13 | ... on Quote { 14 | ...QuoteFields 15 | } 16 | } 17 | revenue { 18 | total { 19 | ...AmountFields 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/revenue-from-publications.graphql: -------------------------------------------------------------------------------- 1 | query RevenueFromPublications($request: RevenueFromPublicationsRequest!) { 2 | revenueFromPublications(request: $request) { 3 | items { 4 | publication { 5 | ... on Post { 6 | ...PostFields 7 | } 8 | ... on Comment { 9 | ...CommentFields 10 | } 11 | ... on Mirror { 12 | ...MirrorFields 13 | } 14 | ... on Quote { 15 | ...QuoteFields 16 | } 17 | } 18 | revenue { 19 | total { 20 | ...AmountFields 21 | } 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/revoke-authentication.graphql: -------------------------------------------------------------------------------- 1 | mutation RevokeAuthentication($request: RevokeAuthenticationRequest!) { 2 | revokeAuthentication(request: $request) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/search-profiles.graphql: -------------------------------------------------------------------------------- 1 | query SearchProfiles($request: ProfileSearchRequest!) { 2 | searchProfiles(request: $request) { 3 | items { 4 | ...ProfileFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/search-publications.graphql: -------------------------------------------------------------------------------- 1 | query SearchPublications($request: PublicationSearchRequest!) { 2 | searchPublications(request: $request) { 3 | items { 4 | ... on Post { 5 | ...PostFields 6 | } 7 | ... on Comment { 8 | ...CommentFields 9 | } 10 | ... on Quote { 11 | ...QuoteFields 12 | } 13 | } 14 | pageInfo { 15 | next 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/tx-id-to-tx-hash.graphql: -------------------------------------------------------------------------------- 1 | query TxIdToTxHash($for: TxId!) { 2 | txIdToTxHash(for: $for) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/who-acted-on-publication.graphql: -------------------------------------------------------------------------------- 1 | query WhoActedOnPublication($request: WhoActedOnPublicationRequest!) { 2 | whoActedOnPublication(request: $request) { 3 | items { 4 | ...ProfileFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/who-have-blocked.graphql: -------------------------------------------------------------------------------- 1 | query WhoHaveBlocked($request: WhoHaveBlockedRequest!) { 2 | whoHaveBlocked(request: $request) { 3 | items { 4 | ...ProfileFields 5 | } 6 | pageInfo { 7 | next 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/lens/documents/queries/who-reacted-publication.graphql: -------------------------------------------------------------------------------- 1 | query WhoReactedPublication($request: WhoReactedPublicationRequest!) { 2 | whoReactedPublication(request: $request) { 3 | items { 4 | profile { 5 | ...ProfileFields 6 | } 7 | } 8 | pageInfo { 9 | next 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/lens/documents/subscriptions/authorization-record-revoked.graphql: -------------------------------------------------------------------------------- 1 | subscription AuthorizationRecordRevokedSubscription($authorizationId: UUID!) { 2 | authorizationRecordRevoked(authorizationId: $authorizationId) 3 | } 4 | -------------------------------------------------------------------------------- /packages/lens/documents/subscriptions/new-notification.graphql: -------------------------------------------------------------------------------- 1 | subscription NewNotificationSubscription($for: ProfileId!) { 2 | newNotification(for: $for) { 3 | ... on ReactionNotification { 4 | id 5 | } 6 | ... on CommentNotification { 7 | id 8 | } 9 | ... on MirrorNotification { 10 | id 11 | } 12 | ... on QuoteNotification { 13 | id 14 | } 15 | ... on ActedNotification { 16 | id 17 | } 18 | ... on FollowNotification { 19 | id 20 | } 21 | ... on MentionNotification { 22 | id 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/lens/documents/subscriptions/user-sig-nonces.graphql: -------------------------------------------------------------------------------- 1 | subscription UserSigNoncesSubscription($address: EvmAddress!) { 2 | userSigNonces(address: $address) { 3 | lensHubOnchainSigNonce 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/lens/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json", 3 | "compilerOptions": { 4 | "module": "ESNext", 5 | "moduleResolution": "Node" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/server/.env.example: -------------------------------------------------------------------------------- 1 | TAPE_DATABASE_URL= 2 | -------------------------------------------------------------------------------- /packages/server/.gitignore: -------------------------------------------------------------------------------- 1 | generated 2 | -------------------------------------------------------------------------------- /packages/server/db/clickhouse/client.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from "@clickhouse/client"; 2 | 3 | const clickhouseClient = createClient({ 4 | compression: { request: true, response: true }, 5 | keep_alive: { enabled: true }, 6 | username: "clickhouse", 7 | password: process.env.CLICKHOUSE_PASSWORD, 8 | url: process.env.CLICKHOUSE_URL 9 | }); 10 | 11 | export { clickhouseClient }; 12 | -------------------------------------------------------------------------------- /packages/server/db/indexer/client.ts: -------------------------------------------------------------------------------- 1 | // import pgp from "pg-promise"; 2 | 3 | // const initOptions: pgp.IInitOptions = { 4 | // error: (err, e) => { 5 | // console.error("[indexer-db] Error:", err?.message || err); 6 | // if (e.query) { 7 | // console.info("[indexer-db] Error Query:", e.query); 8 | // if (e.params) { 9 | // console.info("[indexer-db] Error Parameters:", e.params); 10 | // } 11 | // } 12 | // } 13 | // }; 14 | 15 | // const indexerDb = pgp(initOptions)({ 16 | // connectionString: process.env.INDEXER_DATABASE_URL, 17 | // port: 6432, 18 | // max: 20 19 | // }); 20 | 21 | // export { indexerDb }; 22 | -------------------------------------------------------------------------------- /packages/server/db/tape/client.ts: -------------------------------------------------------------------------------- 1 | import { PrismaClient } from "@prisma/client"; 2 | 3 | const tapeDb = new PrismaClient({ log: ["info"] }); 4 | 5 | export { tapeDb }; 6 | -------------------------------------------------------------------------------- /packages/server/db/tape/migrations/20240723044050_curated_profile/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "CuratedProfile" ( 3 | "id" UUID NOT NULL DEFAULT gen_random_uuid(), 4 | "profileId" TEXT NOT NULL, 5 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 6 | 7 | CONSTRAINT "CuratedProfile_pkey" PRIMARY KEY ("id") 8 | ); 9 | -------------------------------------------------------------------------------- /packages/server/db/tape/migrations/20240809041517_add_profile/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "Profile" ( 3 | "id" UUID NOT NULL DEFAULT gen_random_uuid(), 4 | "profileId" TEXT NOT NULL, 5 | "isCurated" BOOLEAN NOT NULL DEFAULT false, 6 | "isVerified" BOOLEAN NOT NULL DEFAULT false, 7 | "isSuspended" BOOLEAN NOT NULL DEFAULT false, 8 | "isLimited" BOOLEAN NOT NULL DEFAULT false, 9 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 10 | "updatedAt" TIMESTAMP(3) NOT NULL, 11 | 12 | CONSTRAINT "Profile_pkey" PRIMARY KEY ("id") 13 | ); 14 | 15 | -- CreateIndex 16 | CREATE UNIQUE INDEX "Profile_profileId_key" ON "Profile"("profileId"); 17 | 18 | -- CreateIndex 19 | CREATE INDEX "Profile_profileId_idx" ON "Profile"("profileId"); 20 | -------------------------------------------------------------------------------- /packages/server/db/tape/migrations/20240809042300_update_default_time/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "Profile" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP; 3 | -------------------------------------------------------------------------------- /packages/server/db/tape/migrations/20240809051310_drop_tables/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - You are about to drop the `CuratedProfile` table. If the table is not empty, all the data it contains will be lost. 5 | - You are about to drop the `ProfileRestriction` table. If the table is not empty, all the data it contains will be lost. 6 | - You are about to drop the `Verified` table. If the table is not empty, all the data it contains will be lost. 7 | 8 | */ 9 | -- DropTable 10 | DROP TABLE "CuratedProfile"; 11 | 12 | -- DropTable 13 | DROP TABLE "ProfileRestriction"; 14 | 15 | -- DropTable 16 | DROP TABLE "Verified"; 17 | -------------------------------------------------------------------------------- /packages/server/db/tape/migrations/20241004061051_stats/migration.sql: -------------------------------------------------------------------------------- 1 | -- CreateTable 2 | CREATE TABLE "PlatformStats" ( 3 | "id" UUID NOT NULL DEFAULT gen_random_uuid(), 4 | "profiles" TEXT NOT NULL, 5 | "acts" TEXT NOT NULL, 6 | "posts" TEXT NOT NULL, 7 | "comments" TEXT NOT NULL, 8 | "mirrors" TEXT NOT NULL, 9 | "creatorEarnings" JSONB NOT NULL, 10 | "blockTimestamp" TEXT NOT NULL, 11 | "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 12 | "updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, 13 | 14 | CONSTRAINT "PlatformStats_pkey" PRIMARY KEY ("id") 15 | ); 16 | -------------------------------------------------------------------------------- /packages/server/db/tape/migrations/20241004061452_stats_optional/migration.sql: -------------------------------------------------------------------------------- 1 | -- AlterTable 2 | ALTER TABLE "PlatformStats" ALTER COLUMN "creatorEarnings" DROP NOT NULL, 3 | ALTER COLUMN "blockTimestamp" DROP NOT NULL; 4 | -------------------------------------------------------------------------------- /packages/server/db/tape/migrations/20241004062254_platform_stats/migration.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Warnings: 3 | 4 | - Made the column `creatorEarnings` on table `PlatformStats` required. This step will fail if there are existing NULL values in that column. 5 | - Made the column `blockTimestamp` on table `PlatformStats` required. This step will fail if there are existing NULL values in that column. 6 | 7 | */ 8 | -- AlterTable 9 | ALTER TABLE "PlatformStats" ALTER COLUMN "creatorEarnings" SET NOT NULL, 10 | ALTER COLUMN "blockTimestamp" SET NOT NULL; 11 | -------------------------------------------------------------------------------- /packages/server/db/tape/migrations/migration_lock.toml: -------------------------------------------------------------------------------- 1 | # Please do not edit this file manually 2 | # It should be added in your version-control system (i.e. Git) 3 | provider = "postgresql" -------------------------------------------------------------------------------- /packages/server/db/tape/seeds/execute.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO "AllowedToken" (name, symbol, decimals, address) 2 | VALUES 3 | ('Wrapped Matic', 'WMATIC', 18, '0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270'), 4 | ('Wrapped Ether', 'WETH', 18, '0x7ceB23fD6bC0adD59E62ac25578270cFf1b9f619'), 5 | ('Tether USD', 'USDT', 6, '0xc2132D05D31c914a87C6611C10748AEb04B58e8F'), 6 | ('USD Coin', 'USDC', 6, '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359'), 7 | ('DAI Stablecoin', 'DAI', 18, '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063'), 8 | ('Bonsai', 'BONSAI', 18, '0x3d2bD0e15829AA5C362a4144FdF4A1112fa29B5c'); 9 | 10 | INSERT INTO "CuratedProfile" ("profileId") VALUES 11 | ('0x0105f8'), 12 | ('0x01cada'); 13 | -------------------------------------------------------------------------------- /packages/server/env.d.ts: -------------------------------------------------------------------------------- 1 | declare namespace NodeJS { 2 | interface ProcessEnv { 3 | TAPE_DATABASE_URL: string; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/server/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./db/clickhouse/client"; 2 | export * from "./db/tape/client"; 3 | // export * from "./db/indexer/client"; 4 | export * from "./db/redis/client"; 5 | -------------------------------------------------------------------------------- /packages/server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tape.xyz/server", 3 | "version": "0.0.0", 4 | "license": "AGPL-3.0", 5 | "main": "index.ts", 6 | "scripts": { 7 | "typecheck": "tsc --pretty --noEmit", 8 | "tape:migrate": "prisma migrate dev --schema ./db/tape/schema.prisma", 9 | "prisma:generate": "prisma generate --schema ./db/tape/schema.prisma", 10 | "postinstall": "pnpm prisma:generate" 11 | }, 12 | "dependencies": { 13 | "@clickhouse/client": "^1.11.0", 14 | "@prisma/client": "^6.3.1", 15 | "pg-promise": "^11.13.0", 16 | "redis": "^4.7.0" 17 | }, 18 | "devDependencies": { 19 | "@tape.xyz/tsconfig": "workspace:*", 20 | "@types/node": "^22.13.1", 21 | "prisma": "^6.3.1", 22 | "typescript": "^5.7.3" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/base.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/tsconfig/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "esModuleInterop": true, 6 | "incremental": false, 7 | "isolatedModules": true, 8 | "lib": ["es2022", "DOM", "DOM.Iterable"], 9 | "module": "NodeNext", 10 | "moduleDetection": "force", 11 | "moduleResolution": "NodeNext", 12 | "noUncheckedIndexedAccess": true, 13 | "resolveJsonModule": true, 14 | "skipLibCheck": true, 15 | "strict": true, 16 | "target": "ES2022" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/tsconfig/nextjs.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Next.js", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "plugins": [{ "name": "next" }], 7 | "module": "ESNext", 8 | "moduleResolution": "Bundler", 9 | "allowJs": true, 10 | "jsx": "preserve", 11 | "noEmit": true 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@tape.xyz/tsconfig", 3 | "private": true 4 | } 5 | -------------------------------------------------------------------------------- /packages/tsconfig/react.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "React", 4 | "extends": "./base.json", 5 | "compilerOptions": { 6 | "jsx": "react-jsx" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /packages/ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./src/Accordion"; 2 | export * from "./src/Badge"; 3 | export * from "./src/Button"; 4 | export * from "./src/Callout"; 5 | export * from "./src/Checkbox"; 6 | export * from "./src/Dropdown"; 7 | export * from "./src/HoverCard"; 8 | export * from "./src/icons"; 9 | export * from "./src/Input"; 10 | export * from "./src/Modal"; 11 | export * from "./src/players/video"; 12 | export * from "./src/Popover"; 13 | export * from "./src/RangeSlider"; 14 | export * from "./src/Select"; 15 | export * from "./src/Spinner"; 16 | export * from "./src/Switch"; 17 | export * from "./src/Tabs"; 18 | export * from "./src/TextArea"; 19 | export * from "./src/Tooltip"; 20 | -------------------------------------------------------------------------------- /packages/ui/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {} 5 | } 6 | }; 7 | -------------------------------------------------------------------------------- /packages/ui/src/Accordion.tsx: -------------------------------------------------------------------------------- 1 | import * as AccordionPrimitive from "@radix-ui/react-accordion"; 2 | 3 | export const { Accordion } = AccordionPrimitive; 4 | export const { AccordionItem } = AccordionPrimitive; 5 | export const { AccordionTrigger } = AccordionPrimitive; 6 | export const { AccordionContent } = AccordionPrimitive; 7 | -------------------------------------------------------------------------------- /packages/ui/src/HoverCard.tsx: -------------------------------------------------------------------------------- 1 | import * as HoverCardPrimitive from "@radix-ui/react-hover-card"; 2 | 3 | export const HoverCard = HoverCardPrimitive.Root; 4 | export const HoverCardTrigger = HoverCardPrimitive.Trigger; 5 | export const HoverCardContent = HoverCardPrimitive.Content; 6 | -------------------------------------------------------------------------------- /packages/ui/src/Popover.tsx: -------------------------------------------------------------------------------- 1 | import * as PopoverPrimitive from "@radix-ui/react-popover"; 2 | 3 | export const Popover = PopoverPrimitive.Root; 4 | export const PopoverTrigger = PopoverPrimitive.Trigger; 5 | export const PopoverContent = PopoverPrimitive.Content; 6 | -------------------------------------------------------------------------------- /packages/ui/src/Tabs.tsx: -------------------------------------------------------------------------------- 1 | import * as TabsPrimitive from "@radix-ui/react-tabs"; 2 | 3 | export const Tabs = TabsPrimitive.Root; 4 | export const { TabsTrigger } = TabsPrimitive; 5 | export const { TabsList } = TabsPrimitive; 6 | export const TabsContent = TabsPrimitive.Content; 7 | -------------------------------------------------------------------------------- /packages/ui/src/icons/CheckOutline.tsx: -------------------------------------------------------------------------------- 1 | import type { SVGProps } from "react"; 2 | 3 | export const CheckOutline = (props: SVGProps<SVGSVGElement>) => ( 4 | <svg 5 | {...props} 6 | viewBox="0 0 13 12" 7 | fill="none" 8 | xmlns="http://www.w3.org/2000/svg" 9 | > 10 | <path 11 | fillRule="evenodd" 12 | clipRule="evenodd" 13 | d="M12.4933 0.935023C12.8053 1.20743 12.8374 1.68122 12.565 1.99325L4.70786 10.9933C4.56543 11.1564 4.35943 11.25 4.14287 11.25C3.9263 11.25 3.72031 11.1564 3.57788 10.9933L0.435023 7.39325C0.162612 7.08122 0.194733 6.60743 0.506767 6.33502C0.818802 6.06261 1.29259 6.09473 1.565 6.40677L4.14287 9.3596L11.435 1.00677C11.7074 0.694733 12.1812 0.662612 12.4933 0.935023Z" 14 | fill="currentColor" 15 | /> 16 | </svg> 17 | ); 18 | -------------------------------------------------------------------------------- /packages/ui/src/icons/ChevronDownOutline.tsx: -------------------------------------------------------------------------------- 1 | import type { SVGProps } from "react"; 2 | 3 | export const ChevronDownOutline = (props: SVGProps<SVGSVGElement>) => ( 4 | <svg 5 | {...props} 6 | viewBox="0 0 16 8" 7 | fill="none" 8 | xmlns="http://www.w3.org/2000/svg" 9 | > 10 | <path 11 | fillRule="evenodd" 12 | clipRule="evenodd" 13 | d="M0.430571 0.51192C0.700138 0.197426 1.17361 0.161005 1.48811 0.430571L8.00001 6.01221L14.5119 0.430572C14.8264 0.161005 15.2999 0.197426 15.5695 0.511921C15.839 0.826415 15.8026 1.29989 15.4881 1.56946L8.48811 7.56946C8.20724 7.8102 7.79279 7.8102 7.51192 7.56946L0.51192 1.56946C0.197426 1.29989 0.161005 0.826414 0.430571 0.51192Z" 14 | fill="currentColor" 15 | /> 16 | </svg> 17 | ); 18 | -------------------------------------------------------------------------------- /packages/ui/src/icons/ChevronLeftOutline.tsx: -------------------------------------------------------------------------------- 1 | import type { SVGProps } from "react"; 2 | 3 | export const ChevronLeftOutline = (props: SVGProps<SVGSVGElement>) => ( 4 | <svg 5 | {...props} 6 | viewBox="0 0 8 16" 7 | fill="none" 8 | xmlns="http://www.w3.org/2000/svg" 9 | > 10 | <path 11 | fillRule="evenodd" 12 | clipRule="evenodd" 13 | d="M7.48809 0.430571C7.80259 0.700138 7.83901 1.17361 7.56944 1.48811L1.98781 8.00001L7.56944 14.5119C7.83901 14.8264 7.80259 15.2999 7.48809 15.5695C7.1736 15.839 6.70012 15.8026 6.43056 15.4881L0.430558 8.48811C0.189814 8.20724 0.189814 7.79279 0.430558 7.51192L6.43056 0.51192C6.70012 0.197426 7.1736 0.161005 7.48809 0.430571Z" 14 | fill="currentColor" 15 | /> 16 | </svg> 17 | ); 18 | -------------------------------------------------------------------------------- /packages/ui/src/icons/ChevronRightOutline.tsx: -------------------------------------------------------------------------------- 1 | import type { SVGProps } from "react"; 2 | 3 | export const ChevronRightOutline = (props: SVGProps<SVGSVGElement>) => ( 4 | <svg 5 | {...props} 6 | viewBox="0 0 8 16" 7 | fill="none" 8 | xmlns="http://www.w3.org/2000/svg" 9 | > 10 | <path 11 | fillRule="evenodd" 12 | clipRule="evenodd" 13 | d="M0.51192 0.430571C0.826414 0.161005 1.29989 0.197426 1.56946 0.51192L7.56946 7.51192C7.8102 7.79279 7.8102 8.20724 7.56946 8.48811L1.56946 15.4881C1.29989 15.8026 0.826414 15.839 0.51192 15.5695C0.197426 15.2999 0.161005 14.8264 0.430571 14.5119L6.01221 8.00001L0.430571 1.48811C0.161005 1.17361 0.197426 0.700138 0.51192 0.430571Z" 14 | fill="currentColor" 15 | /> 16 | </svg> 17 | ); 18 | -------------------------------------------------------------------------------- /packages/ui/src/icons/ChevronUpOutline.tsx: -------------------------------------------------------------------------------- 1 | import type { SVGProps } from "react"; 2 | 3 | export const ChevronUpOutline = (props: SVGProps<SVGSVGElement>) => ( 4 | <svg 5 | {...props} 6 | viewBox="0 0 16 8" 7 | fill="none" 8 | xmlns="http://www.w3.org/2000/svg" 9 | > 10 | <path 11 | fillRule="evenodd" 12 | clipRule="evenodd" 13 | d="M7.51192 0.430558C7.79279 0.189814 8.20724 0.189814 8.48811 0.430558L15.4881 6.43056C15.8026 6.70012 15.839 7.1736 15.5695 7.48809C15.2999 7.80259 14.8264 7.83901 14.5119 7.56944L8.00001 1.98781L1.48811 7.56944C1.17361 7.83901 0.700138 7.80259 0.430571 7.48809C0.161005 7.1736 0.197426 6.70012 0.51192 6.43056L7.51192 0.430558Z" 14 | fill="currentColor" 15 | /> 16 | </svg> 17 | ); 18 | -------------------------------------------------------------------------------- /packages/ui/src/icons/HeartFilled.tsx: -------------------------------------------------------------------------------- 1 | import type { SVGProps } from "react"; 2 | 3 | export const HeartFilled = (props: SVGProps<SVGSVGElement>) => ( 4 | <svg 5 | {...props} 6 | viewBox="0 0 20 18" 7 | fill="none" 8 | xmlns="http://www.w3.org/2000/svg" 9 | > 10 | <path 11 | d="M0 6.1371C0 11 4.01943 13.5914 6.96173 15.9109C8 16.7294 9 17.5 10 17.5C11 17.5 12 16.7294 13.0383 15.9109C15.9806 13.5914 20 11 20 6.1371C20 1.27416 14.4998 -2.17454 10 2.50063C5.50016 -2.17454 0 1.27416 0 6.1371Z" 12 | fill="currentColor" 13 | /> 14 | </svg> 15 | ); 16 | -------------------------------------------------------------------------------- /packages/ui/src/icons/InfoSolid.tsx: -------------------------------------------------------------------------------- 1 | import type { SVGProps } from "react"; 2 | 3 | export const InfoSolid = (props: SVGProps<SVGSVGElement>) => ( 4 | <svg 5 | {...props} 6 | viewBox="0 0 20 20" 7 | fill="none" 8 | xmlns="http://www.w3.org/2000/svg" 9 | > 10 | <path 11 | fillRule="evenodd" 12 | clipRule="evenodd" 13 | d="M20 10C20 15.5228 15.5228 20 10 20C4.47715 20 0 15.5228 0 10C0 4.47715 4.47715 0 10 0C15.5228 0 20 4.47715 20 10ZM10 15.75C10.4142 15.75 10.75 15.4142 10.75 15V9C10.75 8.58579 10.4142 8.25 10 8.25C9.58579 8.25 9.25 8.58579 9.25 9V15C9.25 15.4142 9.58579 15.75 10 15.75ZM10 5C10.5523 5 11 5.44772 11 6C11 6.55228 10.5523 7 10 7C9.44771 7 9 6.55228 9 6C9 5.44772 9.44771 5 10 5Z" 14 | fill="currentColor" 15 | /> 16 | </svg> 17 | ); 18 | -------------------------------------------------------------------------------- /packages/ui/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const base = require("./tailwind-preset"); 2 | 3 | /** @type {import ('tailwindcss').Config} */ 4 | module.exports = { 5 | ...base, 6 | content: ["./**/*.{ts,tsx}"], 7 | plugins: [ 8 | require("@tailwindcss/aspect-ratio"), 9 | require("@tailwindcss/typography") 10 | ] 11 | }; 12 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/react.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/winder/src/_components/animated-number.tsx: -------------------------------------------------------------------------------- 1 | import { type Spring, m, useSpring, useTransform } from "motion/react"; 2 | import { useEffect } from "react"; 3 | import { tw } from "../tw"; 4 | 5 | type AnimatedNumber = { 6 | value: number; 7 | className?: string; 8 | springOptions?: Spring; 9 | }; 10 | 11 | export const AnimatedNumber = (props: AnimatedNumber) => { 12 | const { value, className, springOptions } = props; 13 | 14 | const spring = useSpring(value, springOptions); 15 | const display = useTransform(spring, (current) => 16 | Math.round(current).toLocaleString("en-US") 17 | ); 18 | 19 | useEffect(() => { 20 | spring.set(value); 21 | }, [spring, value]); 22 | 23 | return <m.span className={tw("tabular-nums", className)}>{display}</m.span>; 24 | }; 25 | -------------------------------------------------------------------------------- /packages/winder/src/_components/toast.tsx: -------------------------------------------------------------------------------- 1 | import { Toaster as Sonner, type ToasterProps, toast } from "sonner"; 2 | import { useTheme } from "../theme"; 3 | 4 | const Toaster = ({ ...props }: React.ComponentProps<typeof Sonner>) => { 5 | const { theme = "system" } = useTheme(); 6 | 7 | return ( 8 | <Sonner 9 | theme={theme as ToasterProps["theme"]} 10 | richColors 11 | toastOptions={{ 12 | classNames: { 13 | icon: "hidden", 14 | toast: "font-sans" 15 | } 16 | }} 17 | {...props} 18 | /> 19 | ); 20 | }; 21 | 22 | export { Toaster, toast }; 23 | -------------------------------------------------------------------------------- /packages/winder/src/tw.ts: -------------------------------------------------------------------------------- 1 | import type { ClassValue } from "clsx"; 2 | import { clsx } from "clsx"; 3 | import { twMerge } from "tailwind-merge"; 4 | 5 | export const tw = (...inputs: ClassValue[]) => { 6 | return twMerge(clsx(inputs)); 7 | }; 8 | -------------------------------------------------------------------------------- /packages/winder/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tape.xyz/tsconfig/react.json", 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "paths": { 6 | "@/*": ["./src/*"] 7 | }, 8 | "module": "ESNext", 9 | "moduleResolution": "bundler" 10 | }, 11 | "include": ["src"], 12 | "exclude": ["node_modules"] 13 | } 14 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'apps/**' 3 | - 'packages/**' 4 | -------------------------------------------------------------------------------- /scripts/clean-branches: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Deleting all branches but 'main' 🗑" 4 | git branch | grep -v "main" | xargs git branch -D 5 | echo "Branches deleted 🎉" 6 | -------------------------------------------------------------------------------- /scripts/clean-packages: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Deleting all node_modules and pnpm-lock.yaml files recursively 🗑" 4 | find . -name "node_modules" -type d -prune -exec rm -rf '{}' + -print 5 | find . -name ".next" -type d -prune -exec rm -rf '{}' + -print 6 | find . -name "pnpm-lock.yaml" -type f -prune -exec rm -rf '{}' + -print 7 | find . -name "tsconfig.tsbuildinfo" -type f -prune -exec rm -rf '{}' + -print 8 | echo "node_modules and lock files deleted 🎉" 9 | -------------------------------------------------------------------------------- /scripts/sync-branches: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | git checkout main 4 | echo "Syncing testnet branch with main branch 🔄" 5 | git pull origin main 6 | git checkout testnet 7 | git merge main 8 | git push origin testnet 9 | git checkout main 10 | echo "Branches synced 🎉" --------------------------------------------------------------------------------