Compare commits
11 Commits
jazz-svelt
...
new-deep-l
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ceb060243a | ||
|
|
a70bebb96a | ||
|
|
b3b2507c35 | ||
|
|
6a8fa16b49 | ||
|
|
1f08807701 | ||
|
|
ba4a7f6170 | ||
|
|
a2854e3602 | ||
|
|
4ea87dc494 | ||
|
|
d8c87c5314 | ||
|
|
46f624a12e | ||
|
|
86ce770f38 |
5
.changeset/fast-beans-decide.md
Normal file
5
.changeset/fast-beans-decide.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"jazz-tools": minor
|
||||
---
|
||||
|
||||
Implement new API for deep loading
|
||||
@@ -28,7 +28,7 @@ export default function Conversation() {
|
||||
const { me } = useAccount();
|
||||
const [chat, setChat] = useState<Chat>();
|
||||
const [message, setMessage] = useState("");
|
||||
const loadedChat = useCoState(Chat, chat?.id, [{}]);
|
||||
const loadedChat = useCoState(Chat, chat?.id, { resolve: { $each: true } });
|
||||
const navigation = useNavigation();
|
||||
const [isUploading, setIsUploading] = useState(false);
|
||||
|
||||
@@ -71,7 +71,7 @@ export default function Conversation() {
|
||||
|
||||
const loadChat = async (chatId: ID<Chat>) => {
|
||||
try {
|
||||
const chat = await Chat.load(chatId, me, []);
|
||||
const chat = await Chat.load(chatId, me);
|
||||
setChat(chat);
|
||||
} catch (error) {
|
||||
console.log("Error loading chat", error);
|
||||
|
||||
@@ -20,7 +20,7 @@ import { Chat, Message } from "./schema";
|
||||
export default function ChatScreen({ navigation }: { navigation: any }) {
|
||||
const { me, logOut } = useAccount();
|
||||
const [chatId, setChatId] = useState<ID<Chat>>();
|
||||
const loadedChat = useCoState(Chat, chatId, [{}]);
|
||||
const loadedChat = useCoState(Chat, chatId, { resolve: { $each: true } });
|
||||
const [message, setMessage] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -49,7 +49,7 @@ export default defineComponent({
|
||||
},
|
||||
},
|
||||
setup(props) {
|
||||
const chat = useCoState(Chat, props.chatId, [{}]);
|
||||
const chat = useCoState(Chat, props.chatId, { resolve: { $each: true } });
|
||||
const showNLastMessages = ref(30);
|
||||
|
||||
const displayedMessages = computed(() => {
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from "./ui.tsx";
|
||||
|
||||
export function ChatScreen(props: { chatID: ID<Chat> }) {
|
||||
const chat = useCoState(Chat, props.chatID, [{}]);
|
||||
const chat = useCoState(Chat, props.chatID, { resolve: { $each: true } });
|
||||
const [showNLastMessages, setShowNLastMessages] = useState(30);
|
||||
|
||||
if (!chat)
|
||||
|
||||
@@ -12,7 +12,9 @@ import {
|
||||
} from "./schema.ts";
|
||||
|
||||
export function CreateOrder() {
|
||||
const { me } = useAccount({ root: { draft: {}, orders: [] } });
|
||||
const { me } = useAccount({
|
||||
resolve: { root: { draft: true, orders: true } },
|
||||
});
|
||||
const router = useIframeHashRouter();
|
||||
const [errors, setErrors] = useState<string[]>([]);
|
||||
|
||||
@@ -60,7 +62,7 @@ function CreateOrderForm({
|
||||
onSave: (draft: DraftBubbleTeaOrder) => void;
|
||||
}) {
|
||||
const draft = useCoState(DraftBubbleTeaOrder, id, {
|
||||
addOns: [],
|
||||
resolve: { addOns: true },
|
||||
});
|
||||
|
||||
if (!draft) return;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useAccount } from "jazz-react";
|
||||
|
||||
export function DraftIndicator() {
|
||||
const { me } = useAccount({
|
||||
root: { draft: {} },
|
||||
resolve: { root: { draft: true } },
|
||||
});
|
||||
|
||||
if (me?.root.draft?.hasChanges) {
|
||||
|
||||
@@ -6,7 +6,7 @@ import { OrderThumbnail } from "./OrderThumbnail.tsx";
|
||||
import { BubbleTeaOrder } from "./schema.ts";
|
||||
|
||||
export function EditOrder(props: { id: ID<BubbleTeaOrder> }) {
|
||||
const order = useCoState(BubbleTeaOrder, props.id, []);
|
||||
const order = useCoState(BubbleTeaOrder, props.id);
|
||||
|
||||
if (!order) return;
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import { OrderThumbnail } from "./OrderThumbnail.tsx";
|
||||
|
||||
export function Orders() {
|
||||
const { me } = useAccount({
|
||||
root: { orders: [] },
|
||||
resolve: { root: { orders: true } },
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -19,10 +19,7 @@ export function HomePage({ mediaPlayer }: { mediaPlayer: MediaPlayer }) {
|
||||
* access rights to CoValues. We get it from the top-level provider `<WithJazz/>`.
|
||||
*/
|
||||
const { me } = useAccount({
|
||||
root: {
|
||||
rootPlaylist: {},
|
||||
playlists: [],
|
||||
},
|
||||
resolve: { root: { rootPlaylist: true, playlists: true } },
|
||||
});
|
||||
|
||||
const navigate = useNavigate();
|
||||
@@ -46,8 +43,9 @@ export function HomePage({ mediaPlayer }: { mediaPlayer: MediaPlayer }) {
|
||||
|
||||
const params = useParams<{ playlistId: ID<Playlist> }>();
|
||||
const playlistId = params.playlistId ?? me?.root._refs.rootPlaylist.id;
|
||||
|
||||
const playlist = useCoState(Playlist, playlistId, {
|
||||
tracks: [],
|
||||
resolve: { tracks: true },
|
||||
});
|
||||
|
||||
const isRootPlaylist = !params.playlistId;
|
||||
|
||||
@@ -24,11 +24,13 @@ import {
|
||||
|
||||
export async function uploadMusicTracks(files: Iterable<File>) {
|
||||
const me = await MusicaAccount.getMe().ensureLoaded({
|
||||
root: {
|
||||
rootPlaylist: {
|
||||
tracks: [],
|
||||
resolve: {
|
||||
root: {
|
||||
rootPlaylist: {
|
||||
tracks: true,
|
||||
},
|
||||
playlists: true,
|
||||
},
|
||||
playlists: [],
|
||||
},
|
||||
});
|
||||
|
||||
@@ -64,8 +66,10 @@ export async function uploadMusicTracks(files: Iterable<File>) {
|
||||
|
||||
export async function createNewPlaylist() {
|
||||
const me = await MusicaAccount.getMe().ensureLoaded({
|
||||
root: {
|
||||
playlists: [],
|
||||
resolve: {
|
||||
root: {
|
||||
playlists: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -153,9 +157,11 @@ export async function updateMusicTrackTitle(track: MusicTrack, title: string) {
|
||||
|
||||
export async function updateActivePlaylist(playlist?: Playlist) {
|
||||
const me = await MusicaAccount.getMe().ensureLoaded({
|
||||
root: {
|
||||
activePlaylist: {},
|
||||
rootPlaylist: {},
|
||||
resolve: {
|
||||
root: {
|
||||
activePlaylist: true,
|
||||
rootPlaylist: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -166,7 +172,7 @@ export async function updateActivePlaylist(playlist?: Playlist) {
|
||||
|
||||
export async function updateActiveTrack(track: MusicTrack) {
|
||||
const me = await MusicaAccount.getMe().ensureLoaded({
|
||||
root: {},
|
||||
resolve: { root: true },
|
||||
});
|
||||
|
||||
if (!me) return;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { getNextTrack, getPrevTrack } from "./lib/getters";
|
||||
|
||||
export function useMediaPlayer() {
|
||||
const { me } = useAccount({
|
||||
root: {},
|
||||
resolve: { root: true },
|
||||
});
|
||||
|
||||
const playState = usePlayState();
|
||||
|
||||
@@ -14,8 +14,10 @@ export function InvitePage() {
|
||||
const playlist = await Playlist.load(playlistId, {});
|
||||
|
||||
const me = await MusicaAccount.getMe().ensureLoaded({
|
||||
root: {
|
||||
playlists: [],
|
||||
resolve: {
|
||||
root: {
|
||||
playlists: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -29,9 +29,7 @@ export function MusicTrackRow({
|
||||
const track = useCoState(MusicTrack, trackId);
|
||||
|
||||
const { me } = useAccount({
|
||||
root: {
|
||||
playlists: [{}],
|
||||
},
|
||||
resolve: { root: { playlists: { $each: true } } },
|
||||
});
|
||||
|
||||
const playlists = me?.root.playlists ?? [];
|
||||
|
||||
@@ -12,9 +12,7 @@ export function PlayerControls({ mediaPlayer }: { mediaPlayer: MediaPlayer }) {
|
||||
const isPlaying = playState.value === "play";
|
||||
|
||||
const activePlaylist = useAccount({
|
||||
root: {
|
||||
activePlaylist: {},
|
||||
},
|
||||
resolve: { root: { activePlaylist: true } },
|
||||
}).me?.root.activePlaylist;
|
||||
|
||||
useMediaEndListener(mediaPlayer.playNextTrack);
|
||||
@@ -25,7 +23,7 @@ export function PlayerControls({ mediaPlayer }: { mediaPlayer: MediaPlayer }) {
|
||||
});
|
||||
|
||||
const activeTrack = useCoState(MusicTrack, mediaPlayer.activeTrackId, {
|
||||
waveform: {},
|
||||
resolve: { waveform: true },
|
||||
});
|
||||
|
||||
if (!activeTrack) return null;
|
||||
|
||||
@@ -5,9 +5,7 @@ export function SidePanel() {
|
||||
const { playlistId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const { me } = useAccount({
|
||||
root: {
|
||||
playlists: [{}],
|
||||
},
|
||||
resolve: { root: { playlists: { $each: true } } },
|
||||
});
|
||||
|
||||
function handleAllTracksClick(evt: React.MouseEvent<HTMLAnchorElement>) {
|
||||
|
||||
@@ -8,7 +8,6 @@ export function Waveform(props: { track: MusicTrack; height: number }) {
|
||||
const waveformData = useCoState(
|
||||
MusicTrackWaveform,
|
||||
track._refs.waveform.id,
|
||||
{},
|
||||
)?.data;
|
||||
const duration = track.duration;
|
||||
|
||||
|
||||
@@ -2,9 +2,11 @@ import { MusicaAccount } from "../1_schema";
|
||||
|
||||
export async function getNextTrack() {
|
||||
const me = await MusicaAccount.getMe().ensureLoaded({
|
||||
root: {
|
||||
activePlaylist: {
|
||||
tracks: [],
|
||||
resolve: {
|
||||
root: {
|
||||
activePlaylist: {
|
||||
tracks: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -23,9 +25,11 @@ export async function getNextTrack() {
|
||||
|
||||
export async function getPrevTrack() {
|
||||
const me = await MusicaAccount.getMe().ensureLoaded({
|
||||
root: {
|
||||
activePlaylist: {
|
||||
tracks: [],
|
||||
resolve: {
|
||||
root: {
|
||||
activePlaylist: {
|
||||
tracks: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -10,7 +10,7 @@ export function useUploadExampleData() {
|
||||
|
||||
async function uploadOnboardingData() {
|
||||
const me = await MusicaAccount.getMe().ensureLoaded({
|
||||
root: {},
|
||||
resolve: { root: true },
|
||||
});
|
||||
|
||||
if (!me) throw new Error("Me not resolved");
|
||||
|
||||
@@ -19,8 +19,10 @@ function ImportEmployee({
|
||||
const { employeeCoId } = useParams();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, [{}]);
|
||||
const employee = useCoState(CoEmployee, employeeCoId as ID<CoEmployee>, {});
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, {
|
||||
resolve: { $each: true },
|
||||
});
|
||||
const employee = useCoState(CoEmployee, employeeCoId as ID<CoEmployee>);
|
||||
|
||||
useEffect(() => {
|
||||
if (!employee || !employees) return;
|
||||
|
||||
@@ -10,7 +10,9 @@ export function EmployeeList({
|
||||
}: {
|
||||
employeeListCoId: ID<EmployeeCoList>;
|
||||
}) {
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, [{}]);
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, {
|
||||
resolve: { $each: true },
|
||||
});
|
||||
|
||||
if (!employees) {
|
||||
return <div>Loading...</div>;
|
||||
|
||||
@@ -22,7 +22,9 @@ export function NewEmployee({
|
||||
const navigate = useNavigate();
|
||||
const { me } = useAccount();
|
||||
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, [{}]);
|
||||
const employees = useCoState(EmployeeCoList, employeeListCoId, {
|
||||
resolve: { $each: true },
|
||||
});
|
||||
|
||||
const [employeeName, setEmployeeName] = useState<string>("");
|
||||
|
||||
|
||||
@@ -5,11 +5,11 @@ import { Organization } from "./schema.ts";
|
||||
|
||||
export function AcceptInvitePage() {
|
||||
const navigate = useNavigate();
|
||||
const { me } = useAccount({ root: { organizations: [] } });
|
||||
const { me } = useAccount({ resolve: { root: { organizations: true } } });
|
||||
|
||||
const onAccept = (organizationId: ID<Organization>) => {
|
||||
if (me?.root?.organizations) {
|
||||
Organization.load(organizationId, me, []).then((organization) => {
|
||||
Organization.load(organizationId).then((organization) => {
|
||||
if (organization) {
|
||||
// avoid duplicates
|
||||
const ids = me.root.organizations.map(
|
||||
|
||||
@@ -5,7 +5,7 @@ import { Heading } from "./components/Heading.tsx";
|
||||
|
||||
export function HomePage() {
|
||||
const { me } = useAccount({
|
||||
root: { organizations: [{}] },
|
||||
resolve: { root: { organizations: true } },
|
||||
});
|
||||
|
||||
if (!me?.root.organizations) return;
|
||||
|
||||
@@ -3,7 +3,7 @@ import { UserIcon } from "lucide-react";
|
||||
|
||||
export function Layout({ children }: { children: React.ReactNode }) {
|
||||
const { me, logOut } = useAccount({
|
||||
root: { draftOrganization: {} },
|
||||
resolve: { root: { draftOrganization: true } },
|
||||
});
|
||||
|
||||
return (
|
||||
|
||||
@@ -13,7 +13,7 @@ export function OrganizationPage() {
|
||||
.organizationId;
|
||||
|
||||
const organization = useCoState(Organization, paramOrganizationId, {
|
||||
projects: [],
|
||||
resolve: { projects: true },
|
||||
});
|
||||
|
||||
if (!organization) return <p>Loading organization...</p>;
|
||||
|
||||
@@ -8,7 +8,7 @@ import { OrganizationForm } from "./OrganizationForm.tsx";
|
||||
|
||||
export function CreateOrganization() {
|
||||
const { me } = useAccount({
|
||||
root: { draftOrganization: {}, organizations: [] },
|
||||
resolve: { root: { draftOrganization: true, organizations: true } },
|
||||
});
|
||||
const [errors, setErrors] = useState<string[]>([]);
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -23,7 +23,9 @@ function Member({
|
||||
accountId,
|
||||
role,
|
||||
}: { accountId: ID<Account>; role?: string }) {
|
||||
const account = useCoState(Account, accountId, { profile: {} });
|
||||
const account = useCoState(Account, accountId, {
|
||||
resolve: { profile: true },
|
||||
});
|
||||
|
||||
if (!account?.profile) return;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Organization } from "../schema.ts";
|
||||
|
||||
export function OrganizationSelector({ className }: { className?: string }) {
|
||||
const { me } = useAccount({
|
||||
root: { organizations: [{}] },
|
||||
resolve: { root: { organizations: { $each: true } } },
|
||||
});
|
||||
|
||||
const navigate = useNavigate();
|
||||
|
||||
@@ -43,9 +43,11 @@ const VaultPage: React.FC = () => {
|
||||
(item): item is Exclude<typeof item, null> => !!item,
|
||||
) || [],
|
||||
);
|
||||
const folders = useCoState(FolderList, me.root?._refs.folders?.id, [
|
||||
{ items: [{}] },
|
||||
]);
|
||||
const folders = useCoState(FolderList, me.root?._refs.folders?.id, {
|
||||
resolve: {
|
||||
$each: { items: { $each: true } },
|
||||
},
|
||||
});
|
||||
|
||||
const [selectedFolder, setSelectedFolder] = useState<Folder | undefined>();
|
||||
const [isNewItemModalOpen, setIsNewItemModalOpen] = useState(false);
|
||||
|
||||
@@ -60,11 +60,9 @@ export async function addSharedFolder(
|
||||
me: PasswordManagerAccount,
|
||||
) {
|
||||
const [sharedFolder, account] = await Promise.all([
|
||||
Folder.load(sharedFolderId, me, {}),
|
||||
PasswordManagerAccount.load(me.id, me, {
|
||||
root: {
|
||||
folders: [],
|
||||
},
|
||||
Folder.load(sharedFolderId),
|
||||
PasswordManagerAccount.load(me.id, {
|
||||
resolve: { root: { folders: true } },
|
||||
}),
|
||||
]);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ const reactionEmojiMap: {
|
||||
};
|
||||
|
||||
export function ReactionsScreen(props: { id: ID<Reactions> }) {
|
||||
const reactions = useCoState(Reactions, props.id, []);
|
||||
const reactions = useCoState(Reactions, props.id);
|
||||
|
||||
if (!reactions) return;
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@
|
||||
<div class="section-header">
|
||||
<h2>Folders</h2>
|
||||
<div class="new-folder">
|
||||
<input
|
||||
v-model="newFolderName"
|
||||
placeholder="New folder name"
|
||||
<input
|
||||
v-model="newFolderName"
|
||||
placeholder="New folder name"
|
||||
class="input"
|
||||
/>
|
||||
<button class="btn btn-primary" @click="createFolder">Create</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="folder-list">
|
||||
<div
|
||||
v-for="folder in folders"
|
||||
@@ -32,9 +32,9 @@
|
||||
<div class="section-header">
|
||||
<h2>{{ selectedFolder?.name }}</h2>
|
||||
<div class="new-todo">
|
||||
<input
|
||||
v-model="newTodoTitle"
|
||||
placeholder="Add a new task"
|
||||
<input
|
||||
v-model="newTodoTitle"
|
||||
placeholder="Add a new task"
|
||||
class="input"
|
||||
/>
|
||||
<button class="btn btn-primary" @click="createTodo">Add</button>
|
||||
@@ -72,7 +72,9 @@ import { Folder, FolderList, ToDoItem, ToDoList } from "../schema";
|
||||
const { me } = useAccount();
|
||||
|
||||
const computedFoldersId = computed(() => me.value?.root?.folders?.id);
|
||||
const folders = useCoState(FolderList, computedFoldersId, [{ items: [{}] }]);
|
||||
const folders = useCoState(FolderList, computedFoldersId, {
|
||||
resolve: { $each: { items: true } },
|
||||
});
|
||||
|
||||
const selectedFolder = ref<Folder>();
|
||||
const newFolderName = ref("");
|
||||
|
||||
@@ -123,7 +123,7 @@ export default function App() {
|
||||
|
||||
function HomeScreen() {
|
||||
const { me } = useAccount({
|
||||
root: { projects: [{}] },
|
||||
resolve: { root: { projects: { $each: true } } },
|
||||
});
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ import { ID } from "jazz-tools";
|
||||
import { IssueComponent } from "./Issue.tsx";
|
||||
import { Issue, Project } from "./schema.ts";
|
||||
export function ProjectComponent({ projectID }: { projectID: ID<Project> }) {
|
||||
const project = useCoState(Project, projectID, { issues: [{}] });
|
||||
const project = useCoState(Project, projectID, {
|
||||
resolve: { issues: { $each: true } },
|
||||
});
|
||||
|
||||
if (!project) return;
|
||||
|
||||
|
||||
@@ -4,10 +4,7 @@
|
||||
"type": "module",
|
||||
"main": "./dist/app.js",
|
||||
"types": "./dist/app.d.ts",
|
||||
"files": [
|
||||
"dist/**",
|
||||
"src"
|
||||
],
|
||||
"files": ["dist/**", "src"],
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
||||
|
||||
@@ -64,7 +64,7 @@ describe("startWorker integration", () => {
|
||||
|
||||
await map.waitForSync();
|
||||
|
||||
const mapOnWorker2 = await TestMap.load(map.id, worker2.worker, {});
|
||||
const mapOnWorker2 = await TestMap.load(map.id, worker2.worker);
|
||||
|
||||
expect(mapOnWorker2?.value).toBe("test");
|
||||
|
||||
@@ -89,7 +89,7 @@ describe("startWorker integration", () => {
|
||||
|
||||
const worker2 = await setupWorker(worker1.syncServer);
|
||||
|
||||
const mapOnWorker2 = await TestMap.load(map.id, worker2.worker, {});
|
||||
const mapOnWorker2 = await TestMap.load(map.id, worker2.worker);
|
||||
|
||||
expect(mapOnWorker2?.value).toBe("test");
|
||||
|
||||
|
||||
@@ -5,12 +5,13 @@ import {
|
||||
AnonymousJazzAgent,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
DeeplyLoaded,
|
||||
DepthsIn,
|
||||
ID,
|
||||
InboxSender,
|
||||
RefsToResolve,
|
||||
Resolved,
|
||||
createCoValueObservable,
|
||||
} from "jazz-tools";
|
||||
import { RefsToResolveStrict } from "jazz-tools";
|
||||
import { JazzContext, JazzContextType } from "./provider.js";
|
||||
|
||||
export function useJazzContext<Acc extends Account>() {
|
||||
@@ -25,28 +26,39 @@ export function useJazzContext<Acc extends Account>() {
|
||||
return value;
|
||||
}
|
||||
|
||||
export function useCoState<V extends CoValue, D>(
|
||||
export function useCoState<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V> = true,
|
||||
>(
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
Schema: CoValueClass<V>,
|
||||
id: ID<V> | undefined,
|
||||
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
|
||||
): DeeplyLoaded<V, D> | undefined {
|
||||
options?: { resolve?: RefsToResolveStrict<V, R> },
|
||||
): Resolved<V, R> | undefined {
|
||||
const context = useJazzContext();
|
||||
|
||||
const [observable] = React.useState(() =>
|
||||
createCoValueObservable({
|
||||
createCoValueObservable<V, R>({
|
||||
syncResolution: true,
|
||||
}),
|
||||
);
|
||||
|
||||
const value = React.useSyncExternalStore<DeeplyLoaded<V, D> | undefined>(
|
||||
const value = React.useSyncExternalStore<Resolved<V, R> | undefined>(
|
||||
React.useCallback(
|
||||
(callback) => {
|
||||
if (!id) return () => {};
|
||||
|
||||
const agent = "me" in context ? context.me : context.guest;
|
||||
|
||||
return observable.subscribe(Schema, id, agent, depth, callback);
|
||||
return observable.subscribe(
|
||||
Schema,
|
||||
id,
|
||||
{
|
||||
loadAs: agent,
|
||||
resolve: options?.resolve,
|
||||
},
|
||||
callback,
|
||||
);
|
||||
},
|
||||
[Schema, id, context],
|
||||
),
|
||||
@@ -62,12 +74,12 @@ export function createUseAccountHooks<Acc extends Account>() {
|
||||
me: Acc;
|
||||
logOut: () => void;
|
||||
};
|
||||
function useAccount<D extends DepthsIn<Acc>>(
|
||||
depth: D,
|
||||
): { me: DeeplyLoaded<Acc, D> | undefined; logOut: () => void };
|
||||
function useAccount<D extends DepthsIn<Acc>>(
|
||||
depth?: D,
|
||||
): { me: Acc | DeeplyLoaded<Acc, D> | undefined; logOut: () => void } {
|
||||
function useAccount<const R extends RefsToResolve<Acc> = true>(options?: {
|
||||
resolve?: RefsToResolveStrict<Acc, R>;
|
||||
}): { me: Resolved<Acc, R> | undefined; logOut: () => void };
|
||||
function useAccount<const R extends RefsToResolve<Acc>>(options?: {
|
||||
resolve?: RefsToResolveStrict<Acc, R>;
|
||||
}): { me: Acc | Resolved<Acc, R> | undefined; logOut: () => void } {
|
||||
const context = useJazzContext<Acc>();
|
||||
|
||||
if (!("me" in context)) {
|
||||
@@ -76,10 +88,14 @@ export function createUseAccountHooks<Acc extends Account>() {
|
||||
);
|
||||
}
|
||||
|
||||
const me = useCoState<Acc, D>(context.AccountSchema, context.me.id, depth);
|
||||
const me = useCoState<Acc, R>(
|
||||
context.AccountSchema,
|
||||
context.me.id,
|
||||
options,
|
||||
);
|
||||
|
||||
return {
|
||||
me: depth === undefined ? me || context.me : me,
|
||||
me: options?.resolve === undefined ? me || context.me : me,
|
||||
logOut: context.logOut,
|
||||
};
|
||||
}
|
||||
@@ -87,21 +103,27 @@ export function createUseAccountHooks<Acc extends Account>() {
|
||||
function useAccountOrGuest(): {
|
||||
me: Acc | AnonymousJazzAgent;
|
||||
};
|
||||
function useAccountOrGuest<D extends DepthsIn<Acc>>(
|
||||
depth: D,
|
||||
): { me: DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent };
|
||||
function useAccountOrGuest<D extends DepthsIn<Acc>>(
|
||||
depth?: D,
|
||||
): { me: Acc | DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent } {
|
||||
function useAccountOrGuest<
|
||||
const R extends RefsToResolve<Acc> = true,
|
||||
>(options?: {
|
||||
resolve?: RefsToResolveStrict<Acc, R>;
|
||||
}): { me: Resolved<Acc, R> | undefined | AnonymousJazzAgent };
|
||||
function useAccountOrGuest<const R extends RefsToResolve<Acc>>(options?: {
|
||||
resolve?: RefsToResolveStrict<Acc, R>;
|
||||
}): { me: Acc | Resolved<Acc, R> | undefined | AnonymousJazzAgent } {
|
||||
const context = useJazzContext<Acc>();
|
||||
|
||||
const contextMe = "me" in context ? context.me : undefined;
|
||||
|
||||
const me = useCoState<Acc, D>(context.AccountSchema, contextMe?.id, depth);
|
||||
const me = useCoState<Acc, R>(
|
||||
context.AccountSchema,
|
||||
contextMe?.id,
|
||||
options,
|
||||
);
|
||||
|
||||
if ("me" in context) {
|
||||
return {
|
||||
me: depth === undefined ? me || context.me : me,
|
||||
me: options?.resolve === undefined ? me || context.me : me,
|
||||
};
|
||||
} else {
|
||||
return { me: context.guest };
|
||||
|
||||
@@ -41,7 +41,9 @@ describe("useAccount", () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useAccount({
|
||||
root: {},
|
||||
resolve: {
|
||||
root: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
account,
|
||||
|
||||
@@ -49,7 +49,9 @@ describe("useAccountOrGuest", () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useAccountOrGuest({
|
||||
root: {},
|
||||
resolve: {
|
||||
root: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
account,
|
||||
@@ -66,7 +68,9 @@ describe("useAccountOrGuest", () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useAccountOrGuest({
|
||||
root: {},
|
||||
resolve: {
|
||||
root: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
account,
|
||||
|
||||
@@ -77,7 +77,9 @@ describe("useCoState", () => {
|
||||
const { result } = renderHook(
|
||||
() =>
|
||||
useCoState(TestMap, map.id, {
|
||||
nested: {},
|
||||
resolve: {
|
||||
nested: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
account,
|
||||
|
||||
@@ -45,7 +45,9 @@ describe("useInboxSender", () => {
|
||||
|
||||
expect(incoming.value).toEqual("hello");
|
||||
const response = await promise;
|
||||
const responseMap = await TestMap.load(response, account, {});
|
||||
const responseMap = await TestMap.load(response, {
|
||||
loadAs: account,
|
||||
});
|
||||
|
||||
expect(responseMap!.value).toEqual("got it");
|
||||
});
|
||||
|
||||
@@ -46,7 +46,9 @@ describe("useAcceptInvite", () => {
|
||||
expect(acceptedId).toBeDefined();
|
||||
});
|
||||
|
||||
const accepted = await TestMap.load(acceptedId!, account, {});
|
||||
const accepted = await TestMap.load(acceptedId!, {
|
||||
loadAs: account,
|
||||
});
|
||||
|
||||
expect(accepted?.value).toEqual("hello");
|
||||
});
|
||||
|
||||
@@ -7,13 +7,14 @@ import type {
|
||||
AnonymousJazzAgent,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
DeeplyLoaded,
|
||||
DepthsIn,
|
||||
ID
|
||||
ID,
|
||||
RefsToResolve,
|
||||
Resolved
|
||||
} from 'jazz-tools';
|
||||
import { Account, subscribeToCoValue } from 'jazz-tools';
|
||||
import { getContext, untrack } from 'svelte';
|
||||
import Provider from './Provider.svelte';
|
||||
import type { RefsToResolveStrict } from 'jazz-tools';
|
||||
|
||||
export { Provider as JazzProvider };
|
||||
|
||||
@@ -44,18 +45,12 @@ export type RegisteredAccount = Register extends { Account: infer Acc }
|
||||
? Acc
|
||||
: Account;
|
||||
|
||||
export function useAccount(): { me: RegisteredAccount; logOut: () => void };
|
||||
export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
depth: D
|
||||
): { me: DeeplyLoaded<RegisteredAccount, D> | undefined; logOut: () => void };
|
||||
/**
|
||||
* Use the current account with a optional depth.
|
||||
* @param depth - The depth.
|
||||
* @returns The current account.
|
||||
*/
|
||||
export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
depth?: D
|
||||
): { me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined; logOut: () => void } {
|
||||
export function useAccount<const R extends RefsToResolve<RegisteredAccount>>(
|
||||
options?: { resolve?: RefsToResolveStrict<RegisteredAccount, R> }
|
||||
): { me: Resolved<RegisteredAccount, R> | undefined; logOut: () => void };
|
||||
export function useAccount<const R extends RefsToResolve<RegisteredAccount>>(
|
||||
options?: { resolve?: RefsToResolveStrict<RegisteredAccount, R> }
|
||||
): { me: RegisteredAccount | Resolved<RegisteredAccount, R> | undefined; logOut: () => void } {
|
||||
const ctx = getJazzContext<RegisteredAccount>();
|
||||
if (!ctx?.current) {
|
||||
throw new Error('useAccount must be used within a JazzProvider');
|
||||
@@ -67,7 +62,7 @@ export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
}
|
||||
|
||||
// If no depth is specified, return the context's me directly
|
||||
if (depth === undefined) {
|
||||
if (options?.resolve === undefined) {
|
||||
return {
|
||||
get me() {
|
||||
return (ctx.current as BrowserContext<RegisteredAccount>).me;
|
||||
@@ -79,10 +74,10 @@ export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
}
|
||||
|
||||
// If depth is specified, use useCoState to get the deeply loaded version
|
||||
const me = useCoState<RegisteredAccount, D>(
|
||||
const me = useCoState<RegisteredAccount, R>(
|
||||
ctx.current.me.constructor as CoValueClass<RegisteredAccount>,
|
||||
(ctx.current as BrowserContext<RegisteredAccount>).me.id,
|
||||
depth
|
||||
options
|
||||
);
|
||||
|
||||
return {
|
||||
@@ -96,17 +91,12 @@ export function useAccount<D extends DepthsIn<RegisteredAccount>>(
|
||||
}
|
||||
|
||||
export function useAccountOrGuest(): { me: RegisteredAccount | AnonymousJazzAgent };
|
||||
export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
|
||||
depth: D
|
||||
): { me: DeeplyLoaded<RegisteredAccount, D> | undefined | AnonymousJazzAgent };
|
||||
/**
|
||||
* Use the current account or guest with a optional depth.
|
||||
* @param depth - The depth.
|
||||
* @returns The current account or guest.
|
||||
*/
|
||||
export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
|
||||
depth?: D
|
||||
): { me: RegisteredAccount | DeeplyLoaded<RegisteredAccount, D> | undefined | AnonymousJazzAgent } {
|
||||
export function useAccountOrGuest<R extends RefsToResolve<RegisteredAccount>>(
|
||||
options?: { resolve?: RefsToResolveStrict<RegisteredAccount, R> }
|
||||
): { me: Resolved<RegisteredAccount, R> | undefined | AnonymousJazzAgent };
|
||||
export function useAccountOrGuest<R extends RefsToResolve<RegisteredAccount>>(
|
||||
options?: { resolve?: RefsToResolveStrict<RegisteredAccount, R> }
|
||||
): { me: RegisteredAccount | Resolved<RegisteredAccount, R> | undefined | AnonymousJazzAgent } {
|
||||
const ctx = getJazzContext<RegisteredAccount>();
|
||||
|
||||
if (!ctx?.current) {
|
||||
@@ -115,17 +105,17 @@ export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
|
||||
|
||||
const contextMe = 'me' in ctx.current ? ctx.current.me : undefined;
|
||||
|
||||
const me = useCoState<RegisteredAccount, D>(
|
||||
const me = useCoState<RegisteredAccount, R>(
|
||||
contextMe?.constructor as CoValueClass<RegisteredAccount>,
|
||||
contextMe?.id,
|
||||
depth
|
||||
options
|
||||
);
|
||||
|
||||
// If the context has a me, return the account.
|
||||
if ('me' in ctx.current) {
|
||||
return {
|
||||
get me() {
|
||||
return depth === undefined
|
||||
return options?.resolve === undefined
|
||||
? me.current || (ctx.current as BrowserContext<RegisteredAccount>)?.me
|
||||
: me.current;
|
||||
}
|
||||
@@ -141,24 +131,17 @@ export function useAccountOrGuest<D extends DepthsIn<RegisteredAccount>>(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a CoValue with a optional depth.
|
||||
* @param Schema - The CoValue schema.
|
||||
* @param id - The CoValue id.
|
||||
* @param depth - The depth.
|
||||
* @returns The CoValue.
|
||||
*/
|
||||
export function useCoState<V extends CoValue, D extends DepthsIn<V> = []>(
|
||||
export function useCoState<V extends CoValue, R extends RefsToResolve<V>>(
|
||||
Schema: CoValueClass<V>,
|
||||
id: ID<V> | undefined,
|
||||
depth: D = [] as D
|
||||
options?: { resolve?: RefsToResolveStrict<V, R> }
|
||||
): {
|
||||
current?: DeeplyLoaded<V, D>;
|
||||
current?: Resolved<V, R>;
|
||||
} {
|
||||
const ctx = getJazzContext<RegisteredAccount>();
|
||||
|
||||
// Create state and a stable observable
|
||||
let state = $state.raw<DeeplyLoaded<V, D> | undefined>(undefined);
|
||||
let state = $state.raw<Resolved<V, R> | undefined>(undefined);
|
||||
|
||||
// Effect to handle subscription
|
||||
$effect(() => {
|
||||
@@ -168,12 +151,13 @@ export function useCoState<V extends CoValue, D extends DepthsIn<V> = []>(
|
||||
// Return early if no context or id, effectively cleaning up any previous subscription
|
||||
if (!ctx?.current || !id) return;
|
||||
|
||||
const agent = "me" in ctx.current ? ctx.current.me : ctx.current.guest;
|
||||
|
||||
// Setup subscription with current values
|
||||
return subscribeToCoValue(
|
||||
return subscribeToCoValue<V, R>(
|
||||
Schema,
|
||||
id,
|
||||
'me' in ctx.current ? ctx.current.me : ctx.current.guest,
|
||||
depth,
|
||||
{ resolve: options?.resolve, loadAs: agent },
|
||||
(value) => {
|
||||
// Get current value from our stable observable
|
||||
state = value;
|
||||
@@ -219,6 +203,7 @@ export function useAcceptInvite<V extends CoValue>({
|
||||
|
||||
// Subscribe to the onAccept function.
|
||||
$effect(() => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
|
||||
_onAccept;
|
||||
// Subscribe to the onAccept function.
|
||||
untrack(() => {
|
||||
|
||||
@@ -18,19 +18,22 @@ import {
|
||||
type CoValue,
|
||||
CoValueBase,
|
||||
CoValueClass,
|
||||
DeeplyLoaded,
|
||||
DepthsIn,
|
||||
ID,
|
||||
MembersSym,
|
||||
Ref,
|
||||
type RefEncoded,
|
||||
RefIfCoValue,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
type Schema,
|
||||
SchemaInit,
|
||||
SubscribeRestArgs,
|
||||
ensureCoValueLoaded,
|
||||
inspect,
|
||||
loadCoValue,
|
||||
loadCoValueWithoutMe,
|
||||
parseSubscribeRestArgs,
|
||||
subscribeToCoValueWithoutMe,
|
||||
subscribeToExistingCoValue,
|
||||
subscriptionsScopes,
|
||||
@@ -166,7 +169,9 @@ export class Account extends CoValueBase implements CoValue {
|
||||
inviteSecret,
|
||||
);
|
||||
|
||||
return loadCoValue(coValueClass, valueID, this as Account, []);
|
||||
return loadCoValue(coValueClass, valueID, {
|
||||
loadAs: this,
|
||||
});
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
}
|
||||
|
||||
@@ -272,73 +277,65 @@ export class Account extends CoValueBase implements CoValue {
|
||||
}
|
||||
|
||||
/** @category Subscription & Loading */
|
||||
static load<A extends Account, Depth>(
|
||||
static load<A extends Account, const R extends RefsToResolve<A> = true>(
|
||||
this: CoValueClass<A>,
|
||||
id: ID<A>,
|
||||
depth: Depth & DepthsIn<A>,
|
||||
): Promise<DeeplyLoaded<A, Depth> | undefined>;
|
||||
static load<A extends Account, Depth>(
|
||||
this: CoValueClass<A>,
|
||||
id: ID<A>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<A>,
|
||||
): Promise<DeeplyLoaded<A, Depth> | undefined>;
|
||||
static load<A extends Account, Depth>(
|
||||
this: CoValueClass<A>,
|
||||
id: ID<A>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<A>),
|
||||
depth?: Depth & DepthsIn<A>,
|
||||
): Promise<DeeplyLoaded<A, Depth> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, asOrDepth, depth);
|
||||
options?: {
|
||||
resolve?: RefsToResolveStrict<A, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
): Promise<Resolved<A, R> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, options);
|
||||
}
|
||||
|
||||
/** @category Subscription & Loading */
|
||||
static subscribe<A extends Account, Depth>(
|
||||
static subscribe<A extends Account, const R extends RefsToResolve<A> = true>(
|
||||
this: CoValueClass<A>,
|
||||
id: ID<A>,
|
||||
depth: Depth & DepthsIn<A>,
|
||||
listener: (value: DeeplyLoaded<A, Depth>) => void,
|
||||
listener: (value: Resolved<A, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<A extends Account, Depth>(
|
||||
static subscribe<A extends Account, const R extends RefsToResolve<A> = true>(
|
||||
this: CoValueClass<A>,
|
||||
id: ID<A>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<A>,
|
||||
listener: (value: DeeplyLoaded<A, Depth>) => void,
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<A, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
listener: (value: Resolved<A, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<A extends Account, Depth>(
|
||||
static subscribe<A extends Account, const R extends RefsToResolve<A>>(
|
||||
this: CoValueClass<A>,
|
||||
id: ID<A>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<A>),
|
||||
depthOrListener:
|
||||
| (Depth & DepthsIn<A>)
|
||||
| ((value: DeeplyLoaded<A, Depth>) => void),
|
||||
listener?: (value: DeeplyLoaded<A, Depth>) => void,
|
||||
...args: SubscribeRestArgs<A, R>
|
||||
): () => void {
|
||||
return subscribeToCoValueWithoutMe<A, Depth>(
|
||||
this,
|
||||
id,
|
||||
asOrDepth,
|
||||
depthOrListener,
|
||||
listener!,
|
||||
);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToCoValueWithoutMe<A, R>(this, id, options, listener);
|
||||
}
|
||||
|
||||
/** @category Subscription & Loading */
|
||||
ensureLoaded<A extends Account, Depth>(
|
||||
ensureLoaded<A extends Account, const R extends RefsToResolve<A>>(
|
||||
this: A,
|
||||
depth: Depth & DepthsIn<A>,
|
||||
): Promise<DeeplyLoaded<A, Depth> | undefined> {
|
||||
return ensureCoValueLoaded(this, depth);
|
||||
options: { resolve: RefsToResolveStrict<A, R> },
|
||||
): Promise<Resolved<A, R> | undefined> {
|
||||
return ensureCoValueLoaded(this, options);
|
||||
}
|
||||
|
||||
/** @category Subscription & Loading */
|
||||
subscribe<A extends Account, Depth>(
|
||||
subscribe<A extends Account, const R extends RefsToResolve<A>>(
|
||||
this: A,
|
||||
depth: Depth & DepthsIn<A>,
|
||||
listener: (value: DeeplyLoaded<A, Depth>) => void,
|
||||
listener: (value: Resolved<A, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<A extends Account, const R extends RefsToResolve<A>>(
|
||||
this: A,
|
||||
options: { resolve?: RefsToResolveStrict<A, R> },
|
||||
listener: (value: Resolved<A, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<A extends Account, const R extends RefsToResolve<A>>(
|
||||
this: A,
|
||||
...args: SubscribeRestArgs<A, R>
|
||||
): () => void {
|
||||
return subscribeToExistingCoValue(this, depth, listener);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToExistingCoValue(this, options, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,17 +10,18 @@ import type {
|
||||
SessionID,
|
||||
} from "cojson";
|
||||
import { MAX_RECOMMENDED_TX_SIZE, cojsonInternals } from "cojson";
|
||||
import { activeAccountContext } from "../implementation/activeAccountContext.js";
|
||||
import type {
|
||||
AnonymousJazzAgent,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
DeeplyLoaded,
|
||||
DepthsIn,
|
||||
ID,
|
||||
IfCo,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
Schema,
|
||||
SchemaFor,
|
||||
SubscribeRestArgs,
|
||||
UnCo,
|
||||
} from "../internal.js";
|
||||
import {
|
||||
@@ -31,11 +32,10 @@ import {
|
||||
co,
|
||||
ensureCoValueLoaded,
|
||||
inspect,
|
||||
isAccountInstance,
|
||||
isRefEncoded,
|
||||
loadCoValueWithoutMe,
|
||||
parseCoValueCreateOptions,
|
||||
subscribeToCoValue,
|
||||
parseSubscribeRestArgs,
|
||||
subscribeToCoValueWithoutMe,
|
||||
subscribeToExistingCoValue,
|
||||
} from "../internal.js";
|
||||
@@ -326,59 +326,42 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
|
||||
* Load a `CoFeed`
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static load<S extends CoFeed, Depth>(
|
||||
this: CoValueClass<S>,
|
||||
id: ID<S>,
|
||||
depth: Depth & DepthsIn<S>,
|
||||
): Promise<DeeplyLoaded<S, Depth> | undefined>;
|
||||
static load<S extends CoFeed, Depth>(
|
||||
this: CoValueClass<S>,
|
||||
id: ID<S>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<S>,
|
||||
): Promise<DeeplyLoaded<S, Depth> | undefined>;
|
||||
static load<S extends CoFeed, Depth>(
|
||||
this: CoValueClass<S>,
|
||||
id: ID<S>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<S>),
|
||||
depth?: Depth & DepthsIn<S>,
|
||||
): Promise<DeeplyLoaded<S, Depth> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, asOrDepth, depth);
|
||||
static load<F extends CoFeed, const R extends RefsToResolve<F> = true>(
|
||||
this: CoValueClass<F>,
|
||||
id: ID<F>,
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<F, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
): Promise<Resolved<F, R> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a `CoFeed`, when you have an ID but don't have a `CoFeed` instance yet
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static subscribe<S extends CoFeed, Depth>(
|
||||
this: CoValueClass<S>,
|
||||
id: ID<S>,
|
||||
depth: Depth & DepthsIn<S>,
|
||||
listener: (value: DeeplyLoaded<S, Depth>) => void,
|
||||
static subscribe<F extends CoFeed, const R extends RefsToResolve<F> = true>(
|
||||
this: CoValueClass<F>,
|
||||
id: ID<F>,
|
||||
listener: (value: Resolved<F, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<S extends CoFeed, Depth>(
|
||||
this: CoValueClass<S>,
|
||||
id: ID<S>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<S>,
|
||||
listener: (value: DeeplyLoaded<S, Depth>) => void,
|
||||
static subscribe<F extends CoFeed, const R extends RefsToResolve<F> = true>(
|
||||
this: CoValueClass<F>,
|
||||
id: ID<F>,
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<F, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
listener: (value: Resolved<F, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<S extends CoFeed, Depth>(
|
||||
this: CoValueClass<S>,
|
||||
id: ID<S>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<S>),
|
||||
depthOrListener:
|
||||
| (Depth & DepthsIn<S>)
|
||||
| ((value: DeeplyLoaded<S, Depth>) => void),
|
||||
listener?: (value: DeeplyLoaded<S, Depth>) => void,
|
||||
static subscribe<F extends CoFeed, const R extends RefsToResolve<F>>(
|
||||
this: CoValueClass<F>,
|
||||
id: ID<F>,
|
||||
...args: SubscribeRestArgs<F, R>
|
||||
): () => void {
|
||||
return subscribeToCoValueWithoutMe<S, Depth>(
|
||||
this,
|
||||
id,
|
||||
asOrDepth,
|
||||
depthOrListener,
|
||||
listener,
|
||||
);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToCoValueWithoutMe<F, R>(this, id, options, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -388,11 +371,11 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
|
||||
* or undefined if it cannot be loaded that deeply
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
ensureLoaded<S extends CoFeed, Depth>(
|
||||
this: S,
|
||||
depth: Depth & DepthsIn<S>,
|
||||
): Promise<DeeplyLoaded<S, Depth> | undefined> {
|
||||
return ensureCoValueLoaded(this, depth);
|
||||
ensureLoaded<F extends CoFeed, const R extends RefsToResolve<F>>(
|
||||
this: F,
|
||||
options?: { resolve?: RefsToResolveStrict<F, R> },
|
||||
): Promise<Resolved<F, R> | undefined> {
|
||||
return ensureCoValueLoaded(this, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -401,12 +384,21 @@ export class CoFeed<Item = any> extends CoValueBase implements CoValue {
|
||||
* No need to provide an ID or Account since they're already part of the instance.
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
subscribe<S extends CoFeed, Depth>(
|
||||
this: S,
|
||||
depth: Depth & DepthsIn<S>,
|
||||
listener: (value: DeeplyLoaded<S, Depth>) => void,
|
||||
subscribe<F extends CoFeed, const R extends RefsToResolve<F>>(
|
||||
this: F,
|
||||
listener: (value: Resolved<F, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<F extends CoFeed, const R extends RefsToResolve<F>>(
|
||||
this: F,
|
||||
options: { resolve?: RefsToResolveStrict<F, R> },
|
||||
listener: (value: Resolved<F, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<F extends CoFeed, const R extends RefsToResolve<F>>(
|
||||
this: F,
|
||||
...args: SubscribeRestArgs<F, R>
|
||||
): () => void {
|
||||
return subscribeToExistingCoValue(this, depth, listener);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToExistingCoValue(this, options, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -761,34 +753,10 @@ export class FileStream extends CoValueBase implements CoValue {
|
||||
id: ID<FileStream>,
|
||||
options?: {
|
||||
allowUnfinished?: boolean;
|
||||
},
|
||||
): Promise<Blob | undefined>;
|
||||
static async loadAsBlob(
|
||||
id: ID<FileStream>,
|
||||
as: Account,
|
||||
options?: {
|
||||
allowUnfinished?: boolean;
|
||||
},
|
||||
): Promise<Blob | undefined>;
|
||||
static async loadAsBlob(
|
||||
id: ID<FileStream>,
|
||||
asOrOptions?:
|
||||
| Account
|
||||
| {
|
||||
allowUnfinished?: boolean;
|
||||
},
|
||||
optionsOrUndefined?: {
|
||||
allowUnfinished?: boolean;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
): Promise<Blob | undefined> {
|
||||
const as = isAccountInstance(asOrOptions)
|
||||
? asOrOptions
|
||||
: activeAccountContext.get();
|
||||
const options = isAccountInstance(asOrOptions)
|
||||
? optionsOrUndefined
|
||||
: asOrOptions;
|
||||
|
||||
let stream = await this.load(id, as, []);
|
||||
let stream = await this.load(id, options);
|
||||
|
||||
/**
|
||||
* If the user hasn't requested an incomplete blob and the
|
||||
@@ -796,12 +764,17 @@ export class FileStream extends CoValueBase implements CoValue {
|
||||
*/
|
||||
if (!options?.allowUnfinished && !stream?.isBinaryStreamEnded()) {
|
||||
stream = await new Promise<FileStream>((resolve) => {
|
||||
subscribeToCoValue(this, id, as, [], (value, unsubscribe) => {
|
||||
if (value.isBinaryStreamEnded()) {
|
||||
unsubscribe();
|
||||
resolve(value);
|
||||
}
|
||||
});
|
||||
subscribeToCoValueWithoutMe(
|
||||
this,
|
||||
id,
|
||||
options || {},
|
||||
(value, unsubscribe) => {
|
||||
if (value.isBinaryStreamEnded()) {
|
||||
unsubscribe();
|
||||
resolve(value);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -900,78 +873,36 @@ export class FileStream extends CoValueBase implements CoValue {
|
||||
* Load a `FileStream`
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static load<C extends FileStream, Depth>(
|
||||
static load<C extends FileStream>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined>;
|
||||
static load<C extends FileStream, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined>;
|
||||
static load<C extends FileStream, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<C>),
|
||||
depth?: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, asOrDepth, depth);
|
||||
options?: { loadAs?: Account | AnonymousJazzAgent },
|
||||
): Promise<Resolved<C, true> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to a `FileStream`, when you have an ID but don't have a `FileStream` instance yet
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static subscribe<C extends FileStream, Depth>(
|
||||
static subscribe<C extends FileStream>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
listener: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
): () => void;
|
||||
static subscribe<C extends FileStream, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
listener: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
): () => void;
|
||||
static subscribe<C extends FileStream, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<C>),
|
||||
depthOrListener:
|
||||
| (Depth & DepthsIn<C>)
|
||||
| ((value: DeeplyLoaded<C, Depth>) => void),
|
||||
listener?: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
options: { loadAs?: Account | AnonymousJazzAgent },
|
||||
listener: (value: Resolved<C, true>) => void,
|
||||
): () => void {
|
||||
return subscribeToCoValueWithoutMe<C, Depth>(
|
||||
this,
|
||||
id,
|
||||
asOrDepth,
|
||||
depthOrListener,
|
||||
listener,
|
||||
);
|
||||
}
|
||||
|
||||
ensureLoaded<B extends FileStream, Depth>(
|
||||
this: B,
|
||||
depth: Depth & DepthsIn<B>,
|
||||
): Promise<DeeplyLoaded<B, Depth> | undefined> {
|
||||
return ensureCoValueLoaded(this, depth);
|
||||
return subscribeToCoValueWithoutMe<C, true>(this, id, options, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* An instance method to subscribe to an existing `FileStream`
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
subscribe<B extends FileStream, Depth>(
|
||||
subscribe<B extends FileStream>(
|
||||
this: B,
|
||||
depth: Depth & DepthsIn<B>,
|
||||
listener: (value: DeeplyLoaded<B, Depth>) => void,
|
||||
listener: (value: Resolved<B, true>) => void,
|
||||
): () => void {
|
||||
return subscribeToExistingCoValue(this, depth, listener);
|
||||
return subscribeToExistingCoValue(this, {}, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,12 +4,14 @@ import type {
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
CoValueFromRaw,
|
||||
DeeplyLoaded,
|
||||
DepthsIn,
|
||||
ID,
|
||||
RefEncoded,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
Schema,
|
||||
SchemaFor,
|
||||
SubscribeRestArgs,
|
||||
UnCo,
|
||||
} from "../internal.js";
|
||||
import {
|
||||
@@ -24,6 +26,7 @@ import {
|
||||
loadCoValueWithoutMe,
|
||||
makeRefs,
|
||||
parseCoValueCreateOptions,
|
||||
parseSubscribeRestArgs,
|
||||
subscribeToCoValueWithoutMe,
|
||||
subscribeToExistingCoValue,
|
||||
subscriptionsScopes,
|
||||
@@ -360,24 +363,15 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static load<C extends CoList, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined>;
|
||||
static load<C extends CoList, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined>;
|
||||
static load<C extends CoList, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<C>),
|
||||
depth?: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, asOrDepth, depth);
|
||||
static load<L extends CoList, const R extends RefsToResolve<L> = true>(
|
||||
this: CoValueClass<L>,
|
||||
id: ID<L>,
|
||||
options?: {
|
||||
resolve?: RefsToResolveStrict<L, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
): Promise<Resolved<L, R> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -408,35 +402,27 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static subscribe<C extends CoList, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
listener: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<L extends CoList, const R extends RefsToResolve<L> = true>(
|
||||
this: CoValueClass<L>,
|
||||
id: ID<L>,
|
||||
listener: (value: Resolved<L, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<C extends CoList, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
listener: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<L extends CoList, const R extends RefsToResolve<L> = true>(
|
||||
this: CoValueClass<L>,
|
||||
id: ID<L>,
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<L, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
listener: (value: Resolved<L, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<C extends CoList, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<C>),
|
||||
depthOrListener:
|
||||
| (Depth & DepthsIn<C>)
|
||||
| ((value: DeeplyLoaded<C, Depth>) => void),
|
||||
listener?: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<L extends CoList, const R extends RefsToResolve<L>>(
|
||||
this: CoValueClass<L>,
|
||||
id: ID<L>,
|
||||
...args: SubscribeRestArgs<L, R>
|
||||
): () => void {
|
||||
return subscribeToCoValueWithoutMe<C, Depth>(
|
||||
this,
|
||||
id,
|
||||
asOrDepth,
|
||||
depthOrListener,
|
||||
listener,
|
||||
);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToCoValueWithoutMe<L, R>(this, id, options, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -446,11 +432,11 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
ensureLoaded<L extends CoList, Depth>(
|
||||
ensureLoaded<L extends CoList, const R extends RefsToResolve<L>>(
|
||||
this: L,
|
||||
depth: Depth & DepthsIn<L>,
|
||||
): Promise<DeeplyLoaded<L, Depth> | undefined> {
|
||||
return ensureCoValueLoaded(this, depth);
|
||||
options: { resolve: RefsToResolveStrict<L, R> },
|
||||
): Promise<Resolved<L, R> | undefined> {
|
||||
return ensureCoValueLoaded(this, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -462,12 +448,21 @@ export class CoList<Item = any> extends Array<Item> implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
**/
|
||||
subscribe<L extends CoList, Depth>(
|
||||
subscribe<L extends CoList, const R extends RefsToResolve<L> = true>(
|
||||
this: L,
|
||||
depth: Depth & DepthsIn<L>,
|
||||
listener: (value: DeeplyLoaded<L, Depth>) => void,
|
||||
listener: (value: Resolved<L, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<L extends CoList, const R extends RefsToResolve<L> = true>(
|
||||
this: L,
|
||||
options: { resolve?: RefsToResolveStrict<L, R> },
|
||||
listener: (value: Resolved<L, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<L extends CoList, const R extends RefsToResolve<L>>(
|
||||
this: L,
|
||||
...args: SubscribeRestArgs<L, R>
|
||||
): () => void {
|
||||
return subscribeToExistingCoValue(this, depth, listener);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToExistingCoValue(this, options, listener);
|
||||
}
|
||||
|
||||
/** @category Type Helpers */
|
||||
|
||||
@@ -12,13 +12,15 @@ import type {
|
||||
AnonymousJazzAgent,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
DeeplyLoaded,
|
||||
DepthsIn,
|
||||
ID,
|
||||
IfCo,
|
||||
RefEncoded,
|
||||
RefIfCoValue,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
Schema,
|
||||
SubscribeRestArgs,
|
||||
co,
|
||||
} from "../internal.js";
|
||||
import {
|
||||
@@ -32,6 +34,7 @@ import {
|
||||
loadCoValueWithoutMe,
|
||||
makeRefs,
|
||||
parseCoValueCreateOptions,
|
||||
parseSubscribeRestArgs,
|
||||
subscribeToCoValueWithoutMe,
|
||||
subscribeToExistingCoValue,
|
||||
subscriptionsScopes,
|
||||
@@ -432,24 +435,15 @@ export class CoMap extends CoValueBase implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static load<C extends CoMap, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined>;
|
||||
static load<C extends CoMap, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined>;
|
||||
static load<C extends CoMap, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<C>),
|
||||
depth?: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, asOrDepth, depth);
|
||||
static load<M extends CoMap, const R extends RefsToResolve<M> = true>(
|
||||
this: CoValueClass<M>,
|
||||
id: ID<M>,
|
||||
options?: {
|
||||
resolve?: RefsToResolveStrict<M, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
): Promise<Resolved<M, R> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -479,35 +473,27 @@ export class CoMap extends CoValueBase implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
static subscribe<C extends CoMap, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
listener: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<M extends CoMap, const R extends RefsToResolve<M> = true>(
|
||||
this: CoValueClass<M>,
|
||||
id: ID<M>,
|
||||
listener: (value: Resolved<M, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<C extends CoMap, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
listener: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<M extends CoMap, const R extends RefsToResolve<M> = true>(
|
||||
this: CoValueClass<M>,
|
||||
id: ID<M>,
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<M, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
listener: (value: Resolved<M, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<C extends CoMap, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<C>),
|
||||
depthOrListener:
|
||||
| (Depth & DepthsIn<C>)
|
||||
| ((value: DeeplyLoaded<C, Depth>) => void),
|
||||
listener?: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<M extends CoMap, const R extends RefsToResolve<M>>(
|
||||
this: CoValueClass<M>,
|
||||
id: ID<M>,
|
||||
...args: SubscribeRestArgs<M, R>
|
||||
): () => void {
|
||||
return subscribeToCoValueWithoutMe<C, Depth>(
|
||||
this,
|
||||
id,
|
||||
asOrDepth,
|
||||
depthOrListener,
|
||||
listener,
|
||||
);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToCoValueWithoutMe<M, R>(this, id, options, listener);
|
||||
}
|
||||
|
||||
static findUnique<M extends CoMap>(
|
||||
@@ -539,11 +525,11 @@ export class CoMap extends CoValueBase implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
*/
|
||||
ensureLoaded<M extends CoMap, Depth>(
|
||||
ensureLoaded<M extends CoMap, const R extends RefsToResolve<M>>(
|
||||
this: M,
|
||||
depth: Depth & DepthsIn<M>,
|
||||
): Promise<DeeplyLoaded<M, Depth> | undefined> {
|
||||
return ensureCoValueLoaded(this, depth);
|
||||
options: { resolve: RefsToResolveStrict<M, R> },
|
||||
): Promise<Resolved<M, R> | undefined> {
|
||||
return ensureCoValueLoaded(this, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -555,12 +541,21 @@ export class CoMap extends CoValueBase implements CoValue {
|
||||
*
|
||||
* @category Subscription & Loading
|
||||
**/
|
||||
subscribe<M extends CoMap, Depth>(
|
||||
subscribe<M extends CoMap, const R extends RefsToResolve<M> = true>(
|
||||
this: M,
|
||||
depth: Depth & DepthsIn<M>,
|
||||
listener: (value: DeeplyLoaded<M, Depth>) => void,
|
||||
listener: (value: Resolved<M, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<M extends CoMap, const R extends RefsToResolve<M> = true>(
|
||||
this: M,
|
||||
options: { resolve?: RefsToResolveStrict<M, R> },
|
||||
listener: (value: Resolved<M, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<M extends CoMap, const R extends RefsToResolve<M>>(
|
||||
this: M,
|
||||
...args: SubscribeRestArgs<M, R>
|
||||
): () => void {
|
||||
return subscribeToExistingCoValue(this, depth, listener);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToExistingCoValue<M, R>(this, options, listener);
|
||||
}
|
||||
|
||||
applyDiff<N extends Partial<CoMapInit<this>>>(newValues: N) {
|
||||
|
||||
@@ -4,13 +4,19 @@ import {
|
||||
type RawCoPlainText,
|
||||
stringifyOpID,
|
||||
} from "cojson";
|
||||
import { activeAccountContext } from "../implementation/activeAccountContext.js";
|
||||
import type { CoValue, CoValueClass, ID } from "../internal.js";
|
||||
import type {
|
||||
AnonymousJazzAgent,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
ID,
|
||||
Resolved,
|
||||
SubscribeRestArgs,
|
||||
} from "../internal.js";
|
||||
import {
|
||||
inspect,
|
||||
isAccountInstance,
|
||||
loadCoValue,
|
||||
subscribeToCoValue,
|
||||
loadCoValueWithoutMe,
|
||||
parseSubscribeRestArgs,
|
||||
subscribeToCoValueWithoutMe,
|
||||
subscribeToExistingCoValue,
|
||||
} from "../internal.js";
|
||||
import { Account } from "./account.js";
|
||||
@@ -122,9 +128,9 @@ export class CoPlainText extends String implements CoValue {
|
||||
static load<T extends CoPlainText>(
|
||||
this: CoValueClass<T>,
|
||||
id: ID<T>,
|
||||
as?: Account,
|
||||
options?: { loadAs?: Account | AnonymousJazzAgent },
|
||||
): Promise<T | undefined> {
|
||||
return loadCoValue(this, id, as ?? activeAccountContext.get(), []);
|
||||
return loadCoValueWithoutMe(this, id, options);
|
||||
}
|
||||
|
||||
// /**
|
||||
@@ -157,47 +163,23 @@ export class CoPlainText extends String implements CoValue {
|
||||
static subscribe<T extends CoPlainText>(
|
||||
this: CoValueClass<T>,
|
||||
id: ID<T>,
|
||||
listener: (value: T) => void,
|
||||
listener: (value: Resolved<T, true>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<T extends CoPlainText>(
|
||||
this: CoValueClass<T>,
|
||||
id: ID<T>,
|
||||
as: Account,
|
||||
listener: (value: T) => void,
|
||||
options: { loadAs?: Account | AnonymousJazzAgent },
|
||||
listener: (value: Resolved<T, true>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<T extends CoPlainText>(
|
||||
this: CoValueClass<T>,
|
||||
id: ID<T>,
|
||||
asOrListener: Account | ((value: T) => void),
|
||||
listener?: (value: T) => void,
|
||||
...args: SubscribeRestArgs<T, true>
|
||||
): () => void {
|
||||
if (isAccountInstance(asOrListener)) {
|
||||
return subscribeToCoValue(this, id, asOrListener, [], listener!);
|
||||
}
|
||||
|
||||
return subscribeToCoValue(
|
||||
this,
|
||||
id,
|
||||
activeAccountContext.get(),
|
||||
[],
|
||||
listener!,
|
||||
);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToCoValueWithoutMe<T, true>(this, id, options, listener);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Effectful version of `CoMap.subscribe()` that returns a stream of updates.
|
||||
// *
|
||||
// * Needs to be run inside an `AccountCtx` context.
|
||||
// *
|
||||
// * @category Subscription & Loading
|
||||
// */
|
||||
// static subscribeEf<T extends CoPlainText>(
|
||||
// this: CoValueClass<T>,
|
||||
// id: ID<T>,
|
||||
// ): Stream.Stream<T, UnavailableError, AccountCtx> {
|
||||
// return subscribeToCoValueEf(this, id, []);
|
||||
// }
|
||||
|
||||
/**
|
||||
* Given an already loaded `CoPlainText`, subscribe to updates to the `CoPlainText` and ensure that the specified fields are loaded to the specified depth.
|
||||
*
|
||||
@@ -209,8 +191,8 @@ export class CoPlainText extends String implements CoValue {
|
||||
**/
|
||||
subscribe<T extends CoPlainText>(
|
||||
this: T,
|
||||
listener: (value: T) => void,
|
||||
listener: (value: Resolved<T, true>, unsubscribe: () => void) => void,
|
||||
): () => void {
|
||||
return subscribeToExistingCoValue(this, [], listener);
|
||||
return subscribeToExistingCoValue(this, {}, listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,22 @@ import { type CoValue, type ID } from "./interfaces.js";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export function fulfillsDepth(depth: any, value: CoValue): boolean {
|
||||
if (depth === true || depth === undefined) {
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
value._type === "CoMap" ||
|
||||
value._type === "Group" ||
|
||||
value._type === "Account"
|
||||
) {
|
||||
if (Array.isArray(depth) && depth.length === 1) {
|
||||
if ("$each" in depth) {
|
||||
return Object.entries(value).every(([key, item]) => {
|
||||
return (
|
||||
value as unknown as {
|
||||
_refs: { [key: string]: Ref<CoValue> | undefined };
|
||||
}
|
||||
)._refs[key]
|
||||
? item && fulfillsDepth(depth[0], item)
|
||||
? item && fulfillsDepth(depth.$each, item)
|
||||
: ((value as CoMap)._schema[ItemsSym] as RefEncoded<CoValue>)!
|
||||
.optional;
|
||||
});
|
||||
@@ -44,29 +47,19 @@ export function fulfillsDepth(depth: any, value: CoValue): boolean {
|
||||
return true;
|
||||
}
|
||||
} else if (value._type === "CoList") {
|
||||
if (depth.length === 0) {
|
||||
return true;
|
||||
} else {
|
||||
const itemDepth = depth[0];
|
||||
return (value as CoList).every((item, i) =>
|
||||
(value as CoList)._refs[i]
|
||||
? item && fulfillsDepth(itemDepth, item)
|
||||
: ((value as CoList)._schema[ItemsSym] as RefEncoded<CoValue>)
|
||||
.optional,
|
||||
);
|
||||
}
|
||||
const itemDepth = depth.$each;
|
||||
return (value as CoList).every((item, i) =>
|
||||
(value as CoList)._refs[i]
|
||||
? item && fulfillsDepth(itemDepth, item)
|
||||
: ((value as CoList)._schema[ItemsSym] as RefEncoded<CoValue>).optional,
|
||||
);
|
||||
} else if (value._type === "CoStream") {
|
||||
if (depth.length === 0) {
|
||||
return true;
|
||||
} else {
|
||||
const itemDepth = depth[0];
|
||||
return Object.values((value as CoFeed).perSession).every((entry) =>
|
||||
entry.ref
|
||||
? entry.value && fulfillsDepth(itemDepth, entry.value)
|
||||
: ((value as CoFeed)._schema[ItemsSym] as RefEncoded<CoValue>)
|
||||
.optional,
|
||||
);
|
||||
}
|
||||
const itemDepth = depth.$each;
|
||||
return Object.values((value as CoFeed).perSession).every((entry) =>
|
||||
entry.ref
|
||||
? entry.value && fulfillsDepth(itemDepth, entry.value)
|
||||
: ((value as CoFeed)._schema[ItemsSym] as RefEncoded<CoValue>).optional,
|
||||
);
|
||||
} else if (
|
||||
value._type === "BinaryCoStream" ||
|
||||
value._type === "CoPlainText"
|
||||
@@ -79,58 +72,75 @@ export function fulfillsDepth(depth: any, value: CoValue): boolean {
|
||||
}
|
||||
|
||||
type UnCoNotNull<T> = UnCo<Exclude<T, null>>;
|
||||
type Clean<T> = UnCo<NonNullable<T>>;
|
||||
export type Clean<T> = UnCo<NonNullable<T>>;
|
||||
|
||||
export type DepthsIn<
|
||||
export type RefsToResolve<
|
||||
V,
|
||||
DepthLimit extends number = 5,
|
||||
CurrentDepth extends number[] = [],
|
||||
> =
|
||||
| boolean
|
||||
| (DepthLimit extends CurrentDepth["length"]
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
any
|
||||
: // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
|
||||
V extends Array<infer Item>
|
||||
?
|
||||
| [DepthsIn<UnCoNotNull<Item>, DepthLimit, [0, ...CurrentDepth]>]
|
||||
| never[]
|
||||
| {
|
||||
$each: RefsToResolve<
|
||||
UnCoNotNull<Item>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
}
|
||||
| boolean
|
||||
: // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
|
||||
V extends { _type: "CoMap" | "Group" | "Account" }
|
||||
?
|
||||
| {
|
||||
[Key in CoKeys<V> as Clean<V[Key]> extends CoValue
|
||||
? Key
|
||||
: never]?: DepthsIn<
|
||||
: never]?: RefsToResolve<
|
||||
Clean<V[Key]>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
}
|
||||
| (ItemsSym extends keyof V
|
||||
? [
|
||||
DepthsIn<
|
||||
? {
|
||||
$each: RefsToResolve<
|
||||
Clean<V[ItemsSym]>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>,
|
||||
]
|
||||
>;
|
||||
}
|
||||
: never)
|
||||
| never[]
|
||||
| boolean
|
||||
: V extends {
|
||||
_type: "CoStream";
|
||||
byMe: CoFeedEntry<infer Item> | undefined;
|
||||
}
|
||||
?
|
||||
| [
|
||||
DepthsIn<
|
||||
| {
|
||||
$each: RefsToResolve<
|
||||
UnCoNotNull<Item>,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>,
|
||||
]
|
||||
| never[]
|
||||
: never[])
|
||||
| never[];
|
||||
>;
|
||||
}
|
||||
| boolean
|
||||
: boolean);
|
||||
|
||||
export type RefsToResolveStrict<T, V> = V extends RefsToResolve<T>
|
||||
? RefsToResolve<T>
|
||||
: V;
|
||||
|
||||
export type Resolved<T, R extends RefsToResolve<T> | undefined> = DeeplyLoaded<
|
||||
T,
|
||||
R,
|
||||
5,
|
||||
[]
|
||||
>;
|
||||
|
||||
export type DeeplyLoaded<
|
||||
V,
|
||||
@@ -139,41 +149,42 @@ export type DeeplyLoaded<
|
||||
CurrentDepth extends number[] = [],
|
||||
> = DepthLimit extends CurrentDepth["length"]
|
||||
? V
|
||||
: // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
|
||||
[V] extends [Array<infer Item>]
|
||||
? Depth extends never[] // []
|
||||
? V
|
||||
: UnCoNotNull<Item> extends CoValue
|
||||
? Depth extends Array<infer ItemDepth> // [item-depth]
|
||||
? (UnCoNotNull<Item> &
|
||||
: Depth extends boolean | undefined // Checking against boolean instead of true because the inference from RefsToResolveStrict transforms true into boolean
|
||||
? V
|
||||
: // Basically V extends CoList - but if we used that we'd introduce circularity into the definition of CoList itself
|
||||
[V] extends [Array<infer Item>]
|
||||
? UnCoNotNull<Item> extends CoValue
|
||||
? Depth extends { $each: infer ItemDepth }
|
||||
? // Deeply loaded CoList
|
||||
(UnCoNotNull<Item> &
|
||||
DeeplyLoaded<
|
||||
UnCoNotNull<Item>,
|
||||
ItemDepth,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>)[] &
|
||||
V
|
||||
V // the CoList base type needs to be intersected after so that built-in methods return the correct narrowed array type
|
||||
: never
|
||||
: V
|
||||
: // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
|
||||
[V] extends [{ _type: "CoMap" | "Group" | "Account" }]
|
||||
? Depth extends never[]
|
||||
? V
|
||||
: Depth extends Array<infer ItemDepth>
|
||||
? ItemsSym extends keyof V
|
||||
? V & {
|
||||
: // Basically V extends CoMap | Group | Account - but if we used that we'd introduce circularity into the definition of CoMap itself
|
||||
[V] extends [{ _type: "CoMap" | "Group" | "Account" }]
|
||||
? ItemsSym extends keyof V
|
||||
? Depth extends { $each: infer ItemDepth }
|
||||
? // Deeply loaded Record-like CoMap
|
||||
{
|
||||
[key: string]: DeeplyLoaded<
|
||||
Clean<V[ItemsSym]>,
|
||||
ItemDepth,
|
||||
DepthLimit,
|
||||
[0, ...CurrentDepth]
|
||||
>;
|
||||
}
|
||||
} & V // same reason as in CoList
|
||||
: never
|
||||
: keyof Depth extends never
|
||||
: keyof Depth extends never // Depth = {}
|
||||
? V
|
||||
: {
|
||||
[Key in keyof Depth]-?: Key extends CoKeys<V>
|
||||
: // Deeply loaded CoMap
|
||||
{
|
||||
-readonly [Key in keyof Depth]-?: Key extends CoKeys<V>
|
||||
? Clean<V[Key]> extends CoValue
|
||||
?
|
||||
| DeeplyLoaded<
|
||||
@@ -185,32 +196,31 @@ export type DeeplyLoaded<
|
||||
| (undefined extends V[Key] ? undefined : never)
|
||||
: never
|
||||
: never;
|
||||
} & V
|
||||
: [V] extends [
|
||||
} & V // same reason as in CoList
|
||||
: [V] extends [
|
||||
{
|
||||
_type: "CoStream";
|
||||
byMe: CoFeedEntry<infer Item> | undefined;
|
||||
},
|
||||
]
|
||||
? // Deeply loaded CoStream
|
||||
{
|
||||
_type: "CoStream";
|
||||
byMe: CoFeedEntry<infer Item> | undefined;
|
||||
},
|
||||
]
|
||||
? Depth extends never[]
|
||||
? V
|
||||
: V & {
|
||||
byMe?: { value: UnCoNotNull<Item> };
|
||||
inCurrentSession?: { value: UnCoNotNull<Item> };
|
||||
perSession: {
|
||||
[key: SessionID]: { value: UnCoNotNull<Item> };
|
||||
};
|
||||
} & { [key: ID<Account>]: { value: UnCoNotNull<Item> } }
|
||||
: [V] extends [
|
||||
{
|
||||
_type: "BinaryCoStream";
|
||||
},
|
||||
]
|
||||
? V
|
||||
} & { [key: ID<Account>]: { value: UnCoNotNull<Item> } } & V // same reason as in CoList
|
||||
: [V] extends [
|
||||
{
|
||||
_type: "CoPlainText";
|
||||
_type: "BinaryCoStream";
|
||||
},
|
||||
]
|
||||
? V
|
||||
: never;
|
||||
: [V] extends [
|
||||
{
|
||||
_type: "CoPlainText";
|
||||
},
|
||||
]
|
||||
? V
|
||||
: never;
|
||||
|
||||
@@ -8,11 +8,13 @@ import type {
|
||||
import type {
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
DeeplyLoaded,
|
||||
DepthsIn,
|
||||
ID,
|
||||
RefEncoded,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
Schema,
|
||||
SubscribeRestArgs,
|
||||
} from "../internal.js";
|
||||
import {
|
||||
CoValueBase,
|
||||
@@ -21,6 +23,7 @@ import {
|
||||
ensureCoValueLoaded,
|
||||
loadCoValueWithoutMe,
|
||||
parseGroupCreateOptions,
|
||||
parseSubscribeRestArgs,
|
||||
subscribeToCoValueWithoutMe,
|
||||
subscribeToExistingCoValue,
|
||||
} from "../internal.js";
|
||||
@@ -187,73 +190,59 @@ export class Group extends CoValueBase implements CoValue {
|
||||
}
|
||||
|
||||
/** @category Subscription & Loading */
|
||||
static load<C extends Group, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined>;
|
||||
static load<C extends Group, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined>;
|
||||
static load<C extends Group, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<C>),
|
||||
depth?: Depth & DepthsIn<C>,
|
||||
): Promise<DeeplyLoaded<C, Depth> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, asOrDepth, depth);
|
||||
static load<G extends Group, const R extends RefsToResolve<G>>(
|
||||
this: CoValueClass<G>,
|
||||
id: ID<G>,
|
||||
options?: { resolve?: RefsToResolveStrict<G, R>; loadAs?: Account },
|
||||
): Promise<Resolved<G, R> | undefined> {
|
||||
return loadCoValueWithoutMe(this, id, options);
|
||||
}
|
||||
|
||||
/** @category Subscription & Loading */
|
||||
static subscribe<C extends Group, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
listener: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<G extends Group, const R extends RefsToResolve<G>>(
|
||||
this: CoValueClass<G>,
|
||||
id: ID<G>,
|
||||
listener: (value: Resolved<G, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<C extends Group, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
as: Account,
|
||||
depth: Depth & DepthsIn<C>,
|
||||
listener: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<G extends Group, const R extends RefsToResolve<G>>(
|
||||
this: CoValueClass<G>,
|
||||
id: ID<G>,
|
||||
options: { resolve?: RefsToResolveStrict<G, R>; loadAs?: Account },
|
||||
listener: (value: Resolved<G, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
static subscribe<C extends Group, Depth>(
|
||||
this: CoValueClass<C>,
|
||||
id: ID<C>,
|
||||
asOrDepth: Account | (Depth & DepthsIn<C>),
|
||||
depthOrListener:
|
||||
| (Depth & DepthsIn<C>)
|
||||
| ((value: DeeplyLoaded<C, Depth>) => void),
|
||||
listener?: (value: DeeplyLoaded<C, Depth>) => void,
|
||||
static subscribe<G extends Group, const R extends RefsToResolve<G>>(
|
||||
this: CoValueClass<G>,
|
||||
id: ID<G>,
|
||||
...args: SubscribeRestArgs<G, R>
|
||||
): () => void {
|
||||
return subscribeToCoValueWithoutMe<C, Depth>(
|
||||
this,
|
||||
id,
|
||||
asOrDepth,
|
||||
depthOrListener,
|
||||
listener,
|
||||
);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToCoValueWithoutMe<G, R>(this, id, options, listener);
|
||||
}
|
||||
|
||||
/** @category Subscription & Loading */
|
||||
ensureLoaded<G extends Group, Depth>(
|
||||
ensureLoaded<G extends Group, const R extends RefsToResolve<G>>(
|
||||
this: G,
|
||||
depth: Depth & DepthsIn<G>,
|
||||
): Promise<DeeplyLoaded<G, Depth> | undefined> {
|
||||
return ensureCoValueLoaded(this, depth);
|
||||
options?: { resolve?: RefsToResolveStrict<G, R> },
|
||||
): Promise<Resolved<G, R> | undefined> {
|
||||
return ensureCoValueLoaded(this, options);
|
||||
}
|
||||
|
||||
/** @category Subscription & Loading */
|
||||
subscribe<G extends Group, Depth>(
|
||||
subscribe<G extends Group, const R extends RefsToResolve<G>>(
|
||||
this: G,
|
||||
depth: Depth & DepthsIn<G>,
|
||||
listener: (value: DeeplyLoaded<G, Depth>) => void,
|
||||
listener: (value: Resolved<G, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<G extends Group, const R extends RefsToResolve<G>>(
|
||||
this: G,
|
||||
options: { resolve?: RefsToResolveStrict<G, R> },
|
||||
listener: (value: Resolved<G, R>, unsubscribe: () => void) => void,
|
||||
): () => void;
|
||||
subscribe<G extends Group, const R extends RefsToResolve<G>>(
|
||||
this: G,
|
||||
...args: SubscribeRestArgs<G, R>
|
||||
): () => void {
|
||||
return subscribeToExistingCoValue(this, depth, listener);
|
||||
const { options, listener } = parseSubscribeRestArgs(args);
|
||||
return subscribeToExistingCoValue(this, options, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -167,12 +167,9 @@ export class Inbox {
|
||||
);
|
||||
}
|
||||
|
||||
return loadCoValue(
|
||||
Schema,
|
||||
message.get("payload") as ID<I>,
|
||||
account,
|
||||
[],
|
||||
);
|
||||
return loadCoValue(Schema, message.get("payload") as ID<I>, {
|
||||
loadAs: account,
|
||||
});
|
||||
})
|
||||
.then((value) => {
|
||||
if (!value) {
|
||||
|
||||
@@ -6,7 +6,6 @@ import type {
|
||||
import { RawAccount } from "cojson";
|
||||
import { activeAccountContext } from "../implementation/activeAccountContext.js";
|
||||
import { AnonymousJazzAgent } from "../implementation/anonymousJazzAgent.js";
|
||||
import type { DeeplyLoaded, DepthsIn } from "../internal.js";
|
||||
import {
|
||||
Ref,
|
||||
SubscriptionScope,
|
||||
@@ -15,7 +14,12 @@ import {
|
||||
} from "../internal.js";
|
||||
import { coValuesCache } from "../lib/cache.js";
|
||||
import { type Account } from "./account.js";
|
||||
import { fulfillsDepth } from "./deepLoading.js";
|
||||
import {
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
fulfillsDepth,
|
||||
} from "./deepLoading.js";
|
||||
import { type Group } from "./group.js";
|
||||
import { RegisteredSchemas } from "./registeredSchemas.js";
|
||||
|
||||
@@ -155,36 +159,39 @@ export class CoValueBase implements CoValue {
|
||||
}
|
||||
}
|
||||
|
||||
export function loadCoValueWithoutMe<V extends CoValue, Depth>(
|
||||
export function loadCoValueWithoutMe<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V>,
|
||||
>(
|
||||
cls: CoValueClass<V>,
|
||||
id: ID<V>,
|
||||
asOrDepth: Account | AnonymousJazzAgent | (Depth & DepthsIn<V>),
|
||||
depth?: Depth & DepthsIn<V>,
|
||||
) {
|
||||
if (isAccountInstance(asOrDepth) || isAnonymousAgentInstance(asOrDepth)) {
|
||||
if (!depth) {
|
||||
throw new Error(
|
||||
"Depth is required when loading a CoValue as an Account or AnonymousJazzAgent",
|
||||
);
|
||||
}
|
||||
return loadCoValue(cls, id, asOrDepth, depth);
|
||||
}
|
||||
|
||||
return loadCoValue(cls, id, activeAccountContext.get(), asOrDepth);
|
||||
options?: {
|
||||
resolve?: RefsToResolveStrict<V, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
): Promise<Resolved<V, R> | undefined> {
|
||||
return loadCoValue(cls, id, {
|
||||
...options,
|
||||
loadAs: options?.loadAs ?? activeAccountContext.get(),
|
||||
});
|
||||
}
|
||||
|
||||
export function loadCoValue<V extends CoValue, Depth>(
|
||||
export function loadCoValue<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V>,
|
||||
>(
|
||||
cls: CoValueClass<V>,
|
||||
id: ID<V>,
|
||||
as: Account | AnonymousJazzAgent,
|
||||
depth: Depth & DepthsIn<V>,
|
||||
): Promise<DeeplyLoaded<V, Depth> | undefined> {
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<V, R>;
|
||||
loadAs: Account | AnonymousJazzAgent;
|
||||
},
|
||||
): Promise<Resolved<V, R> | undefined> {
|
||||
return new Promise((resolve) => {
|
||||
subscribeToCoValue(
|
||||
subscribeToCoValue<V, R>(
|
||||
cls,
|
||||
id,
|
||||
as,
|
||||
depth,
|
||||
options,
|
||||
(value, unsubscribe) => {
|
||||
resolve(value);
|
||||
unsubscribe();
|
||||
@@ -196,63 +203,97 @@ export function loadCoValue<V extends CoValue, Depth>(
|
||||
});
|
||||
}
|
||||
|
||||
export function ensureCoValueLoaded<V extends CoValue, Depth>(
|
||||
export function ensureCoValueLoaded<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V>,
|
||||
>(
|
||||
existing: V,
|
||||
depth: Depth & DepthsIn<V>,
|
||||
): Promise<DeeplyLoaded<V, Depth> | undefined> {
|
||||
return loadCoValue(
|
||||
existing.constructor as CoValueClass<V>,
|
||||
existing.id,
|
||||
existing._loadedAs,
|
||||
depth,
|
||||
);
|
||||
options?: { resolve?: RefsToResolveStrict<V, R> } | undefined,
|
||||
): Promise<Resolved<V, R> | undefined> {
|
||||
return loadCoValue(existing.constructor as CoValueClass<V>, existing.id, {
|
||||
loadAs: existing._loadedAs,
|
||||
resolve: options?.resolve,
|
||||
});
|
||||
}
|
||||
|
||||
export function subscribeToCoValueWithoutMe<V extends CoValue, Depth>(
|
||||
type SubscribeListener<V extends CoValue, R extends RefsToResolve<V>> = (
|
||||
value: Resolved<V, R>,
|
||||
unsubscribe: () => void,
|
||||
) => void;
|
||||
|
||||
export type SubscribeRestArgs<V extends CoValue, R extends RefsToResolve<V>> =
|
||||
| [
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<V, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
listener: SubscribeListener<V, R>,
|
||||
]
|
||||
| [listener: SubscribeListener<V, R>];
|
||||
|
||||
export function parseSubscribeRestArgs<
|
||||
V extends CoValue,
|
||||
R extends RefsToResolve<V>,
|
||||
>(
|
||||
args: SubscribeRestArgs<V, R>,
|
||||
): {
|
||||
options: { resolve?: RefsToResolveStrict<V, R> };
|
||||
listener: SubscribeListener<V, R>;
|
||||
} {
|
||||
if (args.length === 2) {
|
||||
if (
|
||||
typeof args[0] === "object" &&
|
||||
"resolve" in args[0] &&
|
||||
typeof args[1] === "function"
|
||||
) {
|
||||
return { options: { resolve: args[0].resolve }, listener: args[1] };
|
||||
} else {
|
||||
throw new Error("Invalid arguments");
|
||||
}
|
||||
} else {
|
||||
if (typeof args[0] === "function") {
|
||||
return { options: {}, listener: args[0] };
|
||||
} else {
|
||||
throw new Error("Invalid arguments");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function subscribeToCoValueWithoutMe<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V>,
|
||||
>(
|
||||
cls: CoValueClass<V>,
|
||||
id: ID<V>,
|
||||
asOrDepth: Account | AnonymousJazzAgent | (Depth & DepthsIn<V>),
|
||||
depthOrListener:
|
||||
| (Depth & DepthsIn<V>)
|
||||
| ((value: DeeplyLoaded<V, Depth>) => void),
|
||||
listener?: (value: DeeplyLoaded<V, Depth>) => void,
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<V, R>;
|
||||
loadAs?: Account | AnonymousJazzAgent;
|
||||
},
|
||||
listener: SubscribeListener<V, R>,
|
||||
) {
|
||||
if (isAccountInstance(asOrDepth) || isAnonymousAgentInstance(asOrDepth)) {
|
||||
if (typeof depthOrListener !== "function") {
|
||||
return subscribeToCoValue<V, Depth>(
|
||||
cls,
|
||||
id,
|
||||
asOrDepth,
|
||||
depthOrListener,
|
||||
listener!,
|
||||
);
|
||||
}
|
||||
throw new Error("Invalid arguments");
|
||||
}
|
||||
|
||||
if (typeof depthOrListener !== "function") {
|
||||
throw new Error("Invalid arguments");
|
||||
}
|
||||
|
||||
return subscribeToCoValue<V, Depth>(
|
||||
return subscribeToCoValue(
|
||||
cls,
|
||||
id,
|
||||
activeAccountContext.get(),
|
||||
asOrDepth,
|
||||
depthOrListener,
|
||||
{ ...options, loadAs: options.loadAs ?? activeAccountContext.get() },
|
||||
listener,
|
||||
);
|
||||
}
|
||||
|
||||
export function subscribeToCoValue<V extends CoValue, Depth>(
|
||||
export function subscribeToCoValue<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V>,
|
||||
>(
|
||||
cls: CoValueClass<V>,
|
||||
id: ID<V>,
|
||||
as: Account | AnonymousJazzAgent,
|
||||
depth: Depth & DepthsIn<V>,
|
||||
listener: (value: DeeplyLoaded<V, Depth>, unsubscribe: () => void) => void,
|
||||
options: {
|
||||
resolve?: RefsToResolveStrict<V, R>;
|
||||
loadAs: Account | AnonymousJazzAgent;
|
||||
},
|
||||
listener: SubscribeListener<V, R>,
|
||||
onUnavailable?: () => void,
|
||||
syncResolution?: boolean,
|
||||
): () => void {
|
||||
const ref = new Ref(id, as, { ref: cls, optional: false });
|
||||
const ref = new Ref(id, options.loadAs, { ref: cls, optional: false });
|
||||
|
||||
let unsubscribed = false;
|
||||
let unsubscribe: (() => void) | undefined;
|
||||
@@ -267,11 +308,8 @@ export function subscribeToCoValue<V extends CoValue, Depth>(
|
||||
value,
|
||||
cls as CoValueClass<V> & CoValueFromRaw<V>,
|
||||
(update, subscription) => {
|
||||
if (fulfillsDepth(depth, update)) {
|
||||
listener(
|
||||
update as DeeplyLoaded<V, Depth>,
|
||||
subscription.unsubscribeAll,
|
||||
);
|
||||
if (fulfillsDepth(options.resolve, update)) {
|
||||
listener(update as Resolved<V, R>, subscription.unsubscribeAll);
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -298,17 +336,22 @@ export function subscribeToCoValue<V extends CoValue, Depth>(
|
||||
};
|
||||
}
|
||||
|
||||
export function createCoValueObservable<V extends CoValue, Depth>(options?: {
|
||||
export function createCoValueObservable<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V>,
|
||||
>(observableOptions?: {
|
||||
syncResolution?: boolean;
|
||||
}) {
|
||||
let currentValue: DeeplyLoaded<V, Depth> | undefined = undefined;
|
||||
let currentValue: Resolved<V, R> | undefined = undefined;
|
||||
let subscriberCount = 0;
|
||||
|
||||
function subscribe(
|
||||
cls: CoValueClass<V>,
|
||||
id: ID<V>,
|
||||
as: Account | AnonymousJazzAgent,
|
||||
depth: Depth & DepthsIn<V>,
|
||||
options: {
|
||||
loadAs: Account | AnonymousJazzAgent;
|
||||
resolve?: RefsToResolveStrict<V, R>;
|
||||
},
|
||||
listener: () => void,
|
||||
onUnavailable?: () => void,
|
||||
) {
|
||||
@@ -317,14 +360,13 @@ export function createCoValueObservable<V extends CoValue, Depth>(options?: {
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
cls,
|
||||
id,
|
||||
as,
|
||||
depth,
|
||||
options,
|
||||
(value) => {
|
||||
currentValue = value;
|
||||
listener();
|
||||
},
|
||||
onUnavailable,
|
||||
options?.syncResolution,
|
||||
observableOptions?.syncResolution,
|
||||
);
|
||||
|
||||
return () => {
|
||||
@@ -344,16 +386,18 @@ export function createCoValueObservable<V extends CoValue, Depth>(options?: {
|
||||
return observable;
|
||||
}
|
||||
|
||||
export function subscribeToExistingCoValue<V extends CoValue, Depth>(
|
||||
export function subscribeToExistingCoValue<
|
||||
V extends CoValue,
|
||||
const R extends RefsToResolve<V>,
|
||||
>(
|
||||
existing: V,
|
||||
depth: Depth & DepthsIn<V>,
|
||||
listener: (value: DeeplyLoaded<V, Depth>) => void,
|
||||
options: { resolve?: RefsToResolveStrict<V, R> } | undefined,
|
||||
listener: SubscribeListener<V, R>,
|
||||
): () => void {
|
||||
return subscribeToCoValue(
|
||||
existing.constructor as CoValueClass<V>,
|
||||
existing.id,
|
||||
existing._loadedAs,
|
||||
depth,
|
||||
{ loadAs: existing._loadedAs, resolve: options?.resolve },
|
||||
listener,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,13 @@ export { CoValueBase } from "./coValues/interfaces.js";
|
||||
export { Profile } from "./coValues/profile.js";
|
||||
export { SchemaUnion } from "./coValues/schemaUnion.js";
|
||||
|
||||
export type { CoValueClass, DeeplyLoaded, DepthsIn } from "./internal.js";
|
||||
export type {
|
||||
CoValueClass,
|
||||
DeeplyLoaded,
|
||||
Resolved,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
} from "./internal.js";
|
||||
|
||||
export {
|
||||
createCoValueObservable,
|
||||
|
||||
@@ -121,15 +121,16 @@ describe("CoFeed resolution", async () => {
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const loadedStream = await TestStream.load(stream.id, meOnSecondPeer, []);
|
||||
const loadedStream = await TestStream.load(stream.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
});
|
||||
|
||||
expect(loadedStream?.[me.id]?.value).toEqual(null);
|
||||
expect(loadedStream?.[me.id]?.ref?.id).toEqual(stream[me.id]?.value?.id);
|
||||
|
||||
const loadedNestedStream = await NestedStream.load(
|
||||
stream[me.id]!.value!.id,
|
||||
meOnSecondPeer,
|
||||
[],
|
||||
{ loadAs: meOnSecondPeer },
|
||||
);
|
||||
|
||||
// expect(loadedStream?.[me.id]?.value).toEqual(loadedNestedStream);
|
||||
@@ -145,8 +146,7 @@ describe("CoFeed resolution", async () => {
|
||||
|
||||
const loadedTwiceNestedStream = await TwiceNestedStream.load(
|
||||
stream[me.id]!.value![me.id]!.value!.id,
|
||||
meOnSecondPeer,
|
||||
[],
|
||||
{ loadAs: meOnSecondPeer },
|
||||
);
|
||||
|
||||
// expect(loadedStream?.[me.id]?.value?.[me.id]?.value).toEqual(
|
||||
@@ -208,9 +208,13 @@ describe("CoFeed resolution", async () => {
|
||||
|
||||
const queue = new cojsonInternals.Channel();
|
||||
|
||||
TestStream.subscribe(stream.id, meOnSecondPeer, [], (subscribedStream) => {
|
||||
void queue.push(subscribedStream);
|
||||
});
|
||||
TestStream.subscribe(
|
||||
stream.id,
|
||||
{ loadAs: meOnSecondPeer },
|
||||
(subscribedStream) => {
|
||||
void queue.push(subscribedStream);
|
||||
},
|
||||
);
|
||||
|
||||
const update1 = (await queue.next()).value;
|
||||
expect(update1[me.id]?.value).toEqual(null);
|
||||
@@ -325,7 +329,9 @@ describe("FileStream loading & Subscription", async () => {
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const loadedStream = await FileStream.load(stream.id, meOnSecondPeer, []);
|
||||
const loadedStream = await FileStream.load(stream.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
});
|
||||
|
||||
expect(loadedStream?.getChunks()).toEqual({
|
||||
mimeType: "text/plain",
|
||||
@@ -358,9 +364,13 @@ describe("FileStream loading & Subscription", async () => {
|
||||
|
||||
const queue = new cojsonInternals.Channel();
|
||||
|
||||
FileStream.subscribe(stream.id, meOnSecondPeer, [], (subscribedStream) => {
|
||||
void queue.push(subscribedStream);
|
||||
});
|
||||
FileStream.subscribe(
|
||||
stream.id,
|
||||
{ loadAs: meOnSecondPeer },
|
||||
(subscribedStream) => {
|
||||
void queue.push(subscribedStream);
|
||||
},
|
||||
);
|
||||
|
||||
const update1 = (await queue.next()).value;
|
||||
expect(update1.getChunks()).toBe(undefined);
|
||||
@@ -429,9 +439,7 @@ describe("FileStream.loadAsBlob", async () => {
|
||||
const { stream, me } = await setup();
|
||||
stream.push(new Uint8Array([1]));
|
||||
|
||||
const promise = FileStream.loadAsBlob(stream.id, me);
|
||||
|
||||
await stream.ensureLoaded([]);
|
||||
const promise = FileStream.loadAsBlob(stream.id, { loadAs: me });
|
||||
|
||||
stream.push(new Uint8Array([2]));
|
||||
stream.end();
|
||||
@@ -447,12 +455,11 @@ describe("FileStream.loadAsBlob", async () => {
|
||||
const { stream, me } = await setup();
|
||||
stream.push(new Uint8Array([1]));
|
||||
|
||||
const promise = FileStream.loadAsBlob(stream.id, me, {
|
||||
const promise = FileStream.loadAsBlob(stream.id, {
|
||||
loadAs: me,
|
||||
allowUnfinished: true,
|
||||
});
|
||||
|
||||
await stream.ensureLoaded([]);
|
||||
|
||||
stream.push(new Uint8Array([2]));
|
||||
stream.end();
|
||||
|
||||
|
||||
@@ -180,16 +180,14 @@ describe("CoList resolution", async () => {
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const loadedList = await TestList.load(list.id, meOnSecondPeer, []);
|
||||
const loadedList = await TestList.load(list.id, { loadAs: meOnSecondPeer });
|
||||
|
||||
expect(loadedList?.[0]).toBe(null);
|
||||
expect(loadedList?._refs[0]?.id).toEqual(list[0]!.id);
|
||||
|
||||
const loadedNestedList = await NestedList.load(
|
||||
list[0]!.id,
|
||||
meOnSecondPeer,
|
||||
[],
|
||||
);
|
||||
const loadedNestedList = await NestedList.load(list[0]!.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
});
|
||||
|
||||
expect(loadedList?.[0]).toBeDefined();
|
||||
expect(loadedList?.[0]?.[0]).toBe(null);
|
||||
@@ -200,11 +198,9 @@ describe("CoList resolution", async () => {
|
||||
loadedNestedList?.toJSON(),
|
||||
);
|
||||
|
||||
const loadedTwiceNestedList = await TwiceNestedList.load(
|
||||
list[0]![0]!.id,
|
||||
meOnSecondPeer,
|
||||
[],
|
||||
);
|
||||
const loadedTwiceNestedList = await TwiceNestedList.load(list[0]![0]!.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
});
|
||||
|
||||
expect(loadedList?.[0]?.[0]).toBeDefined();
|
||||
expect(loadedList?.[0]?.[0]?.[0]).toBe("a");
|
||||
@@ -253,13 +249,17 @@ describe("CoList resolution", async () => {
|
||||
|
||||
const queue = new cojsonInternals.Channel();
|
||||
|
||||
TestList.subscribe(list.id, meOnSecondPeer, [], (subscribedList) => {
|
||||
console.log(
|
||||
"subscribedList?.[0]?.[0]?.[0]",
|
||||
subscribedList?.[0]?.[0]?.[0],
|
||||
);
|
||||
void queue.push(subscribedList);
|
||||
});
|
||||
TestList.subscribe(
|
||||
list.id,
|
||||
{ loadAs: meOnSecondPeer },
|
||||
(subscribedList) => {
|
||||
console.log(
|
||||
"subscribedList?.[0]?.[0]?.[0]",
|
||||
subscribedList?.[0]?.[0]?.[0],
|
||||
);
|
||||
void queue.push(subscribedList);
|
||||
},
|
||||
);
|
||||
|
||||
const update1 = (await queue.next()).value;
|
||||
expect(update1?.[0]).toBe(null);
|
||||
|
||||
@@ -418,7 +418,7 @@ describe("CoMap resolution", async () => {
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const loadedMap = await TestMap.load(map.id, meOnSecondPeer, {});
|
||||
const loadedMap = await TestMap.load(map.id, { loadAs: meOnSecondPeer });
|
||||
|
||||
expect(loadedMap?.color).toEqual("red");
|
||||
expect(loadedMap?.height).toEqual(10);
|
||||
@@ -426,11 +426,9 @@ describe("CoMap resolution", async () => {
|
||||
expect(loadedMap?._refs.nested?.id).toEqual(map.nested?.id);
|
||||
expect(loadedMap?._refs.nested?.value).toEqual(null);
|
||||
|
||||
const loadedNestedMap = await NestedMap.load(
|
||||
map.nested!.id,
|
||||
meOnSecondPeer,
|
||||
{},
|
||||
);
|
||||
const loadedNestedMap = await NestedMap.load(map.nested!.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
});
|
||||
|
||||
expect(loadedMap?.nested?.name).toEqual("nested");
|
||||
expect(loadedMap?.nested?._fancyName).toEqual("Sir nested");
|
||||
@@ -439,8 +437,7 @@ describe("CoMap resolution", async () => {
|
||||
|
||||
const loadedTwiceNestedMap = await TwiceNestedMap.load(
|
||||
map.nested!.twiceNested!.id,
|
||||
meOnSecondPeer,
|
||||
{},
|
||||
{ loadAs: meOnSecondPeer },
|
||||
);
|
||||
|
||||
expect(loadedMap?.nested?.twiceNested?.taste).toEqual("sour");
|
||||
@@ -491,7 +488,7 @@ describe("CoMap resolution", async () => {
|
||||
|
||||
const queue = new cojsonInternals.Channel<TestMap>();
|
||||
|
||||
TestMap.subscribe(map.id, meOnSecondPeer, {}, (subscribedMap) => {
|
||||
TestMap.subscribe(map.id, { loadAs: meOnSecondPeer }, (subscribedMap) => {
|
||||
// Read to property to trigger loading
|
||||
subscribedMap.nested?.twiceNested?.taste;
|
||||
void queue.push(subscribedMap);
|
||||
|
||||
@@ -108,7 +108,7 @@ describe("CoPlainText", () => {
|
||||
});
|
||||
|
||||
// Load the text on the second peer
|
||||
const loaded = await CoPlainText.load(id, meOnSecondPeer);
|
||||
const loaded = await CoPlainText.load(id, { loadAs: meOnSecondPeer });
|
||||
expect(loaded).toBeDefined();
|
||||
expect(loaded!.toString()).toBe("hello world");
|
||||
});
|
||||
@@ -140,9 +140,13 @@ describe("CoPlainText", () => {
|
||||
const queue = new cojsonInternals.Channel();
|
||||
|
||||
// Subscribe to text updates
|
||||
CoPlainText.subscribe(text.id, meOnSecondPeer, (subscribedText) => {
|
||||
void queue.push(subscribedText);
|
||||
});
|
||||
CoPlainText.subscribe(
|
||||
text.id,
|
||||
{ loadAs: meOnSecondPeer },
|
||||
(subscribedText) => {
|
||||
void queue.push(subscribedText);
|
||||
},
|
||||
);
|
||||
|
||||
// Initial subscription should give us the text
|
||||
const update1 = (await queue.next()).value;
|
||||
|
||||
@@ -602,17 +602,17 @@ describe("CoRichText", async () => {
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const loadedText = await CoRichText.load(text.id, meOnSecondPeer, {
|
||||
marks: [{}],
|
||||
text: [],
|
||||
const loadedText = await CoRichText.load(text.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: { marks: { $each: true }, text: true },
|
||||
});
|
||||
|
||||
expect(loadedText).toBeDefined();
|
||||
expect(loadedText?.toString()).toEqual("hello world");
|
||||
|
||||
const loadedText2 = await CoRichText.load(text.id, meOnSecondPeer, {
|
||||
marks: [{}],
|
||||
text: [],
|
||||
const loadedText2 = await CoRichText.load(text.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: { marks: { $each: true }, text: true },
|
||||
});
|
||||
|
||||
expect(loadedText2).toBeDefined();
|
||||
@@ -645,8 +645,10 @@ describe("CoRichText", async () => {
|
||||
|
||||
CoRichText.subscribe(
|
||||
text.id,
|
||||
meOnSecondPeer,
|
||||
{ marks: [{}], text: [] },
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: { marks: { $each: true }, text: true },
|
||||
},
|
||||
(subscribedText) => {
|
||||
void queue.push(subscribedText);
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const Crypto = await WasmCrypto.create();
|
||||
import { connectedPeers } from "cojson/src/streamUtils.ts";
|
||||
import { cojsonInternals } from "cojson";
|
||||
import { describe, expect, expectTypeOf, test } from "vitest";
|
||||
import {
|
||||
Account,
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
} from "../index.web.js";
|
||||
import { randomSessionProvider } from "../internal.js";
|
||||
|
||||
const { connectedPeers } = cojsonInternals;
|
||||
|
||||
class TestMap extends CoMap {
|
||||
list = co.ref(TestList);
|
||||
optionalRef = co.ref(InnermostMap, { optional: true });
|
||||
@@ -81,14 +83,17 @@ describe("Deep loading with depth arg", async () => {
|
||||
ownership,
|
||||
);
|
||||
|
||||
const map1 = await TestMap.load(map.id, meOnSecondPeer, {});
|
||||
const map1 = await TestMap.load(map.id, { loadAs: meOnSecondPeer });
|
||||
expectTypeOf(map1).toEqualTypeOf<TestMap | undefined>();
|
||||
if (map1 === undefined) {
|
||||
throw new Error("map1 is undefined");
|
||||
}
|
||||
expect(map1.list).toBe(null);
|
||||
|
||||
const map2 = await TestMap.load(map.id, meOnSecondPeer, { list: [] });
|
||||
const map2 = await TestMap.load(map.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: { list: true },
|
||||
});
|
||||
expectTypeOf(map2).toEqualTypeOf<
|
||||
| (TestMap & {
|
||||
list: TestList;
|
||||
@@ -101,7 +106,10 @@ describe("Deep loading with depth arg", async () => {
|
||||
expect(map2.list).not.toBe(null);
|
||||
expect(map2.list[0]).toBe(null);
|
||||
|
||||
const map3 = await TestMap.load(map.id, meOnSecondPeer, { list: [{}] });
|
||||
const map3 = await TestMap.load(map.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: { list: { $each: true } },
|
||||
});
|
||||
expectTypeOf(map3).toEqualTypeOf<
|
||||
| (TestMap & {
|
||||
list: TestList & InnerMap[];
|
||||
@@ -114,8 +122,9 @@ describe("Deep loading with depth arg", async () => {
|
||||
expect(map3.list[0]).not.toBe(null);
|
||||
expect(map3.list[0]?.stream).toBe(null);
|
||||
|
||||
const map3a = await TestMap.load(map.id, meOnSecondPeer, {
|
||||
optionalRef: {},
|
||||
const map3a = await TestMap.load(map.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: { optionalRef: true } as const,
|
||||
});
|
||||
expectTypeOf(map3a).toEqualTypeOf<
|
||||
| (TestMap & {
|
||||
@@ -124,8 +133,9 @@ describe("Deep loading with depth arg", async () => {
|
||||
| undefined
|
||||
>();
|
||||
|
||||
const map4 = await TestMap.load(map.id, meOnSecondPeer, {
|
||||
list: [{ stream: [] }],
|
||||
const map4 = await TestMap.load(map.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: { list: { $each: { stream: true } } },
|
||||
});
|
||||
expectTypeOf(map4).toEqualTypeOf<
|
||||
| (TestMap & {
|
||||
@@ -140,8 +150,9 @@ describe("Deep loading with depth arg", async () => {
|
||||
expect(map4.list[0]?.stream?.[me.id]).not.toBe(null);
|
||||
expect(map4.list[0]?.stream?.byMe?.value).toBe(null);
|
||||
|
||||
const map5 = await TestMap.load(map.id, meOnSecondPeer, {
|
||||
list: [{ stream: [{}] }],
|
||||
const map5 = await TestMap.load(map.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: { list: { $each: { stream: { $each: true } } } },
|
||||
});
|
||||
type ExpectedMap5 =
|
||||
| (TestMap & {
|
||||
@@ -198,8 +209,10 @@ class CustomAccount extends Account {
|
||||
}
|
||||
|
||||
const thisLoaded = await this.ensureLoaded({
|
||||
profile: { stream: [] },
|
||||
root: { list: [] },
|
||||
resolve: {
|
||||
profile: { stream: true },
|
||||
root: { list: true },
|
||||
},
|
||||
});
|
||||
expectTypeOf(thisLoaded).toEqualTypeOf<
|
||||
| (CustomAccount & {
|
||||
@@ -222,8 +235,10 @@ test("Deep loading within account", async () => {
|
||||
});
|
||||
|
||||
const meLoaded = await me.ensureLoaded({
|
||||
profile: { stream: [] },
|
||||
root: { list: [] },
|
||||
resolve: {
|
||||
profile: { stream: true },
|
||||
root: { list: true },
|
||||
},
|
||||
});
|
||||
expectTypeOf(meLoaded).toEqualTypeOf<
|
||||
| (CustomAccount & {
|
||||
@@ -285,9 +300,12 @@ test("Deep loading a record-like coMap", async () => {
|
||||
{ owner: me },
|
||||
);
|
||||
|
||||
const recordLoaded = await RecordLike.load(record.id, meOnSecondPeer, [
|
||||
{ list: [{}] },
|
||||
]);
|
||||
const recordLoaded = await RecordLike.load(record.id, {
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: {
|
||||
$each: { list: { $each: true } },
|
||||
},
|
||||
});
|
||||
expectTypeOf(recordLoaded).toEqualTypeOf<
|
||||
| (RecordLike & {
|
||||
[key: string]: TestMap & {
|
||||
@@ -304,3 +322,53 @@ test("Deep loading a record-like coMap", async () => {
|
||||
expect(recordLoaded.key2?.list).not.toBe(null);
|
||||
expect(recordLoaded.key2?.list).not.toBe(undefined);
|
||||
});
|
||||
|
||||
test("The resolve type doesn't accept extra keys", async () => {
|
||||
const me = await CustomAccount.create({
|
||||
creationProps: { name: "Hermes Puggington" },
|
||||
crypto: Crypto,
|
||||
});
|
||||
|
||||
const meLoaded = await me.ensureLoaded({
|
||||
resolve: {
|
||||
// @ts-expect-error
|
||||
profile: { stream: true, extraKey: true },
|
||||
// @ts-expect-error
|
||||
root: { list: true, extraKey: true },
|
||||
},
|
||||
});
|
||||
|
||||
await me.ensureLoaded({
|
||||
resolve: {
|
||||
// @ts-expect-error
|
||||
root: { list: { $each: true, extraKey: true } },
|
||||
},
|
||||
});
|
||||
|
||||
await me.ensureLoaded({
|
||||
resolve: {
|
||||
root: { list: true },
|
||||
// @ts-expect-error
|
||||
extraKey: true,
|
||||
},
|
||||
});
|
||||
|
||||
expectTypeOf(meLoaded).toEqualTypeOf<
|
||||
| (CustomAccount & {
|
||||
profile: CustomProfile & {
|
||||
stream: TestStream;
|
||||
extraKey: never;
|
||||
};
|
||||
root: TestMap & {
|
||||
list: TestList;
|
||||
extraKey: never;
|
||||
};
|
||||
})
|
||||
| undefined
|
||||
>();
|
||||
if (meLoaded === undefined) {
|
||||
throw new Error("meLoaded is undefined");
|
||||
}
|
||||
expect(meLoaded.profile.stream).not.toBe(null);
|
||||
expect(meLoaded.root.list).not.toBe(null);
|
||||
});
|
||||
|
||||
@@ -119,18 +119,16 @@ describe("Group inheritance", () => {
|
||||
|
||||
const mapInChild = TestMap.create({ title: "In Child" }, { owner: group });
|
||||
|
||||
const mapAsReader = await TestMap.load(mapInChild.id, reader, {});
|
||||
const mapAsReader = await TestMap.load(mapInChild.id, { loadAs: reader });
|
||||
expect(mapAsReader?.title).toBe("In Child");
|
||||
|
||||
parentGroup.removeMember(reader);
|
||||
|
||||
mapInChild.title = "In Child (updated)";
|
||||
|
||||
const mapAsReaderAfterUpdate = await TestMap.load(
|
||||
mapInChild.id,
|
||||
reader,
|
||||
{},
|
||||
);
|
||||
const mapAsReaderAfterUpdate = await TestMap.load(mapInChild.id, {
|
||||
loadAs: reader,
|
||||
});
|
||||
expect(mapAsReaderAfterUpdate?.title).toBe("In Child");
|
||||
});
|
||||
|
||||
@@ -158,18 +156,18 @@ describe("Group inheritance", () => {
|
||||
{ owner: group },
|
||||
);
|
||||
|
||||
const mapAsReader = await TestMap.load(mapInGrandChild.id, reader, {});
|
||||
const mapAsReader = await TestMap.load(mapInGrandChild.id, {
|
||||
loadAs: reader,
|
||||
});
|
||||
expect(mapAsReader?.title).toBe("In Grand Child");
|
||||
|
||||
grandParentGroup.removeMember(reader);
|
||||
|
||||
mapInGrandChild.title = "In Grand Child (updated)";
|
||||
|
||||
const mapAsReaderAfterUpdate = await TestMap.load(
|
||||
mapInGrandChild.id,
|
||||
reader,
|
||||
{},
|
||||
);
|
||||
const mapAsReaderAfterUpdate = await TestMap.load(mapInGrandChild.id, {
|
||||
loadAs: reader,
|
||||
});
|
||||
expect(mapAsReaderAfterUpdate?.title).toBe("In Grand Child");
|
||||
});
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ describe("Inbox", () => {
|
||||
);
|
||||
const resultId = await inboxSender.sendMessage(message);
|
||||
|
||||
const result = await Message.load(resultId, receiver, {});
|
||||
const result = await Message.load(resultId, { loadAs: receiver });
|
||||
expect(result?.text).toBe("Responded from the inbox");
|
||||
|
||||
unsubscribe();
|
||||
|
||||
@@ -88,23 +88,16 @@ describe("SchemaUnion", () => {
|
||||
{ owner: me },
|
||||
);
|
||||
|
||||
const loadedButtonWidget = await loadCoValue(
|
||||
WidgetUnion,
|
||||
buttonWidget.id,
|
||||
me,
|
||||
{},
|
||||
);
|
||||
const loadedSliderWidget = await loadCoValue(
|
||||
WidgetUnion,
|
||||
sliderWidget.id,
|
||||
me,
|
||||
{},
|
||||
);
|
||||
const loadedButtonWidget = await loadCoValue(WidgetUnion, buttonWidget.id, {
|
||||
loadAs: me,
|
||||
});
|
||||
const loadedSliderWidget = await loadCoValue(WidgetUnion, sliderWidget.id, {
|
||||
loadAs: me,
|
||||
});
|
||||
const loadedCheckboxWidget = await loadCoValue(
|
||||
WidgetUnion,
|
||||
checkboxWidget.id,
|
||||
me,
|
||||
{},
|
||||
{ loadAs: me },
|
||||
);
|
||||
|
||||
expect(loadedButtonWidget).toBeInstanceOf(RedButtonWidget);
|
||||
@@ -121,8 +114,7 @@ describe("SchemaUnion", () => {
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
WidgetUnion,
|
||||
buttonWidget.id,
|
||||
me,
|
||||
{},
|
||||
{ loadAs: me },
|
||||
(value: BaseWidget) => {
|
||||
if (value instanceof BlueButtonWidget) {
|
||||
expect(value.label).toBe(currentValue);
|
||||
|
||||
@@ -8,11 +8,7 @@ import {
|
||||
Group,
|
||||
co,
|
||||
} from "../index.web.js";
|
||||
import {
|
||||
type DepthsIn,
|
||||
createCoValueObservable,
|
||||
subscribeToCoValue,
|
||||
} from "../internal.js";
|
||||
import { createCoValueObservable, subscribeToCoValue } from "../internal.js";
|
||||
import { setupAccount, waitFor } from "./utils.js";
|
||||
|
||||
class ChatRoom extends CoMap {
|
||||
@@ -53,8 +49,7 @@ describe("subscribeToCoValue", () => {
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
ChatRoom,
|
||||
chatRoom.id,
|
||||
meOnSecondPeer,
|
||||
{},
|
||||
{ loadAs: meOnSecondPeer },
|
||||
updateFn,
|
||||
);
|
||||
|
||||
@@ -114,9 +109,11 @@ describe("subscribeToCoValue", () => {
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
ChatRoom,
|
||||
chatRoom.id,
|
||||
meOnSecondPeer,
|
||||
{
|
||||
messages: [],
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: {
|
||||
messages: true,
|
||||
},
|
||||
},
|
||||
updateFn,
|
||||
);
|
||||
@@ -153,9 +150,13 @@ describe("subscribeToCoValue", () => {
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
ChatRoom,
|
||||
chatRoom.id,
|
||||
meOnSecondPeer,
|
||||
{
|
||||
messages: [{}],
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: {
|
||||
messages: {
|
||||
$each: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
updateFn,
|
||||
);
|
||||
@@ -198,13 +199,15 @@ describe("subscribeToCoValue", () => {
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
ChatRoom,
|
||||
chatRoom.id,
|
||||
meOnSecondPeer,
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
reactions: [],
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: {
|
||||
messages: {
|
||||
$each: {
|
||||
reactions: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
updateFn,
|
||||
);
|
||||
@@ -264,13 +267,15 @@ describe("subscribeToCoValue", () => {
|
||||
const unsubscribe = subscribeToCoValue(
|
||||
ChatRoom,
|
||||
chatRoom.id,
|
||||
meOnSecondPeer,
|
||||
{
|
||||
messages: [
|
||||
{
|
||||
reactions: [],
|
||||
loadAs: meOnSecondPeer,
|
||||
resolve: {
|
||||
messages: {
|
||||
$each: {
|
||||
reactions: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
updateFn,
|
||||
);
|
||||
@@ -321,14 +326,15 @@ describe("createCoValueObservable", () => {
|
||||
it("should update currentValue when subscribed", async () => {
|
||||
const { me, meOnSecondPeer } = await setupAccount();
|
||||
const testMap = createTestMap(me);
|
||||
const observable = createCoValueObservable<TestMap, DepthsIn<TestMap>>();
|
||||
const observable = createCoValueObservable();
|
||||
const mockListener = vi.fn();
|
||||
|
||||
const unsubscribe = observable.subscribe(
|
||||
TestMap,
|
||||
testMap.id,
|
||||
meOnSecondPeer,
|
||||
{},
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
},
|
||||
() => {
|
||||
mockListener();
|
||||
},
|
||||
@@ -349,14 +355,15 @@ describe("createCoValueObservable", () => {
|
||||
it("should reset to undefined after unsubscribe", async () => {
|
||||
const { me, meOnSecondPeer } = await setupAccount();
|
||||
const testMap = createTestMap(me);
|
||||
const observable = createCoValueObservable<TestMap, DepthsIn<TestMap>>();
|
||||
const observable = createCoValueObservable();
|
||||
const mockListener = vi.fn();
|
||||
|
||||
const unsubscribe = observable.subscribe(
|
||||
TestMap,
|
||||
testMap.id,
|
||||
meOnSecondPeer,
|
||||
{},
|
||||
{
|
||||
loadAs: meOnSecondPeer,
|
||||
},
|
||||
() => {
|
||||
mockListener();
|
||||
},
|
||||
|
||||
@@ -19,7 +19,7 @@ describe("Jazz Test Sync", () => {
|
||||
map._raw.set("test", "value");
|
||||
|
||||
// Verify account2 can see the group
|
||||
const loadedMap = await CoMap.load(map.id, account2, {});
|
||||
const loadedMap = await CoMap.load(map.id, { loadAs: account2 });
|
||||
expect(loadedMap).toBeDefined();
|
||||
expect(loadedMap?._raw.get("test")).toBe("value");
|
||||
});
|
||||
|
||||
@@ -9,8 +9,10 @@ import {
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
DeeplyLoaded,
|
||||
DepthsIn,
|
||||
ID,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
Resolved,
|
||||
subscribeToCoValue,
|
||||
} from "jazz-tools";
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
@@ -49,16 +51,16 @@ export function createUseAccountComposables<Acc extends Account>() {
|
||||
me: ComputedRef<Acc>;
|
||||
logOut: () => void;
|
||||
};
|
||||
function useAccount<D extends DepthsIn<Acc>>(
|
||||
depth: D,
|
||||
): {
|
||||
me: ComputedRef<DeeplyLoaded<Acc, D> | undefined>;
|
||||
function useAccount<const R extends RefsToResolve<Acc>>(options?: {
|
||||
resolve?: RefsToResolveStrict<Acc, R>;
|
||||
}): {
|
||||
me: ComputedRef<Resolved<Acc, R> | undefined>;
|
||||
logOut: () => void;
|
||||
};
|
||||
function useAccount<D extends DepthsIn<Acc>>(
|
||||
depth?: D,
|
||||
): {
|
||||
me: ComputedRef<Acc | DeeplyLoaded<Acc, D> | undefined>;
|
||||
function useAccount<const R extends RefsToResolve<Acc>>(options?: {
|
||||
resolve?: RefsToResolveStrict<Acc, R>;
|
||||
}): {
|
||||
me: ComputedRef<Acc | Resolved<Acc, R> | undefined>;
|
||||
logOut: () => void;
|
||||
} {
|
||||
const context = useJazzContext();
|
||||
@@ -75,16 +77,16 @@ export function createUseAccountComposables<Acc extends Account>() {
|
||||
|
||||
const contextMe = context.value.me as Acc;
|
||||
|
||||
const me = useCoState<Acc, D>(
|
||||
const me = useCoState<Acc, R>(
|
||||
contextMe.constructor as CoValueClass<Acc>,
|
||||
contextMe.id,
|
||||
depth,
|
||||
options,
|
||||
);
|
||||
|
||||
return {
|
||||
me: computed(() => {
|
||||
const value =
|
||||
depth === undefined
|
||||
options?.resolve === undefined
|
||||
? me.value || toRaw((context.value as BrowserContext<Acc>).me)
|
||||
: me.value;
|
||||
|
||||
@@ -97,17 +99,15 @@ export function createUseAccountComposables<Acc extends Account>() {
|
||||
function useAccountOrGuest(): {
|
||||
me: ComputedRef<Acc | AnonymousJazzAgent>;
|
||||
};
|
||||
function useAccountOrGuest<D extends DepthsIn<Acc>>(
|
||||
depth: D,
|
||||
): {
|
||||
me: ComputedRef<DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent>;
|
||||
function useAccountOrGuest<const R extends RefsToResolve<Acc>>(options?: {
|
||||
resolve?: RefsToResolveStrict<Acc, R>;
|
||||
}): {
|
||||
me: ComputedRef<Resolved<Acc, R> | undefined | AnonymousJazzAgent>;
|
||||
};
|
||||
function useAccountOrGuest<D extends DepthsIn<Acc>>(
|
||||
depth?: D,
|
||||
): {
|
||||
me: ComputedRef<
|
||||
Acc | DeeplyLoaded<Acc, D> | undefined | AnonymousJazzAgent
|
||||
>;
|
||||
function useAccountOrGuest<const R extends RefsToResolve<Acc>>(options?: {
|
||||
resolve?: RefsToResolveStrict<Acc, R>;
|
||||
}): {
|
||||
me: ComputedRef<Acc | Resolved<Acc, R> | undefined | AnonymousJazzAgent>;
|
||||
} {
|
||||
const context = useJazzContext();
|
||||
|
||||
@@ -119,16 +119,16 @@ export function createUseAccountComposables<Acc extends Account>() {
|
||||
"me" in context.value ? (context.value.me as Acc) : undefined,
|
||||
);
|
||||
|
||||
const me = useCoState<Acc, D>(
|
||||
const me = useCoState<Acc, R>(
|
||||
contextMe.value?.constructor as CoValueClass<Acc>,
|
||||
contextMe.value?.id,
|
||||
depth,
|
||||
options,
|
||||
);
|
||||
|
||||
if ("me" in context.value) {
|
||||
return {
|
||||
me: computed(() =>
|
||||
depth === undefined
|
||||
options?.resolve === undefined
|
||||
? me.value || toRaw((context.value as BrowserContext<Acc>).me)
|
||||
: me.value,
|
||||
),
|
||||
@@ -151,13 +151,12 @@ const { useAccount, useAccountOrGuest } =
|
||||
|
||||
export { useAccount, useAccountOrGuest };
|
||||
|
||||
export function useCoState<V extends CoValue, D>(
|
||||
export function useCoState<V extends CoValue, const R extends RefsToResolve<V>>(
|
||||
Schema: CoValueClass<V>,
|
||||
id: MaybeRef<ID<V> | undefined>,
|
||||
depth: D & DepthsIn<V> = [] as D & DepthsIn<V>,
|
||||
): Ref<DeeplyLoaded<V, D> | undefined> {
|
||||
const state: ShallowRef<DeeplyLoaded<V, D> | undefined> =
|
||||
shallowRef(undefined);
|
||||
options?: { resolve?: RefsToResolveStrict<V, R> },
|
||||
): Ref<Resolved<V, R> | undefined> {
|
||||
const state: ShallowRef<Resolved<V, R> | undefined> = shallowRef(undefined);
|
||||
const context = useJazzContext();
|
||||
|
||||
if (!context.value) {
|
||||
@@ -167,7 +166,7 @@ export function useCoState<V extends CoValue, D>(
|
||||
let unsubscribe: (() => void) | undefined;
|
||||
|
||||
watch(
|
||||
[() => unref(id), () => context, () => Schema, () => depth],
|
||||
[() => unref(id), () => context, () => Schema, () => options],
|
||||
() => {
|
||||
if (unsubscribe) unsubscribe();
|
||||
|
||||
@@ -177,10 +176,13 @@ export function useCoState<V extends CoValue, D>(
|
||||
unsubscribe = subscribeToCoValue(
|
||||
Schema,
|
||||
idValue,
|
||||
"me" in context.value
|
||||
? toRaw(context.value.me)
|
||||
: toRaw(context.value.guest),
|
||||
depth,
|
||||
{
|
||||
resolve: options?.resolve,
|
||||
loadAs:
|
||||
"me" in context.value
|
||||
? toRaw(context.value.me)
|
||||
: toRaw(context.value.guest),
|
||||
},
|
||||
(value) => {
|
||||
state.value = value;
|
||||
},
|
||||
|
||||
@@ -39,7 +39,9 @@ describe("useAccount", () => {
|
||||
const [result] = withJazzTestSetup(
|
||||
() =>
|
||||
useAccount({
|
||||
root: {},
|
||||
resolve: {
|
||||
root: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
account,
|
||||
|
||||
@@ -51,7 +51,9 @@ describe("useAccountOrGuest", () => {
|
||||
const [result] = withJazzTestSetup(
|
||||
() =>
|
||||
useAccountOrGuest({
|
||||
root: {},
|
||||
resolve: {
|
||||
root: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
account,
|
||||
@@ -68,7 +70,9 @@ describe("useAccountOrGuest", () => {
|
||||
const [result] = withJazzTestSetup(
|
||||
() =>
|
||||
useAccountOrGuest({
|
||||
root: {},
|
||||
resolve: {
|
||||
root: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
account,
|
||||
|
||||
@@ -81,7 +81,9 @@ describe("useCoState", () => {
|
||||
const [result] = withJazzTestSetup(
|
||||
() =>
|
||||
useCoState(TestMap, map.id, {
|
||||
nested: {},
|
||||
resolve: {
|
||||
nested: true,
|
||||
},
|
||||
}),
|
||||
{
|
||||
account,
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Form } from "./Form.tsx";
|
||||
import { Logo } from "./Logo.tsx";
|
||||
|
||||
function App() {
|
||||
const { me, logOut } = useAccount({ profile: {}, root: {} });
|
||||
const { me, logOut } = useAccount({ resolve: { profile: true, root: true } });
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useAccount } from "jazz-react";
|
||||
|
||||
export function Form() {
|
||||
const { me } = useAccount({ profile: {}, root: {} });
|
||||
const { me } = useAccount({ resolve: { profile: true, root: true } });
|
||||
|
||||
if (!me) return null;
|
||||
|
||||
|
||||
@@ -2,25 +2,27 @@ import {
|
||||
Account,
|
||||
CoValue,
|
||||
CoValueClass,
|
||||
DepthsIn,
|
||||
ID,
|
||||
RefsToResolve,
|
||||
RefsToResolveStrict,
|
||||
subscribeToCoValue,
|
||||
} from "jazz-tools";
|
||||
|
||||
export function waitForCoValue<T extends CoValue>(
|
||||
export function waitForCoValue<
|
||||
T extends CoValue,
|
||||
const R extends RefsToResolve<T>,
|
||||
>(
|
||||
coMap: CoValueClass<T>,
|
||||
valueId: ID<T>,
|
||||
account: Account,
|
||||
predicate: (value: T) => boolean,
|
||||
depth: DepthsIn<T>,
|
||||
options: { loadAs: Account; resolve?: RefsToResolveStrict<T, R> },
|
||||
) {
|
||||
return new Promise<T>((resolve) => {
|
||||
function subscribe() {
|
||||
subscribeToCoValue(
|
||||
coMap,
|
||||
valueId,
|
||||
account,
|
||||
depth,
|
||||
options,
|
||||
(value, unsubscribe) => {
|
||||
if (predicate(value)) {
|
||||
resolve(value);
|
||||
|
||||
@@ -9,7 +9,9 @@ export function DownloaderPeer(props: { testCoMapId: ID<UploadedFile> }) {
|
||||
|
||||
useEffect(() => {
|
||||
async function run(me: Account, uploadedFileId: ID<UploadedFile>) {
|
||||
const uploadedFile = await UploadedFile.load(uploadedFileId, me, {});
|
||||
const uploadedFile = await UploadedFile.load(uploadedFileId, {
|
||||
loadAs: me,
|
||||
});
|
||||
|
||||
if (!uploadedFile) {
|
||||
throw new Error("Uploaded file not found");
|
||||
@@ -21,7 +23,9 @@ export function DownloaderPeer(props: { testCoMapId: ID<UploadedFile> }) {
|
||||
|
||||
uploadedFile.coMapDownloaded = true;
|
||||
|
||||
await FileStream.loadAsBlob(uploadedFile._refs.file.id, me);
|
||||
await FileStream.loadAsBlob(uploadedFile._refs.file.id, {
|
||||
loadAs: me,
|
||||
});
|
||||
|
||||
uploadedFile.syncCompleted = true;
|
||||
}
|
||||
|
||||
@@ -46,9 +46,8 @@ export function UploaderPeer() {
|
||||
await waitForCoValue(
|
||||
UploadedFile,
|
||||
file.id,
|
||||
account.me,
|
||||
(value) => value.syncCompleted,
|
||||
{},
|
||||
{ loadAs: account.me },
|
||||
);
|
||||
|
||||
iframe.remove();
|
||||
|
||||
@@ -53,11 +53,11 @@ export function InboxPage() {
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
if (!id) return;
|
||||
const account = await Account.load(id, me, {});
|
||||
const account = await Account.load(id);
|
||||
|
||||
if (!account) return;
|
||||
|
||||
const group = Group.create({ owner: me });
|
||||
const group = Group.create();
|
||||
group.addMember(account, "writer");
|
||||
const pingPong = PingPong.create({ ping: Date.now() }, { owner: group });
|
||||
|
||||
|
||||
@@ -95,14 +95,13 @@ function SharedCoMapWithChildren(props: {
|
||||
level: number;
|
||||
revealLevels: number;
|
||||
}) {
|
||||
const coMap = useCoState(SharedCoMap, props.id, {});
|
||||
const { me } = useAccount();
|
||||
const coMap = useCoState(SharedCoMap, props.id);
|
||||
const nextLevel = props.level + 1;
|
||||
|
||||
const addChild = () => {
|
||||
if (!me || !coMap) return;
|
||||
if (!coMap) return;
|
||||
|
||||
const group = Group.create({ owner: me });
|
||||
const group = Group.create();
|
||||
|
||||
const child = SharedCoMap.create(
|
||||
{ value: "CoValue child " + nextLevel },
|
||||
@@ -118,7 +117,7 @@ function SharedCoMapWithChildren(props: {
|
||||
|
||||
while (node?._refs.child?.id) {
|
||||
const parentGroup = node._owner as Group;
|
||||
node = await SharedCoMap.load(node._refs.child.id, me, {});
|
||||
node = await SharedCoMap.load(node._refs.child.id);
|
||||
|
||||
if (node) {
|
||||
const childGroup = node._owner as Group;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { createInviteLink } from "jazz-react";
|
||||
import { useAcceptInvite, useAccount, useCoState } from "jazz-react";
|
||||
import { CoList, CoMap, Group, ID, co } from "jazz-tools";
|
||||
import { useAcceptInvite, useCoState } from "jazz-react";
|
||||
import { Account, CoList, CoMap, Group, ID, co } from "jazz-tools";
|
||||
import { useState } from "react";
|
||||
|
||||
class SharedCoMap extends CoMap {
|
||||
@@ -10,15 +10,14 @@ class SharedCoMap extends CoMap {
|
||||
class SharedCoList extends CoList.Of(co.ref(SharedCoMap)) {}
|
||||
|
||||
export function WriteOnlyRole() {
|
||||
const { me } = useAccount();
|
||||
const [id, setId] = useState<ID<SharedCoList> | undefined>(undefined);
|
||||
const [inviteLinks, setInviteLinks] = useState<Record<string, string>>({});
|
||||
const coList = useCoState(SharedCoList, id, []);
|
||||
const coList = useCoState(SharedCoList, id);
|
||||
|
||||
const createCoList = async () => {
|
||||
if (!me || id) return;
|
||||
if (id) return;
|
||||
|
||||
const group = Group.create({ owner: me });
|
||||
const group = Group.create();
|
||||
|
||||
const coList = SharedCoList.create([], { owner: group });
|
||||
|
||||
@@ -35,7 +34,7 @@ export function WriteOnlyRole() {
|
||||
};
|
||||
|
||||
const addNewItem = async () => {
|
||||
if (!me || !coList) return;
|
||||
if (!coList) return;
|
||||
|
||||
const group = coList._owner as Group;
|
||||
const coMap = SharedCoMap.create({ value: "" }, { owner: group });
|
||||
@@ -52,7 +51,7 @@ export function WriteOnlyRole() {
|
||||
if (
|
||||
member.account &&
|
||||
member.role !== "admin" &&
|
||||
member.account.id !== me.id
|
||||
member.account.id !== Account.getMe().id
|
||||
) {
|
||||
coListGroup.removeMember(member.account);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user