replies timeline only, appview-less bluesky client

feat: add readme, add divider between post chains and fix post composer unfocusing when post button is clicked

Changed files
+34 -44
resources
src
components
routes
+11 -34
README.md
···
-
# sv
+
## nucleus
-
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
+
a WIP replies timeline only (eg. it only shows replies to your posts and your own posts) appview-less (it does not use the bluesky appview, but rather uses [microcosm](https://www.microcosm.blue/) services) bluesky client. it is implemented in SvelteKit and uses [atcute](https://tangled.org/@mary.my.id/atcute).
-
## Creating a project
+
![screenshot](./resources/screenshot.png)
-
If you're seeing this, you've probably already done this step. Congrats!
+
### todos
-
```sh
-
# create a new project in the current directory
-
npx sv create
-
-
# create a new project in my-app
-
npx sv create my-app
-
```
-
-
## Developing
-
-
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
-
-
```sh
-
npm run dev
-
-
# or start the server and open the app in a new browser tab
-
npm run dev -- --open
-
```
-
-
## Building
-
-
To create a production version of your app:
-
-
```sh
-
npm run build
-
```
-
-
You can preview the production build with `npm run preview`.
-
-
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
+
- [ ] properly implement auth (current state is just for deving) (we want oauth only)
+
- [ ] implement popouts for showing full chains instead of expanding in the timeline
+
- [ ] implement moderation (mutes, muted words etc., use blocks from `app.bsky.graph.block`)
+
- [ ] profile view popout
+
- [ ] consider showing posts that mention / quote the user..
+
- [ ] notifications when replied to (and mentioned and quoted?)
+
- [ ] basic filtering settings for the timeline (dont show self posts and if we implement mentioned / quoted add toggles for those as well)
resources/screenshot.png

This is a binary file and will not be displayed.

+19 -9
src/components/PostComposer.svelte
···
let isFocused = $state(false);
let textareaEl: HTMLTextAreaElement | undefined = $state();
+
const unfocus = () => {
+
isFocused = false;
+
quoting = undefined;
+
replying = undefined;
+
};
+
const doPost = () => {
if (postText.length === 0 || postText.length > 300) return;
···
onPostSent(res.value);
postText = '';
info = 'posted!';
-
isFocused = false;
-
quoting = undefined;
-
replying = undefined;
+
unfocus();
setTimeout(() => (info = ''), 1000 * 0.8);
} else {
// todo: add a way to clear error
···
<div class="min-h-16"></div>
{/if}
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
<div
+
onmousedown={(e) => {
+
if (isFocused) {
+
e.preventDefault();
+
}
+
}}
class="flex max-w-full rounded-sm border-2 shadow-lg transition-all duration-300"
class:min-h-16={!isFocused}
class:items-center={!isFocused}
···
bind:this={textareaEl}
bind:value={postText}
onfocus={() => (isFocused = true)}
-
onblur={() => {
-
isFocused = false;
-
quoting = undefined;
-
replying = undefined;
-
}}
+
onblur={unfocus}
onkeydown={(event) => {
+
if (event.key === 'Escape') unfocus();
if (event.key === 'Enter' && (event.metaKey || event.ctrlKey)) doPost();
}}
placeholder="what's on your mind?"
···
{postText.length} / 300
</span>
<button
-
onclick={doPost}
+
onmousedown={(e) => {
+
e.preventDefault();
+
doPost();
+
}}
disabled={postText.length === 0 || postText.length > 300}
class="action-button border-none px-5 text-(--nucleus-fg)/94 disabled:cursor-not-allowed disabled:opacity-50 disabled:hover:scale-100"
style="background: color-mix(in srgb, {color} 87%, transparent);"
+4 -1
src/routes/+page.svelte
···
{#snippet threadsView()}
{#each threads as thread (thread.rootUri)}
-
<div class="flex {reverseChronological ? 'flex-col' : 'flex-col-reverse'} mb-6.5">
+
<div class="flex w-full shrink-0 {reverseChronological ? 'flex-col' : 'flex-col-reverse'}">
{#if thread.branchParentPost}
{@render replyPost(thread.branchParentPost)}
{/if}
···
{/if}
{/each}
</div>
+
<div
+
class="mx-8 mt-3 mb-4 h-px bg-gradient-to-r from-(--nucleus-accent)/30 to-(--nucleus-accent2)/30"
+
></div>
{/each}
{/snippet}