MapleLeaf 🍁
MapleLeaf 🍁
CCConvex Community
Created by Definedβ„’ on 12/14/2024 in #support-community
Sync local database to Convex database
probably need to add like a localData arg to the mutation function or something
10 replies
CCConvex Community
Created by Definedβ„’ on 12/14/2024 in #support-community
Sync local database to Convex database
this also assumes the data passed to the mutation is the same as what you get from the query, so uh... :kek:
10 replies
CCConvex Community
Created by Definedβ„’ on 12/14/2024 in #support-community
Sync local database to Convex database
you'd have to make sure your key is unique depending on the function args you pass; you could automatically compute a key from the function reference and the args, but i wanted to keep it simple
10 replies
CCConvex Community
Created by Definedβ„’ on 12/14/2024 in #support-community
Sync local database to Convex database
I'm on a phone and told Claude to write this, but this is how I'd go about it
import { useQuery, useMutation } from 'convex/react';
import { useEffect, useState } from 'react';
import type {
OptionalQueryArgs,
OptionalQueryArgsOrSkip,
FunctionArgs,
FunctionResult,
FunctionReference
} from 'convex/server';

/**
* Hook that wraps Convex useQuery with local storage caching
* @param key - Local storage key to use for caching
* @param queryFn - Convex query function reference
* @param args - Arguments to pass to query function
* @returns Data from local storage while loading, then remote data
*/
export function useQueryWithLocalStorage<Query extends FunctionReference<'query', 'public'>>(
key: string,
queryFn: Query,
...args: OptionalQueryArgsOrSkip<Query>
): FunctionResult<Query> | null {
// Get initial data from local storage
const [localData, setLocalData] = useState<FunctionResult<Query> | null>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : null;
});

// Get remote data
const remoteData = useQuery(queryFn, ...args);

// Update local storage when remote data changes
useEffect(() => {
if (remoteData !== undefined) {
localStorage.setItem(key, JSON.stringify(remoteData));
setLocalData(remoteData);
}
}, [key, remoteData]);

// Return local data while loading, then remote data
return remoteData === undefined ? localData : remoteData;
}

/**
* Hook that wraps Convex useMutation with optimistic local storage updates
* @param key - Local storage key to update
* @param mutationFn - Convex mutation function reference
* @returns Wrapped mutation function that handles local storage updates
*/
export function useMutationWithLocalStorage<Mutation extends FunctionReference<'mutation', 'public'>>(
key: string,
mutationFn: Mutation
): (args: FunctionArgs<Mutation>) => Promise<FunctionResult<Mutation>> {
const mutation = useMutation(mutationFn);

const wrappedMutation = async (args: FunctionArgs<Mutation>) => {
// Get current data
const currentData = localStorage.getItem(key);
const parsedData = currentData ? JSON.parse(currentData) : null;

try {
// Optimistically update local storage
if (parsedData) {
// Assuming the mutation affects the data in a predictable way
// You may need to customize this based on your mutation logic
const optimisticData = Array.isArray(parsedData)
? [...parsedData, args] // If array, append new item
: { ...parsedData, ...args }; // If object, merge new data

localStorage.setItem(key, JSON.stringify(optimisticData));
}

// Call remote mutation
const result = await mutation(args);
return result;

} catch (error) {
// Revert local storage on error
if (currentData) {
localStorage.setItem(key, currentData);
}
throw error;
}
};

return wrappedMutation;
}
import { useQuery, useMutation } from 'convex/react';
import { useEffect, useState } from 'react';
import type {
OptionalQueryArgs,
OptionalQueryArgsOrSkip,
FunctionArgs,
FunctionResult,
FunctionReference
} from 'convex/server';

/**
* Hook that wraps Convex useQuery with local storage caching
* @param key - Local storage key to use for caching
* @param queryFn - Convex query function reference
* @param args - Arguments to pass to query function
* @returns Data from local storage while loading, then remote data
*/
export function useQueryWithLocalStorage<Query extends FunctionReference<'query', 'public'>>(
key: string,
queryFn: Query,
...args: OptionalQueryArgsOrSkip<Query>
): FunctionResult<Query> | null {
// Get initial data from local storage
const [localData, setLocalData] = useState<FunctionResult<Query> | null>(() => {
const stored = localStorage.getItem(key);
return stored ? JSON.parse(stored) : null;
});

// Get remote data
const remoteData = useQuery(queryFn, ...args);

// Update local storage when remote data changes
useEffect(() => {
if (remoteData !== undefined) {
localStorage.setItem(key, JSON.stringify(remoteData));
setLocalData(remoteData);
}
}, [key, remoteData]);

// Return local data while loading, then remote data
return remoteData === undefined ? localData : remoteData;
}

/**
* Hook that wraps Convex useMutation with optimistic local storage updates
* @param key - Local storage key to update
* @param mutationFn - Convex mutation function reference
* @returns Wrapped mutation function that handles local storage updates
*/
export function useMutationWithLocalStorage<Mutation extends FunctionReference<'mutation', 'public'>>(
key: string,
mutationFn: Mutation
): (args: FunctionArgs<Mutation>) => Promise<FunctionResult<Mutation>> {
const mutation = useMutation(mutationFn);

const wrappedMutation = async (args: FunctionArgs<Mutation>) => {
// Get current data
const currentData = localStorage.getItem(key);
const parsedData = currentData ? JSON.parse(currentData) : null;

try {
// Optimistically update local storage
if (parsedData) {
// Assuming the mutation affects the data in a predictable way
// You may need to customize this based on your mutation logic
const optimisticData = Array.isArray(parsedData)
? [...parsedData, args] // If array, append new item
: { ...parsedData, ...args }; // If object, merge new data

localStorage.setItem(key, JSON.stringify(optimisticData));
}

// Call remote mutation
const result = await mutation(args);
return result;

} catch (error) {
// Revert local storage on error
if (currentData) {
localStorage.setItem(key, currentData);
}
throw error;
}
};

return wrappedMutation;
}
10 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
thanks for the help! :meowthumbsup:
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
alright that seems to work, but there's some other stuff I need to fix on my end that I'll take a stab at later
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
ah yeah
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
this is the CI command for build:
run: pnpm convex deploy
--cmd "pnpm run build"
--cmd-url-env-var-name VITE_PUBLIC_CONVEX_URL
--preview-name "e2e-${{ github.ref_name }}"
run: pnpm convex deploy
--cmd "pnpm run build"
--cmd-url-env-var-name VITE_PUBLIC_CONVEX_URL
--preview-name "e2e-${{ github.ref_name }}"
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
so if it's not being picked up, then that means I'm not loading the env correctly? πŸ€”
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
oh I wasn't aware the deploy command generated an env file
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
what I mean is, in CI, it runs convex deploy ... to run the build and deploy the functions, but I'm not sure how to get the preview URL from that point to run the tests
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
well, that doesn't exist in CI
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
actually, the issue still remains that I can't get the VITE_PUBLIC_CONVEX_URL variable for the preview after the build step happens, unless I'm missing something? πŸ€”
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
you have no idea how long I've needed this lmao
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
that'd be more straightforward and one less dependency than having to run a child process to run a function
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
although maybe it could be possible to create an authoritative instance of ConvexHttpClient? one that can run internal mutations, given a deploy key
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
If we added something like npx convex run "seed:characters" --preview-name "e2e-..." or npx convex run "seed:characters" --url VITE_PUBLIC_CONVEX_URL which required a CONVEX_DEPLOY_KEY, would that work here?
yeah that would probably work :meowthumbsup:
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 12/4/2023 in #support-community
e2e testing with preview deployments
although maybe I can approach this differently πŸ€” I could hit an API route in my app like /setup/characters, which would then run that function in the backend and probably checks some conditional PREVIEW=true env to make sure it can't just be run arbitrarily
28 replies
CCConvex Community
Created by MapleLeaf 🍁 on 8/18/2023 in #support-community
Feature request: v.record() and v.unknown()
in fact we can kind of already do that by doing v.any() as Validator<Something>
9 replies