Skip to content

Commit bcfdcf9

Browse files
feat: added post api and connected all the classes (#60)
Co-authored-by: TAKAHASHI Shuuji <shuuji3@gmail.com>
1 parent e41bfa9 commit bcfdcf9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+3631
-1107
lines changed

biome.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"enabled": true
55
},
66
"files": {
7-
"include": ["docs", "packages/*"],
7+
"include": ["docs", "packages/*", "examples/*"],
88
"ignore": [
99
".github",
1010
".gitignore",

examples/user-profile/.gitignore

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
pnpm-debug.log*
8+
lerna-debug.log*
9+
10+
node_modules
11+
dist
12+
dist-ssr
13+
*.local
14+
15+
# Editor directories and files
16+
.vscode/*
17+
!.vscode/extensions.json
18+
.idea
19+
.DS_Store
20+
*.suo
21+
*.ntvs*
22+
*.njsproj
23+
*.sln
24+
*.sw?

examples/user-profile/index.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
7+
<title>Vite + React + TS</title>
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
<script type="module" src="/src/main.tsx"></script>
12+
</body>
13+
</html>

examples/user-profile/package.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"name": "user-profile",
3+
"private": true,
4+
"version": "0.0.0",
5+
"type": "module",
6+
"scripts": {
7+
"dev": "vite",
8+
"build": "tsc -b && vite build",
9+
"preview": "vite preview"
10+
},
11+
"dependencies": {
12+
"@tsky/client": "workspace:*",
13+
"react": "^19.0.0",
14+
"react-dom": "^19.0.0"
15+
},
16+
"devDependencies": {
17+
"@tailwindcss/vite": "^4.0.12",
18+
"@tsky/lexicons": "workspace:*",
19+
"@types/react": "^19.0.10",
20+
"@types/react-dom": "^19.0.4",
21+
"@vitejs/plugin-react": "^4.3.4",
22+
"globals": "^15.15.0",
23+
"tailwindcss": "^4.0.12",
24+
"typescript": "~5.7.2",
25+
"typescript-eslint": "^8.24.1",
26+
"vite": "^6.2.0"
27+
}
28+
}
27 KB
Loading

examples/user-profile/src/App.tsx

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
import { type ActorProfile, createAgent } from '@tsky/client';
2+
import type { At } from '@tsky/lexicons';
3+
import { useEffect, useState } from 'react';
4+
5+
async function getUserProfile(identity: string) {
6+
try {
7+
const agent = await createAgent({
8+
options: {
9+
service: 'https://public.api.bsky.app',
10+
},
11+
});
12+
13+
let did = identity;
14+
15+
if (!did.startsWith('did:')) {
16+
const _id = await agent.resolveDIDFromHandle(identity);
17+
did = _id.did;
18+
}
19+
20+
const actor = await agent.actor(did as At.DID);
21+
22+
return actor.profile();
23+
} catch (err) {
24+
console.error(err);
25+
}
26+
}
27+
28+
function App() {
29+
const [search, setSearch] = useState<string>();
30+
const [user, setUser] = useState<ActorProfile>();
31+
32+
useEffect(() => {
33+
if (search) {
34+
getUserProfile(search).then(setUser);
35+
}
36+
}, [search]);
37+
38+
return (
39+
<main className="w-full h-screen bg-white text-black dark:bg-zinc-950 dark:text-white">
40+
<div className="max-w-lg mx-auto w-full py-6 space-y-6">
41+
<form
42+
action={(values) => {
43+
const value = values.get('search');
44+
45+
if (value) {
46+
setSearch(value.toString());
47+
}
48+
}}
49+
className="flex gap-3 items-center"
50+
>
51+
<a
52+
href="https://github.com/tsky-dev/tsky"
53+
target="_blank"
54+
rel="noreferrer"
55+
>
56+
<img
57+
src="/tsky-logo.png"
58+
alt="tsky logo"
59+
className="size-9 min-w-9 rounded"
60+
/>
61+
</a>
62+
<input
63+
name="search"
64+
type="search"
65+
placeholder="Search Profile"
66+
className="w-full border appearance-none outline-none dark:text-zinc-100 transition-all placeholder:text-zinc-400 dark:placeholder:text-zinc-500 py-1.5 text-base hover:border-blue-500 dark:hover:border-blue-300 border-zinc-300 dark:border-zinc-700 focus-visible:ring-2 ring-offset-2 ring-offset-white dark:ring-offset-zinc-950 focus:border-blue-500 dark:focus:border-blue-300 ring-blue-300 dark:ring-blue-100 bg-transparent rounded-l-md rounded-r-md px-3"
67+
/>
68+
<button
69+
type="submit"
70+
className="cursor-pointer whitespace-nowrap font-semibold h-max transition-all border select-none outline-none px-3 py-2 text-sm flex items-center justify-center rounded-md text-white dark:text-black focus-visible:ring-2 ring-offset-2 ring-offset-white dark:ring-offset-zinc-950 border-transparent bg-blue-500 hover:bg-blue-600 dark:bg-blue-300/90 dark:hover:bg-blue-400/80 ring-blue-300 dark:ring-blue-100"
71+
>
72+
Search
73+
</button>
74+
</form>
75+
{user && (
76+
<div className="w-full rounded-xl ring-1 ring-transparent dark:ring-white/5 overflow-hidden drop-shadow-lg dark:drop-shadow-none">
77+
<img
78+
src={user.banner}
79+
alt="banner"
80+
className="h-36 w-full object-cover"
81+
/>
82+
<div className="bg-white dark:bg-zinc-900 p-4">
83+
<div className="flex items-end justify-between -mt-16">
84+
<div className="rounded-full size-28 border-[3px] border-white dark:border-zinc-900 overflow-hidden">
85+
<img
86+
src={user.avatar}
87+
alt="profile-pic"
88+
className="size-full object-cover"
89+
/>
90+
</div>
91+
<a
92+
href={`https://bsky.app/profile/${user.handle}`}
93+
target="_blank"
94+
rel="noreferrer"
95+
className="rounded-full"
96+
>
97+
<button
98+
type="button"
99+
className="whitespace-nowrap cursor-pointer font-semibold h-max transition-all border select-none outline-none px-3 py-2 text-sm flex items-center justify-center rounded-full text-white dark:text-black focus-visible:ring-2 ring-offset-2 ring-offset-white dark:ring-offset-zinc-950 border-transparent bg-blue-500 hover:bg-blue-600 dark:bg-blue-300/90 dark:hover:bg-blue-400/80 ring-blue-300 dark:ring-blue-100"
100+
>
101+
<svg
102+
xmlns="http://www.w3.org/2000/svg"
103+
fill="none"
104+
viewBox="0 0 24 24"
105+
strokeWidth="3"
106+
stroke="currentColor"
107+
className="size-3.5 mr-1"
108+
>
109+
<title>Plus Icon</title>
110+
<path
111+
strokeLinecap="round"
112+
strokeLinejoin="round"
113+
d="M12 4.5v15m7.5-7.5h-15"
114+
/>
115+
</svg>
116+
Follow
117+
</button>
118+
</a>
119+
</div>
120+
<h1 className="text-lg font-semibold mt-2">{user.displayName}</h1>
121+
<p className="text-sm text-zinc-500 dark:text-zinc-400">
122+
@{user.handle}
123+
</p>
124+
<div className="mt-3 flex items-center gap-3 text-sm font-semibold">
125+
<p>
126+
{user.followersCount}
127+
<span className="font-normal text-zinc-500 dark:text-zinc-400">
128+
{' '}
129+
Followers
130+
</span>
131+
</p>
132+
<p>
133+
{user.followsCount}
134+
<span className="font-normal text-zinc-500 dark:text-zinc-400">
135+
{' '}
136+
Following
137+
</span>
138+
</p>
139+
</div>
140+
<p className="mt-4">{user.description}</p>
141+
<div className="flex items-end justify-between">
142+
<p className="text-sm text-zinc-400 dark:text-zinc-500">
143+
Made with Tsky
144+
</p>
145+
<a
146+
href="https://github.com/tsky-dev/tsky"
147+
target="_blank"
148+
rel="noreferrer"
149+
>
150+
<svg
151+
xmlns="http://www.w3.org/2000/svg"
152+
fill="none"
153+
viewBox="0 0 320 286"
154+
className="size-8"
155+
>
156+
<title>Bluesky Icon</title>
157+
<path
158+
fill="rgb(10,122,255)"
159+
d="M69.364 19.146c36.687 27.806 76.147 84.186 90.636 114.439 14.489-30.253 53.948-86.633 90.636-114.439C277.107-.917 320-16.44 320 32.957c0 9.865-5.603 82.875-8.889 94.729-11.423 41.208-53.045 51.719-90.071 45.357 64.719 11.12 81.182 47.953 45.627 84.785-80 82.874-106.667-44.333-106.667-44.333s-26.667 127.207-106.667 44.333c-35.555-36.832-19.092-73.665 45.627-84.785-37.026 6.362-78.648-4.149-90.071-45.357C5.603 115.832 0 42.822 0 32.957 0-16.44 42.893-.917 69.364 19.147Z"
160+
/>
161+
</svg>
162+
</a>
163+
</div>
164+
</div>
165+
</div>
166+
)}
167+
</div>
168+
</main>
169+
);
170+
}
171+
172+
export default App;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@import "tailwindcss";

examples/user-profile/src/main.tsx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { StrictMode } from 'react';
2+
import { createRoot } from 'react-dom/client';
3+
import './index.css';
4+
import App from './App.tsx';
5+
6+
const root = document.getElementById('root');
7+
8+
if (!root) {
9+
throw new Error('No root element found');
10+
}
11+
12+
createRoot(root).render(
13+
<StrictMode>
14+
<App />
15+
</StrictMode>,
16+
);
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/// <reference types="vite/client" />
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"compilerOptions": {
3+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
4+
"target": "ES2020",
5+
"useDefineForClassFields": true,
6+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
7+
"module": "ESNext",
8+
"skipLibCheck": true,
9+
"moduleResolution": "bundler",
10+
"allowImportingTsExtensions": true,
11+
"isolatedModules": true,
12+
"moduleDetection": "force",
13+
"noEmit": true,
14+
"jsx": "react-jsx",
15+
"strict": true,
16+
"noUnusedLocals": true,
17+
"noUnusedParameters": true,
18+
"noFallthroughCasesInSwitch": true,
19+
"noUncheckedSideEffectImports": true
20+
},
21+
"include": ["src"]
22+
}

0 commit comments

Comments
 (0)