= [];
4 |
5 | processWorkQueue(ZapperQueue);
6 |
--------------------------------------------------------------------------------
/packages/app/src/bench.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Snort Benchmarks
6 |
7 |
8 | Check console
9 |
10 |
11 |
--------------------------------------------------------------------------------
/packages/app/src/Components/RightWidgets/index.tsx:
--------------------------------------------------------------------------------
1 | export enum RightColumnWidget {
2 | TaskList,
3 | TrendingNotes,
4 | TrendingPeople,
5 | TrendingHashtags,
6 | LatestArticls,
7 | LiveStreams,
8 | InviteFriends,
9 | }
10 |
--------------------------------------------------------------------------------
/packages/app/src/Pages/Profile/ProfileTabType.tsx:
--------------------------------------------------------------------------------
1 | export enum ProfileTabType {
2 | NOTES = 0,
3 | REACTIONS = 1,
4 | FOLLOWERS = 2,
5 | FOLLOWS = 3,
6 | ZAPS = 4,
7 | MUTED = 5,
8 | RELAYS = 7,
9 | BOOKMARKS = 8,
10 | }
11 |
--------------------------------------------------------------------------------
/packages/system/tests/setupTests.ts:
--------------------------------------------------------------------------------
1 | import { TextEncoder, TextDecoder } from "util";
2 | import { Crypto } from "@peculiar/webcrypto";
3 |
4 | Object.assign(global, { TextDecoder, TextEncoder });
5 | Object.assign(globalThis.window.crypto, new Crypto());
6 |
--------------------------------------------------------------------------------
/src-tauri/capabilities/migrated.json:
--------------------------------------------------------------------------------
1 | {
2 | "identifier": "migrated",
3 | "description": "permissions that were migrated from v1",
4 | "local": true,
5 | "windows": [
6 | "main"
7 | ],
8 | "permissions": [
9 | "core:default"
10 | ]
11 | }
--------------------------------------------------------------------------------
/packages/app/src/Components/Embed/NostrNestsEmbed.tsx:
--------------------------------------------------------------------------------
1 | const NostrNestsEmbed = ({ link }: { link: string }) => (
2 |
3 | );
4 |
5 | export default NostrNestsEmbed;
6 |
--------------------------------------------------------------------------------
/packages/system/tests/negentropy.test.ts:
--------------------------------------------------------------------------------
1 | import { NegentropyStorageVector, VectorStorageItem } from "../src/negentropy/vector-storage";
2 | import { describe, test } from "bun:test";
3 |
4 | describe("negentropy", () => {
5 | test("should decodeBound", () => {});
6 | });
7 |
--------------------------------------------------------------------------------
/packages/app/src/Components/json.tsx:
--------------------------------------------------------------------------------
1 | export default function JsonBlock({ obj }: { obj: object }) {
2 | return (
3 |
4 | {JSON.stringify(obj, undefined, 2)}
5 |
6 | );
7 | }
8 |
--------------------------------------------------------------------------------
/packages/app/src/Hooks/useRelays.tsx:
--------------------------------------------------------------------------------
1 | import useLogin from "./useLogin";
2 |
3 | export default function useRelays() {
4 | const relays = useLogin(s => s.state.relays);
5 | return relays ? Object.fromEntries(relays.map(a => [a.url, a.settings])) : CONFIG.defaultRelays;
6 | }
7 |
--------------------------------------------------------------------------------
/packages/app/src/Pages/settings/wallet/index.tsx:
--------------------------------------------------------------------------------
1 | export { default as WalletSettings } from "../WalletSettings";
2 | export { default as AlbyOAuth } from "./Alby";
3 | export { default as ConnectLNDHub } from "./LNDHub";
4 | export { default as ConnectNostrWallet } from "./NWC";
5 |
--------------------------------------------------------------------------------
/packages/worker-relay/src/debug.ts:
--------------------------------------------------------------------------------
1 | let debug = false;
2 | export function debugLog(scope: string, msg: string, ...args: Array) {
3 | if (!debug) return;
4 | console.log(scope, msg, ...args);
5 | }
6 |
7 | export function setLogging(v: boolean) {
8 | debug = v;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/app/src/Feed/RelayState.ts:
--------------------------------------------------------------------------------
1 | import { SnortContext } from "@snort/system-react";
2 | import { use } from "react";
3 |
4 | export default function useRelayState(addr: string) {
5 | const system = use(SnortContext);
6 | const c = system.pool.getConnection(addr);
7 | return c;
8 | }
9 |
--------------------------------------------------------------------------------
/nap.yaml:
--------------------------------------------------------------------------------
1 | id: "social.snort.app"
2 | name: "Snort"
3 | description: ""
4 | icon: "https://snort.social/nostrich_256.png"
5 | images:
6 | - "https://snort.social/nostrich_512.png"
7 | repository: "https://github.com/v0l/snort"
8 | license: "MIT"
9 | tags:
10 | - "social"
11 | - "twitter"
12 |
--------------------------------------------------------------------------------
/packages/app/src/Components/PageSpinner.tsx:
--------------------------------------------------------------------------------
1 | import Spinner from "@/Components/Icons/Spinner";
2 |
3 | export default function PageSpinner() {
4 | return (
5 |
6 |
7 |
8 | );
9 | }
10 |
--------------------------------------------------------------------------------
/packages/system/src/encryption/index.ts:
--------------------------------------------------------------------------------
1 | export enum MessageEncryptorVersion {
2 | Nip4 = 0,
3 | Nip44 = 1,
4 | }
5 |
6 | export interface MessageEncryptor {
7 | encryptData(plaintext: string): Promise | string;
8 | decryptData(ciphertext: string): Promise | string;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/app/public/phoenix/.well-known/apple-app-site-association:
--------------------------------------------------------------------------------
1 | {
2 | "applinks": {
3 | "details": [
4 | {
5 | "appIDs": [
6 | "snort.social.app"
7 | ]
8 | }
9 | ]
10 | },
11 | "webcredentials": {
12 | "apps": [
13 | "snort.social.app"
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/app/public/snort/.well-known/apple-app-site-association:
--------------------------------------------------------------------------------
1 | {
2 | "applinks": {
3 | "details": [
4 | {
5 | "appIDs": [
6 | "snort.social.app"
7 | ]
8 | }
9 | ]
10 | },
11 | "webcredentials": {
12 | "apps": [
13 | "snort.social.app"
14 | ]
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/maintainers.yaml:
--------------------------------------------------------------------------------
1 | maintainers:
2 | - npub1g53mukxnjkcmr94fhryzkqutdz2ukq4ks0gvy5af25rgmwsl4ngq43drvk
3 | - npub1v0lxxxxutpvrelsksy8cdhgfux9l6a42hsj2qzquu2zk7vc9qnkszrqj49
4 | relays:
5 | - wss://relay.snort.social/
6 | - wss://pyramid.fiatjaf.com/
7 | - wss://nos.lol/
8 | - ws://skzzn6cimfdv5e2phjc4yr5v7ikbxtn5f7dkwn5c7v47tduzlbosqmqd.onion/
9 |
--------------------------------------------------------------------------------
/packages/app/src/Components/Embed/ZapstrEmbed.css:
--------------------------------------------------------------------------------
1 | .zapstr {
2 | }
3 |
4 | .zapstr > img {
5 | margin: 0 10px 0 0;
6 | }
7 |
8 | .zapstr audio {
9 | margin: 0;
10 | height: 2em;
11 | }
12 |
13 | .zapstr .pfp .avatar {
14 | width: 35px;
15 | height: 35px;
16 | }
17 |
18 | .zapstr .pfp .subheader {
19 | text-transform: capitalize;
20 | }
21 |
--------------------------------------------------------------------------------
/packages/app/src/Pages/settings/tools/index.tsx:
--------------------------------------------------------------------------------
1 | import { Outlet } from "react-router-dom";
2 |
3 | export { FollowsRelayHealth } from "./follows-relay-health";
4 | export { PruneFollowList } from "./prune-follows";
5 | export { default as SyncAccountTool } from "./sync-account";
6 |
7 | export function ToolsPage() {
8 | return ;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/app/src/Pages/ZapPool/ZapPool.tsx:
--------------------------------------------------------------------------------
1 | import "./ZapPool.css";
2 |
3 | import { ZapPoolPageInner } from "@/Pages/ZapPool/ZapPoolPageInner";
4 | import { ZapPoolController } from "@/Utils/ZapPoolController";
5 |
6 | export default function ZapPoolPage() {
7 | if (!ZapPoolController) {
8 | return null;
9 | }
10 | return ;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/system-wasm/pkg/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@snort/system-wasm",
3 | "type": "module",
4 | "version": "0.1.0",
5 | "files": [
6 | "system_wasm_bg.wasm",
7 | "system_wasm.js",
8 | "system_wasm.d.ts"
9 | ],
10 | "main": "system_wasm.js",
11 | "types": "system_wasm.d.ts",
12 | "sideEffects": [
13 | "./snippets/*"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/packages/app/src/Components/Embed/SoundCloudEmded.tsx:
--------------------------------------------------------------------------------
1 | const SoundCloudEmbed = ({ link }: { link: string }) => {
2 | return (
3 |
10 | );
11 | };
12 |
13 | export default SoundCloudEmbed;
14 |
--------------------------------------------------------------------------------
/packages/app/src/Components/Relay/software.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | export default function RelaySoftware({ software }: { software: string }) {
4 | if (software.includes("git")) {
5 | const u = new URL(software);
6 | return {u.pathname.split("/").at(-1)?.replace(".git", "")};
7 | }
8 | return software;
9 | }
10 |
--------------------------------------------------------------------------------
/packages/app/src/Components/Text/DisableMedia.tsx:
--------------------------------------------------------------------------------
1 | const DisableMedia = ({ content }: { content: string }) => (
2 | e.stopPropagation()}
5 | target="_blank"
6 | rel="noreferrer"
7 | className="text-highlight no-underline hover:underline">
8 | {content}
9 |
10 | );
11 |
12 | export default DisableMedia;
13 |
--------------------------------------------------------------------------------
/packages/shared/src/custom.d.ts:
--------------------------------------------------------------------------------
1 | declare module "light-bolt11-decoder" {
2 | export function decode(pr?: string): ParsedInvoice;
3 |
4 | export interface ParsedInvoice {
5 | paymentRequest: string;
6 | sections: Section[];
7 | }
8 |
9 | export interface Section {
10 | name: string;
11 | value: string | Uint8Array | number | undefined;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "files.exclude": {
3 | "**/.git": true,
4 | "**/.svn": true,
5 | "**/.hg": true,
6 | "**/CVS": true,
7 | "**/.DS_Store": true,
8 | "**/Thumbs.db": true,
9 | "**/node_modules": true
10 | },
11 | "search.exclude": {},
12 | "typescript.tsdk": "node_modules/typescript/lib",
13 | "typescript.enablePromptUseWorkspaceTsdk": true
14 | }
15 |
--------------------------------------------------------------------------------
/packages/app/src/Components/Embed/Hashtag.tsx:
--------------------------------------------------------------------------------
1 | import { Link } from "react-router-dom";
2 |
3 | const Hashtag = ({ tag }: { tag: string }) => {
4 | return (
5 |
6 | e.stopPropagation()} className="hover:underline">
7 | #{tag}
8 |
9 |
10 | );
11 | };
12 |
13 | export default Hashtag;
14 |
--------------------------------------------------------------------------------
/packages/app/src/Components/Embed/WavlakeEmbed.tsx:
--------------------------------------------------------------------------------
1 | const WavlakeEmbed = ({ link }: { link: string }) => {
2 | const convertedUrl = link.replace(/(?:player\.|www\.)?wavlake\.com/, "embed.wavlake.com");
3 |
4 | return (
5 |
6 | );
7 | };
8 |
9 | export default WavlakeEmbed;
10 |
--------------------------------------------------------------------------------
/packages/app/src/Hooks/useImgProxy.ts:
--------------------------------------------------------------------------------
1 | import usePreferences from "@/Hooks/usePreferences";
2 | import { proxyImg } from "@snort/shared";
3 |
4 | export default function useImgProxy() {
5 | const imgProxyConfig = usePreferences(s => s.imgProxyConfig);
6 |
7 | return {
8 | proxy: (url: string, resize?: number, sha256?: string) => proxyImg(url, imgProxyConfig, resize, sha256),
9 | };
10 | }
11 |
--------------------------------------------------------------------------------
/packages/app/README.md:
--------------------------------------------------------------------------------
1 | # bun-react-template
2 |
3 | To install dependencies:
4 |
5 | ```bash
6 | bun install
7 | ```
8 |
9 | To start a development server:
10 |
11 | ```bash
12 | bun dev
13 | ```
14 |
15 | To run for production:
16 |
17 | ```bash
18 | bun start
19 | ```
20 |
21 | This project was created using `bun init` in bun v1.3.0. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
22 |
--------------------------------------------------------------------------------
/packages/app/public/iris/.well-known/assetlinks.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "relation": ["delegate_permission/common.handle_all_urls"],
4 | "target": {
5 | "namespace": "android_app",
6 | "package_name": "to.iris.twa",
7 | "sha256_cert_fingerprints": [
8 | "63:B5:70:E8:F1:75:7E:D6:EF:81:11:66:F4:9D:47:AB:49:3C:2E:00:B9:67:92:40:89:A5:03:0B:96:B9:40:09"
9 | ]
10 | }
11 | }
12 | ]
13 |
--------------------------------------------------------------------------------
/packages/app/src/Components/IntlProvider/langStore.tsx:
--------------------------------------------------------------------------------
1 | import { ExternalStore } from "@snort/shared";
2 |
3 | class LangStore extends ExternalStore {
4 | setLang(s: string) {
5 | localStorage.setItem("lang", s);
6 | this.notifyChange();
7 | }
8 |
9 | takeSnapshot() {
10 | return localStorage.getItem("lang");
11 | }
12 | }
13 |
14 | export const LangOverride = new LangStore();
15 |
--------------------------------------------------------------------------------
/packages/app/src/Pages/Root/BlindSpots.tsx:
--------------------------------------------------------------------------------
1 | import { TimelineRenderer } from "@/Components/Feed/TimelineRenderer";
2 | import { useBlindSpot } from "@/Hooks/useBlindSpot";
3 |
4 | export function BlindSpots() {
5 | const data = useBlindSpot();
6 |
7 | const frag = {
8 | events: data,
9 | refTime: 0,
10 | };
11 |
12 | return {}} />;
13 | }
14 |
--------------------------------------------------------------------------------
/packages/system-react/src/useSystemState.tsx:
--------------------------------------------------------------------------------
1 | import { useSyncExternalStore } from "react";
2 | import type { SystemSnapshot } from "@snort/system";
3 | import type { ExternalStore } from "@snort/shared";
4 |
5 | export function useSystemState(system: ExternalStore) {
6 | return useSyncExternalStore(
7 | cb => system.hook(cb),
8 | () => system.snapshot(),
9 | );
10 | }
11 |
--------------------------------------------------------------------------------
/packages/app/src/Components/zap-amount.tsx:
--------------------------------------------------------------------------------
1 | import { formatShort } from "@/Utils/Number";
2 |
3 | import Icon from "./Icons/Icon";
4 |
5 | export default function ZapAmount({ n }: { n: number }) {
6 | return (
7 |
8 |
9 | {formatShort(n)}
10 |
11 | );
12 | }
13 |
--------------------------------------------------------------------------------
/packages/app/src/Pages/Messages/UnreadCount.tsx:
--------------------------------------------------------------------------------
1 | const UnreadCount = ({ unread }: { unread: number }) => {
2 | return (
3 | 0 ? "bg-highlight light:text-white" : "bg-neutral-800"}`}>
5 | {unread}
6 |
7 | );
8 | };
9 |
10 | export default UnreadCount;
11 |
--------------------------------------------------------------------------------
/packages/app/bun-env.d.ts:
--------------------------------------------------------------------------------
1 | // Generated by `bun init`
2 |
3 | declare module "*.svg" {
4 | /**
5 | * A path to the SVG file
6 | */
7 | const path: `${string}.svg`;
8 | export = path;
9 | }
10 |
11 | declare module "*.module.css" {
12 | /**
13 | * A record of class names to their corresponding CSS module classes
14 | */
15 | const classes: { readonly [key: string]: string };
16 | export = classes;
17 | }
18 |
--------------------------------------------------------------------------------
/packages/system/tests/node.ts:
--------------------------------------------------------------------------------
1 | import { NostrSystem, type SystemInterface } from "..";
2 |
3 | const Relay = "wss://relay.snort.social/";
4 |
5 | const system = new NostrSystem({}) as SystemInterface;
6 |
7 | async function test() {
8 | await system.ConnectToRelay(Relay, { read: true, write: true });
9 | setTimeout(() => {
10 | system.DisconnectRelay(Relay);
11 | }, 1000);
12 | }
13 |
14 | test().catch(console.error);
15 |
--------------------------------------------------------------------------------
/packages/system/src/filter-cache-layer.ts:
--------------------------------------------------------------------------------
1 | import type { BuiltRawReqFilter } from "./request-builder";
2 | import type { NostrEvent } from "./nostr";
3 | import type { Query } from "./query";
4 |
5 | export interface EventCache {
6 | bulkGet: (ids: Array) => Promise>;
7 | }
8 |
9 | export interface FilterCacheLayer {
10 | processFilter(q: Query, req: BuiltRawReqFilter): Promise;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/app/src/Utils/Number.ts:
--------------------------------------------------------------------------------
1 | const intl = new Intl.NumberFormat("en", {
2 | minimumFractionDigits: 0,
3 | maximumFractionDigits: 2,
4 | });
5 |
6 | export function formatShort(n: number) {
7 | if (n < 2e3) {
8 | return n;
9 | } else if (n < 1e6) {
10 | return `${intl.format(n / 1e3)}K`;
11 | } else if (n < 1e9) {
12 | return `${intl.format(n / 1e6)}M`;
13 | } else {
14 | return `${intl.format(n / 1e9)}G`;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/packages/app/src/Pages/subscribe/index.css:
--------------------------------------------------------------------------------
1 | .subscribe-page > div {
2 | margin: 5px;
3 | min-height: 400px;
4 | user-select: none;
5 | flex: 1;
6 | }
7 |
8 | .subscribe-page h2 {
9 | text-align: center;
10 | }
11 |
12 | .subscribe-page ul {
13 | padding-inline-start: 20px;
14 | }
15 |
16 | @media (max-width: 720px) {
17 | .subscribe-page {
18 | flex-direction: column;
19 | }
20 |
21 | .subscribe-page > div {
22 | flex: unset;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/packages/system-wasm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@snort/system-wasm",
3 | "version": "1.0.6",
4 | "author": "Kieran",
5 | "license": "MIT",
6 | "scripts": {
7 | "build": "wasm-pack build --release -t web -s snort && rm -f pkg/.gitignore"
8 | },
9 | "files": [
10 | "pkg/system_wasm_bg.wasm",
11 | "pkg/system_wasm.js",
12 | "pkg/system_wasm.d.ts"
13 | ],
14 | "module": "pkg/system_wasm.js",
15 | "types": "pkg/system_wasm.d.ts"
16 | }
17 |
--------------------------------------------------------------------------------
/packages/app/src/Cache/CommunityLeadersStore.tsx:
--------------------------------------------------------------------------------
1 | import { ExternalStore } from "@snort/shared";
2 |
3 | class CommunityLeadersStore extends ExternalStore> {
4 | #leaders: Array = [];
5 |
6 | setLeaders(arr: Array) {
7 | this.#leaders = arr;
8 | this.notifyChange();
9 | }
10 |
11 | takeSnapshot(): string[] {
12 | return [...this.#leaders];
13 | }
14 | }
15 |
16 | export const LeadersStore = new CommunityLeadersStore();
17 |
--------------------------------------------------------------------------------
/packages/shared/src/const.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Regex to match email address
3 | */
4 | export const EmailRegex =
5 | // eslint-disable-next-line no-useless-escape
6 | /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
7 |
8 | /**
9 | * Match any NIP-19 code
10 | */
11 | export const Bech32Regex = /(n(?:pub|profile|event|ote|addr|req|relay|chat)1[acdefghjklmnpqrstuvwxyz023456789]+)/;
12 |
--------------------------------------------------------------------------------
/packages/system-svelte/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "src",
4 | "target": "ESNext",
5 | "moduleResolution": "Bundler",
6 | "esModuleInterop": true,
7 | "noImplicitOverride": true,
8 | "module": "ESNext",
9 | "strict": true,
10 | "declaration": true,
11 | "inlineSourceMap": true,
12 | "outDir": "dist",
13 | "skipLibCheck": true
14 | },
15 | "include": ["./src/**/*.ts"],
16 | "exclude": ["**/*.test.ts"]
17 | }
18 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM oven/bun:latest AS build
2 | WORKDIR /src
3 | RUN apt update \
4 | && apt install -y --no-install-recommends git ca-certificates \
5 | && git clone --single-branch -b main https://github.com/v0l/snort \
6 | && cd snort \
7 | && bun install \
8 | && bun run build
9 |
10 | FROM nginxinc/nginx-unprivileged:mainline-alpine
11 | COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
12 | COPY --from=build /src/snort/packages/app/build /usr/share/nginx/html
13 |
--------------------------------------------------------------------------------
/packages/app/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
25 | .idea
26 |
27 | dist/
28 | dev-dist/
29 | .wrangler/
--------------------------------------------------------------------------------
/packages/app/src/Utils/emoji-search.ts:
--------------------------------------------------------------------------------
1 | import { matchSorter } from "match-sorter";
2 |
3 | export default async function searchEmoji(key: string) {
4 | const emoji = await import("emojilib");
5 | /* build proper library with included name of the emoji */
6 | const library = Object.entries(emoji.default).map(([emoji, keywords]) => ({
7 | name: keywords[0],
8 | keywords,
9 | char: emoji,
10 | }));
11 | return matchSorter(library, key, { keys: ["keywords"] });
12 | }
13 |
--------------------------------------------------------------------------------
/packages/app/src/Components/Embed/TwitchEmbed.tsx:
--------------------------------------------------------------------------------
1 | const TwitchEmbed = ({ link }: { link: string }) => {
2 | const channel = link.split("/").slice(-1);
3 |
4 | const args = `?channel=${channel}&parent=${window.location.hostname}&muted=true`;
5 | return (
6 |
12 | );
13 | };
14 |
15 | export default TwitchEmbed;
16 |
--------------------------------------------------------------------------------
/packages/app/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": ".",
4 | "target": "esnext",
5 | "module": "esnext",
6 | "jsx": "react-jsx",
7 | "moduleResolution": "bundler",
8 | "sourceMap": true,
9 | "forceConsistentCasingInFileNames": true,
10 | "strict": true,
11 | "skipLibCheck": true,
12 | "resolveJsonModule": true,
13 | "allowSyntheticDefaultImports": true,
14 | "paths": {
15 | "@/*": ["./src/*"]
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/packages/app/src/Components/User/FollowsYou.tsx:
--------------------------------------------------------------------------------
1 | import { useIntl } from "react-intl";
2 |
3 | import messages from "../messages";
4 |
5 | export interface FollowsYouProps {
6 | followsMe: boolean;
7 | }
8 |
9 | export default function FollowsYou({ followsMe }: FollowsYouProps) {
10 | const { formatMessage } = useIntl();
11 | return followsMe ? (
12 | {formatMessage(messages.FollowsYou)}
13 | ) : null;
14 | }
15 |
--------------------------------------------------------------------------------
/packages/app/CLAUDE.md:
--------------------------------------------------------------------------------
1 | Default to using Bun instead of Node.js.
2 |
3 | - Use `bun ` instead of `node ` or `ts-node `
4 | - Use `bun test` instead of `jest` or `vitest`
5 | - Use `bun build ` instead of `webpack` or `esbuild`
6 | - Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
7 | - Use `bun run