+7
-14
src/components/FollowingItem.svelte
+7
-14
src/components/FollowingItem.svelte
···
1
-
<script lang="ts" module>
2
-
const profileCache = new SvelteMap<string, { displayName?: string; handle: string }>();
3
-
</script>
4
-
5
<script lang="ts">
6
import ProfilePicture from './ProfilePicture.svelte';
7
import BlockedUserIndicator from './BlockedUserIndicator.svelte';
···
10
import type { Did } from '@atcute/lexicons';
11
import type { calculateFollowedUserStats, Sort } from '$lib/following';
12
import { resolveDidDoc, type AtpClient } from '$lib/at/client.svelte';
13
-
import { SvelteMap } from 'svelte/reactivity';
14
-
import { router, getBlockRelationship } from '$lib/state.svelte';
15
import { map } from '$lib/result';
16
17
interface Props {
···
31
);
32
const isBlocked = $derived(blockRel.userBlocked || blockRel.blockedByTarget);
33
34
-
const cached = $derived(profileCache.get(did));
35
-
const displayName = $derived(cached?.displayName);
36
-
const handle = $derived(cached?.handle ?? 'handle.invalid');
37
38
let error = $state('');
39
40
const loadProfile = async (targetDid: Did) => {
41
-
if (profileCache.has(targetDid)) return;
42
43
try {
44
const [profileRes, handleRes] = await Promise.all([
···
47
]);
48
if (did !== targetDid) return;
49
50
-
profileCache.set(targetDid, {
51
-
handle: handleRes.ok ? handleRes.value : handle,
52
-
displayName: profileRes.ok ? profileRes.value.displayName : displayName
53
-
});
54
} catch (e) {
55
if (did !== targetDid) return;
56
console.error(`failed to load profile for ${targetDid}`, e);
···
1
<script lang="ts">
2
import ProfilePicture from './ProfilePicture.svelte';
3
import BlockedUserIndicator from './BlockedUserIndicator.svelte';
···
6
import type { Did } from '@atcute/lexicons';
7
import type { calculateFollowedUserStats, Sort } from '$lib/following';
8
import { resolveDidDoc, type AtpClient } from '$lib/at/client.svelte';
9
+
import { router, getBlockRelationship, profiles, handles } from '$lib/state.svelte';
10
import { map } from '$lib/result';
11
12
interface Props {
···
26
);
27
const isBlocked = $derived(blockRel.userBlocked || blockRel.blockedByTarget);
28
29
+
const displayName = $derived(profiles.get(did)?.displayName);
30
+
const handle = $derived(handles.get(did) ?? 'loading...');
31
32
let error = $state('');
33
34
const loadProfile = async (targetDid: Did) => {
35
+
if (profiles.has(targetDid) && handles.has(targetDid)) return;
36
37
try {
38
const [profileRes, handleRes] = await Promise.all([
···
41
]);
42
if (did !== targetDid) return;
43
44
+
if (profileRes.ok) profiles.set(targetDid, profileRes.value);
45
+
if (handleRes.ok) handles.set(targetDid, handleRes.value);
46
+
else handles.set(targetDid, 'handle.invalid');
47
} catch (e) {
48
if (did !== targetDid) return;
49
console.error(`failed to load profile for ${targetDid}`, e);
+18
-12
src/components/PostComposer.svelte
+18
-12
src/components/PostComposer.svelte
···
44
);
45
46
const uploadVideo = async (blobUrl: string, mimeType: string) => {
47
-
const blob = await (await fetch(blobUrl)).blob();
48
-
return await client.uploadVideo(blob, mimeType, (status) => {
49
if (status.stage === 'uploading' && status.progress !== undefined) {
50
_state.blobsState.set(blobUrl, { state: 'uploading', progress: status.progress * 0.5 });
51
} else if (status.stage === 'processing' && status.progress !== undefined) {
···
57
});
58
};
59
const uploadImage = async (blobUrl: string) => {
60
-
const blob = await (await fetch(blobUrl)).blob();
61
-
return await client.uploadBlob(blob, (progress) => {
62
_state.blobsState.set(blobUrl, { state: 'uploading', progress });
63
});
64
};
···
101
video: upload.blob
102
};
103
}
104
-
console.log('media', media);
105
106
const record: AppBskyFeedPost.Main = {
107
$type: 'app.bsky.feed.post',
···
156
let fileInputEl: HTMLInputElement | undefined = $state();
157
let selectingFile = $state(false);
158
159
const unfocus = () => (_state.focus = 'null');
160
161
const handleFiles = (files: File[]) => {
162
-
if (!files || files.length === 0) return;
163
164
const existingImages =
165
_state.attachedMedia?.$type === 'app.bsky.embed.images' ? _state.attachedMedia.images : [];
···
220
};
221
}
222
223
-
const handleUpload = (blobUrl: string, blob: Result<AtpBlob<string>, string>) => {
224
-
if (blob.ok) _state.blobsState.set(blobUrl, { state: 'uploaded', blob: blob.value });
225
-
else _state.blobsState.set(blobUrl, { state: 'error', message: blob.error });
226
};
227
228
const media = _state.attachedMedia;
···
459
fileInputEl?.click();
460
}}
461
onmousedown={(e) => e.preventDefault()}
462
-
disabled={_state.attachedMedia?.$type === 'app.bsky.embed.video' ||
463
-
(_state.attachedMedia?.$type === 'app.bsky.embed.images' &&
464
-
_state.attachedMedia.images.length >= 4)}
465
class="rounded-sm p-1.5 transition-all duration-150 enabled:hover:scale-110 disabled:cursor-not-allowed disabled:opacity-50"
466
style="background: color-mix(in srgb, {color} 15%, transparent); color: {color};"
467
title="attach media"
···
44
);
45
46
const uploadVideo = async (blobUrl: string, mimeType: string) => {
47
+
const file = await (await fetch(blobUrl)).blob();
48
+
return await client.uploadVideo(file, mimeType, (status) => {
49
if (status.stage === 'uploading' && status.progress !== undefined) {
50
_state.blobsState.set(blobUrl, { state: 'uploading', progress: status.progress * 0.5 });
51
} else if (status.stage === 'processing' && status.progress !== undefined) {
···
57
});
58
};
59
const uploadImage = async (blobUrl: string) => {
60
+
const file = await (await fetch(blobUrl)).blob();
61
+
return await client.uploadBlob(file, (progress) => {
62
_state.blobsState.set(blobUrl, { state: 'uploading', progress });
63
});
64
};
···
101
video: upload.blob
102
};
103
}
104
+
// console.log('media', media);
105
106
const record: AppBskyFeedPost.Main = {
107
$type: 'app.bsky.feed.post',
···
156
let fileInputEl: HTMLInputElement | undefined = $state();
157
let selectingFile = $state(false);
158
159
+
const canUpload = $derived(
160
+
!(
161
+
_state.attachedMedia?.$type === 'app.bsky.embed.video' ||
162
+
(_state.attachedMedia?.$type === 'app.bsky.embed.images' &&
163
+
_state.attachedMedia.images.length >= 4)
164
+
)
165
+
);
166
+
167
const unfocus = () => (_state.focus = 'null');
168
169
const handleFiles = (files: File[]) => {
170
+
if (!canUpload || !files || files.length === 0) return;
171
172
const existingImages =
173
_state.attachedMedia?.$type === 'app.bsky.embed.images' ? _state.attachedMedia.images : [];
···
228
};
229
}
230
231
+
const handleUpload = (blobUrl: string, res: Result<AtpBlob<string>, string>) => {
232
+
if (res.ok) _state.blobsState.set(blobUrl, { state: 'uploaded', blob: res.value });
233
+
else _state.blobsState.set(blobUrl, { state: 'error', message: res.error });
234
};
235
236
const media = _state.attachedMedia;
···
467
fileInputEl?.click();
468
}}
469
onmousedown={(e) => e.preventDefault()}
470
+
disabled={!canUpload}
471
class="rounded-sm p-1.5 transition-all duration-150 enabled:hover:scale-110 disabled:cursor-not-allowed disabled:opacity-50"
472
style="background: color-mix(in srgb, {color} 15%, transparent); color: {color};"
473
title="attach media"
+1
-1
src/lib/at/client.svelte.ts
+1
-1
src/lib/at/client.svelte.ts