Profile: supports webfinger

* timelines: add emptyTimeline
This commit is contained in:
thislight 2025-01-02 18:11:35 +08:00
parent f860baa376
commit 7153a1fec1
No known key found for this signature in database
GPG key ID: FCFE5192241CCD4E
2 changed files with 96 additions and 14 deletions

View file

@ -248,6 +248,64 @@ export type TimelineResource<R> = [
{ refetch(info?: TimelineFetchDirection): void }, { refetch(info?: TimelineFetchDirection): void },
]; ];
export const emptyTimeline = {
list() {
return emptyTimeline;
},
setDirection() {
return emptyTimeline;
},
async next(): Promise<IteratorResult<any, undefined>> {
return {
value: undefined,
done: true,
};
},
getDirection(): TimelineFetchDirection {
return "next";
},
clone() {
return emptyTimeline;
},
async return(): Promise<IteratorResult<any, undefined>> {
return {
value: undefined,
done: true,
};
},
async throw(e?: unknown) {
throw e;
},
async *values() {},
async *[Symbol.asyncIterator](): AsyncIterator<any[], undefined> {
return undefined;
},
async then<TNext, ENext>(
onresolve?: null | ((value: any[]) => TNext | PromiseLike<TNext>),
onrejected?: null | ((reason: unknown) => ENext | PromiseLike<ENext>),
) {
try {
if (!onresolve) {
throw new TypeError("no onresolve");
}
return await onresolve([]);
} catch (reason) {
if (!onrejected) {
throw reason;
}
return await onrejected(reason);
}
},
};
/** /**
* Create auto managed timeline controls. * Create auto managed timeline controls.
* *

View file

@ -47,7 +47,11 @@ import { useSessionForAcctStr } from "../masto/clients";
import { resolveCustomEmoji } from "../masto/toot"; import { resolveCustomEmoji } from "../masto/toot";
import { FastAverageColor } from "fast-average-color"; import { FastAverageColor } from "fast-average-color";
import { useWindowSize } from "@solid-primitives/resize-observer"; import { useWindowSize } from "@solid-primitives/resize-observer";
import { createTimeline, createTimelineSnapshot } from "../masto/timelines"; import {
createTimeline,
createTimelineSnapshot,
emptyTimeline,
} from "../masto/timelines";
import TootList from "../timelines/TootList"; import TootList from "../timelines/TootList";
import { createTimeSource, TimeSourceProvider } from "~platform/timesrc"; import { createTimeSource, TimeSourceProvider } from "~platform/timesrc";
import TootFilterButton from "./TootFilterButton"; import TootFilterButton from "./TootFilterButton";
@ -102,6 +106,9 @@ const Profile: Component = () => {
const [profileUncaught] = createResource( const [profileUncaught] = createResource(
() => [session().client, params.id] as const, () => [session().client, params.id] as const,
async ([client, id]) => { async ([client, id]) => {
if (id.startsWith("@")) {
return await client.v1.accounts.lookup({ acct: id.slice(1) });
}
return await client.v1.accounts.$select(id).fetch(); return await client.v1.accounts.$select(id).fetch();
}, },
); );
@ -114,6 +121,15 @@ const Profile: Component = () => {
} }
}; };
const profileAcctId = () => {
if (params.id.startsWith("@")) {
// Webfinger
return profile()?.id;
} else {
return params.id;
}
};
const isCurrentSessionProfile = () => { const isCurrentSessionProfile = () => {
return (session().account as Account).inf?.url === profile()?.url; return (session().account as Account).inf?.url === profile()?.url;
}; };
@ -125,26 +141,33 @@ const Profile: Component = () => {
original: true, original: true,
}); });
const recentTimeline = () => {
const id = profileAcctId();
if (id) {
return session().client.v1.accounts.$select(id).statuses;
} else {
return emptyTimeline;
}
};
const [recentToots, recentTootChunk, { refetch: refetchRecentToots }] = const [recentToots, recentTootChunk, { refetch: refetchRecentToots }] =
createTimeline( createTimeline(recentTimeline, () => {
() => session().client.v1.accounts.$select(params.id).statuses,
() => {
const { boost, reply } = recentTootFilter(); const { boost, reply } = recentTootFilter();
return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply }; return { limit: 20, excludeReblogs: !boost, excludeReplies: !reply };
}, });
);
const [pinnedToots, pinnedTootChunk] = createTimelineSnapshot( const [pinnedToots, pinnedTootChunk] = createTimelineSnapshot(
() => session().client.v1.accounts.$select(params.id).statuses, recentTimeline,
() => { () => {
return { limit: 20, pinned: true }; return { limit: 20, pinned: true };
}, },
); );
const [relationshipUncaught, { mutate: mutateRelationship }] = createResource( const [relationshipUncaught, { mutate: mutateRelationship }] = createResource(
() => [session(), params.id] as const, () => [session(), profileAcctId()] as const,
async ([sess, id]) => { async ([sess, id]) => {
if (!sess.account) return; // No account, no relation if (!sess.account || !id) return; // No account, no relation
const relations = await session().client.v1.accounts.relationships.fetch({ const relations = await session().client.v1.accounts.relationships.fetch({
id: [id], id: [id],
}); });
@ -177,16 +200,17 @@ const Profile: Component = () => {
const toggleSubscribeHome = async (event: Event) => { const toggleSubscribeHome = async (event: Event) => {
const client = session().client; const client = session().client;
if (!session().account) return; const acctId = profileAcctId();
if (!session().account || !acctId) return;
const isSubscribed = relationship()?.following ?? false; const isSubscribed = relationship()?.following ?? false;
mutateRelationship((x) => Object.assign({ following: !isSubscribed }, x)); mutateRelationship((x) => Object.assign({ following: !isSubscribed }, x));
subscribeMenuState.onClose(event); subscribeMenuState.onClose(event);
if (isSubscribed) { if (isSubscribed) {
const nrel = await client.v1.accounts.$select(params.id).unfollow(); const nrel = await client.v1.accounts.$select(acctId).unfollow();
mutateRelationship(nrel); mutateRelationship(nrel);
} else { } else {
const nrel = await client.v1.accounts.$select(params.id).follow(); const nrel = await client.v1.accounts.$select(acctId).follow();
mutateRelationship(nrel); mutateRelationship(nrel);
} }
}; };