diff --git a/src/lib/components/tweet.svelte b/src/lib/components/tweet.svelte
new file mode 100644
index 0000000..125199d
--- /dev/null
+++ b/src/lib/components/tweet.svelte
@@ -0,0 +1,238 @@
+
+
+
on_click_tweet(tweet)} on:keypress>
+ {#if tweet.is_retweet}
+
+ {/if}
+
+
+
+
diff --git a/src/lib/components/tweet_list.svelte b/src/lib/components/tweet_list.svelte
new file mode 100644
index 0000000..c5ece5d
--- /dev/null
+++ b/src/lib/components/tweet_list.svelte
@@ -0,0 +1,74 @@
+
+
+{#if is_loading}
+
+
+
+{:else}
+
+ {#each tweets_data as tweet_data}
+
+ {/each}
+
+{/if}
diff --git a/src/lib/icons/calendar.svelte b/src/lib/icons/calendar.svelte
new file mode 100644
index 0000000..ea58e20
--- /dev/null
+++ b/src/lib/icons/calendar.svelte
@@ -0,0 +1,7 @@
+
diff --git a/src/lib/icons/link.svelte b/src/lib/icons/link.svelte
new file mode 100644
index 0000000..1249a1f
--- /dev/null
+++ b/src/lib/icons/link.svelte
@@ -0,0 +1,7 @@
+
diff --git a/src/lib/icons/location_pin.svelte b/src/lib/icons/location_pin.svelte
new file mode 100644
index 0000000..4230724
--- /dev/null
+++ b/src/lib/icons/location_pin.svelte
@@ -0,0 +1,7 @@
+
diff --git a/src/routes/[username]/+page.svelte b/src/routes/[username]/+page.svelte
new file mode 100644
index 0000000..115550c
--- /dev/null
+++ b/src/routes/[username]/+page.svelte
@@ -0,0 +1,347 @@
+
+
+{#if is_loading}
+
+
+
+{/if}
+{#if user_data}
+
+
+
+
+
+ {#if user_data.profile_image_url}
+

+ {/if}
+
+
+
{user_data.name}
+
@{user_data.username}
+
+
+ {#if user_data.description}
+ {@html user_data.description
+ .replace(/\n/g, '
')
+ .replace(/\B#\w*[a-zA-Z]+\w*/gi, '$&')}
+ {/if}
+
+
+
+ {#if user_data.location}
+
+
+
+
+
{user_data.location}
+
+ {/if}
+ {#if user_data.entities?.url?.urls}
+
+ {/if}
+
+
+
+
+
+
2009年3月からTwitterを利用しています
+
+
+
+
+ フォロー中
+
+
+ フォロワー
+
+
+
+
+
+
+
+{/if}
+
+
diff --git a/src/routes/api/user/[id]/tweets/+server.ts b/src/routes/api/user/[id]/tweets/+server.ts
new file mode 100644
index 0000000..af5713c
--- /dev/null
+++ b/src/routes/api/user/[id]/tweets/+server.ts
@@ -0,0 +1,41 @@
+import { type RequestHandler, json } from '@sveltejs/kit'
+import Client from 'twitter-api-sdk'
+
+export const GET: RequestHandler = async ({ locals, params }) => {
+ if (!locals.user) {
+ return new Response('Not signed in', { status: 500, headers: { error_code: '1' } })
+ }
+
+ const { id } = params
+
+ if (!id) {
+ return new Response('User ID is not valid', { status: 500, headers: { error_code: '1' } })
+ }
+
+ const twitterClient = new Client(locals.user.access_token)
+
+ try {
+ const tweets = await twitterClient.tweets.usersIdTweets(id, {
+ // max_results: 30,
+ expansions: [
+ 'author_id',
+ 'referenced_tweets.id',
+ 'referenced_tweets.id.author_id',
+ 'attachments.media_keys',
+ ],
+ 'tweet.fields': ['created_at', 'public_metrics', 'entities'],
+ 'user.fields': ['profile_image_url'],
+ 'media.fields': ['preview_image_url', 'url'],
+ // 'tweet.fields': ['author_id'],
+ // 'tweet.fields': ['created_at', 'entities', 'public_metrics'],
+ })
+
+ // console.log('tweets', tweets)
+ // console.log('media', tweets.includes?.media)
+
+ return json(tweets)
+ } catch (err) {
+ console.error(err)
+ return new Response('Error loading tweets', { status: 500, headers: { error_code: '2' } })
+ }
+}
diff --git a/src/routes/api/user/[username]/+server.ts b/src/routes/api/user/[username]/+server.ts
new file mode 100644
index 0000000..ec2069a
--- /dev/null
+++ b/src/routes/api/user/[username]/+server.ts
@@ -0,0 +1,39 @@
+import { type RequestHandler, json } from '@sveltejs/kit'
+import Client from 'twitter-api-sdk'
+
+export const GET: RequestHandler = async ({ locals, params }) => {
+ if (!locals.user) {
+ return new Response('Not signed in', { status: 500, headers: { error_code: '1' } })
+ }
+
+ const { username } = params
+
+ if (!username) return new Response('No username', { status: 500, headers: { error_code: '3' } })
+
+ const twitterClient = new Client(locals.user.access_token)
+
+ try {
+ const profile = await twitterClient.users.findUserByUsername(username, {
+ 'user.fields': [
+ 'username',
+ 'created_at',
+ 'description',
+ 'entities',
+ 'id',
+ 'location',
+ 'name',
+ 'pinned_tweet_id',
+ 'profile_image_url',
+ 'protected',
+ 'public_metrics',
+ 'url',
+ 'verified',
+ 'withheld',
+ ],
+ })
+ return json(profile)
+ } catch (err) {
+ console.error(err)
+ return new Response('Error loading profile', { status: 500, headers: { error_code: '2' } })
+ }
+}
diff --git a/src/routes/home/+page.svelte b/src/routes/home/+page.svelte
index 0ad4e98..105c781 100644
--- a/src/routes/home/+page.svelte
+++ b/src/routes/home/+page.svelte
@@ -1,78 +1,12 @@
@@ -161,151 +87,7 @@
- {#if is_loading}
-
-
-
- {:else}
-
- {#each tweets_data as tweet_data}
- {@const tweet = new Tweet(
- tweet_data,
- user_data_map,
- referenced_tweets_data_map,
- media_data_map
- )}
-
on_click_tweet(tweet)}
- on:keypress
- >
- {#if tweet.is_retweet}
-
- {/if}
-
-
- {/each}
-
- {/if}
+
@@ -338,83 +120,4 @@
.post_element {
position: relative;
}
-
- .tweet_container {
- padding: 16px;
- gap: 6px;
- cursor: pointer;
- }
-
- .tweet_container:hover {
- transition: 0.2s;
- background-color: rgb(245, 248, 250);
- }
-
- .avatar_above {
- min-width: 48px;
- justify-content: flex-end;
- }
-
- .tweet_element {
- gap: 12px;
- }
-
- .tweet_body {
- gap: 12px;
- min-width: 0;
- flex: auto;
-
- font-size: 16px;
- line-height: 20px;
- font-weight: 400;
- }
-
- .text_column {
- gap: 8px;
- min-width: 0;
- overflow-wrap: break-word;
- }
-
- .username_row {
- gap: 4px;
-
- font-weight: 400;
- font-size: 15px;
- line-height: 20px;
- }
-
- .time {
- white-space: nowrap;
- }
-
- .time a {
- color: var(--gray-color);
- }
-
- .action_row {
- gap: 10px;
-
- color: var(--gray-color);
- font-size: 13px;
- line-height: 16px;
- }
-
- .action {
- gap: 10px;
- flex: 1;
- }
-
- .action_up_arrow {
- flex: 0;
- }
-
- .icon_text {
- font-size: 14px;
- }
-
- .action_icon {
- width: 17.5px;
- height: 17.5px;
- fill: var(--gray-color);
- }