Mathias
Mathias
CCConvex Community
Created by Mathias on 9/22/2024 in #support-community
How to do something on a specific date or time?
I'm wondering if it is possible to do something on a specific date or time. For instance, I have a taskboard in my app where I can set a due date. I would like to add to a table if the date is either near, on date or past date. I was looking at scheduled functions, but I couldn't figure out if that could lookup a value in the database and then do the function?
3 replies
CCConvex Community
Created by Mathias on 8/20/2024 in #support-community
How to upload images using Convex storage? (Solved)
I'm wondering if anyone has played around using Convex storage for their text editors? I'm using the Novel.sh package, and they are providing an uploadFn like this:
import { createImageUpload } from "novel/plugins"
import { toast } from "sonner"

const onUpload = async (file: File) => {
console.log("Uploading file", file)

const generateUploadUrl = useMutation(api.files.generateUploadUrl);
const { startUpload } = useUploadFiles(generateUploadUrl);

await startUpload([file])
}

export const uploadFn = createImageUpload({
onUpload,
validateFn: (file) => {
if (!file.type.includes("image/")) {
toast.error("File type not supported.")
return false
}
if (file.size / 1024 / 1024 > 20) {
toast.error("File size too big (max 20MB).")
return false
}
return true
},
})
import { createImageUpload } from "novel/plugins"
import { toast } from "sonner"

const onUpload = async (file: File) => {
console.log("Uploading file", file)

const generateUploadUrl = useMutation(api.files.generateUploadUrl);
const { startUpload } = useUploadFiles(generateUploadUrl);

await startUpload([file])
}

export const uploadFn = createImageUpload({
onUpload,
validateFn: (file) => {
if (!file.type.includes("image/")) {
toast.error("File type not supported.")
return false
}
if (file.size / 1024 / 1024 > 20) {
toast.error("File size too big (max 20MB).")
return false
}
return true
},
})
I have stripped it down to get rid of the Vercel blob storage implementation, and though I would use the uploadstuff.dev, but I really struggle to make it work within the onUpload. It does not seem to run the following lines:
const generateUploadUrl = useMutation(api.files.generateUploadUrl);
const { startUpload } = useUploadFiles(generateUploadUrl);

await startUpload([file])
const generateUploadUrl = useMutation(api.files.generateUploadUrl);
const { startUpload } = useUploadFiles(generateUploadUrl);

await startUpload([file])
If I run the console.log, I get the following:
argument #0:

'Uploading file'
file:

File {
name: 'IMG_1403.CR3',
lastModified: 1701880784000,
lastModifiedDate: new Date('2023-12-06T16:39:44.000Z'),
webkitRelativePath: '',
size: 18311314,
type: 'image/x-canon-cr3'
}
argument #0:

'Uploading file'
file:

File {
name: 'IMG_1403.CR3',
lastModified: 1701880784000,
lastModifiedDate: new Date('2023-12-06T16:39:44.000Z'),
webkitRelativePath: '',
size: 18311314,
type: 'image/x-canon-cr3'
}
So it would seem that the onUpload is running but not the Convex specific functions. What have I missed?
2 replies
CCConvex Community
Created by Mathias on 8/17/2024 in #support-community
How to fix Uncaught Error: Not authenticated?
When I reload the page within my app, I get this error message in the Convex logs within the dashboard:
Aug 17, 18:58:42

Q
users:getUser
failure
22ms
Uncaught Error: Not authenticated
at handler (../convex/users.ts:50:4)
Aug 17, 18:58:42

Q
users:getUser
failure
22ms
Uncaught Error: Not authenticated
at handler (../convex/users.ts:50:4)
This isthe getUser query:
export const getUser = query({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Not authenticated")
}

const userId = identity.subject

const user = ctx.db
.query("users")
.withIndex("by_user", (q) => q.eq("userId", userId))
.first()

return user
},
})
export const getUser = query({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Not authenticated")
}

const userId = identity.subject

const user = ctx.db
.query("users")
.withIndex("by_user", (q) => q.eq("userId", userId))
.first()

return user
},
})
The query does seem to work, as I am logged in to the app. Is there a loading step to add to the query, so the error doesn't show unless the identity is in fact missing?
3 replies
CCConvex Community
Created by Mathias on 8/17/2024 in #support-community
How to use skip in my useStableQuery?
I wonder if I can use skip using my useStableQuery, when args are not present. The reason I got to think about it was when I realised that I got a lot of similar logs like this one within Convex dashboard:
flows:getById
failure
1ms
ArgumentValidationError: Value does not match validator.
Path: .id
Value: null
Validator: v.id("flows")
flows:getById
failure
1ms
ArgumentValidationError: Value does not match validator.
Path: .id
Value: null
Validator: v.id("flows")
I then tried something like this:
const { data: flow, isPending } = useStableQuery(
`flows + ${flowId}`,
api.flows.getById,
flowId !== undefined ? { id: flowId } : "skip"
)
const { data: flow, isPending } = useStableQuery(
`flows + ${flowId}`,
api.flows.getById,
flowId !== undefined ? { id: flowId } : "skip"
)
This is my useStableQuery:
export const useStableQuery = ((globalKey, query, ...args) => {
const { status, data, error } = useQuery(query, ...args)
const convex = useConvex()
const cache = ((convex as any).__cache__ ??= {})

if (status === "success" && data !== undefined) {
cache[globalKey] = data
}

return {
status,
data: cache[globalKey],
error,
isSuccess: status === "success",
isPending: status === "pending",
isError: status === "error",
}
}) as QueryWithGlobalCacheKey
export const useStableQuery = ((globalKey, query, ...args) => {
const { status, data, error } = useQuery(query, ...args)
const convex = useConvex()
const cache = ((convex as any).__cache__ ??= {})

if (status === "success" && data !== undefined) {
cache[globalKey] = data
}

return {
status,
data: cache[globalKey],
error,
isSuccess: status === "success",
isPending: status === "pending",
isError: status === "error",
}
}) as QueryWithGlobalCacheKey
How can I make sure I conditionally query when args are present?
4 replies
CCConvex Community
Created by Mathias on 8/8/2024 in #support-community
How to implement sort by reordering items in a list?
I'm about to implement sorting in a table, where I would like to use either Framer motion or Atlassian Pragmatic drag and drop. Has anyone experience with mutations and how this can be done? This is the schema for what I want to sort by the ordercolumn.
js
statuses: defineTable({
name: v.string(),
description: v.optional(v.string()),
color: v.string(),
order: v.number(),
projectId: v.string(),
templateId: v.optional(v.string()),
})
.index("by_project", ["projectId"])
.index("by_template", ["templateId"]),
js
statuses: defineTable({
name: v.string(),
description: v.optional(v.string()),
color: v.string(),
order: v.number(),
projectId: v.string(),
templateId: v.optional(v.string()),
})
.index("by_project", ["projectId"])
.index("by_template", ["templateId"]),
Framer motion reorder seems to do it by index and using states. I would like to avoid states if possible and do mutations directly. What do you think is a good way to do this?
4 replies
CCConvex Community
Created by Mathias on 7/9/2024 in #support-community
Queries and best practices: How to query multiple tables and return object?
I have a few queries, that seems very large in terms of lines of code, and I was wondering if there is a better way to do this. This example is a get query to get multiple tables to then end up returning one object with a lot of information: See the code example here as Discord doesn't allow longer messages: https://stackoverflow.com/questions/78727640/queries-and-best-practices-how-to-query-multiple-tables-and-return-object-using An example of my flow table:
flows: defineTable({
title: v.string(),
content: v.optional(v.string()),
parentId: v.optional(v.string()),
teamId: v.string(),
pipelineId: v.string(),
statusId: v.optional(v.string()),
priorityId: v.optional(v.string()),
typeId: v.optional(v.string()),
})
.index("by_team", ["teamId"])
.index("by_parent", ["parentId"])
.index("by_pipeline", ["pipelineId"])
.index("by_status", ["statusId"])
.index("by_type", ["typeId"]),
flows: defineTable({
title: v.string(),
content: v.optional(v.string()),
parentId: v.optional(v.string()),
teamId: v.string(),
pipelineId: v.string(),
statusId: v.optional(v.string()),
priorityId: v.optional(v.string()),
typeId: v.optional(v.string()),
})
.index("by_team", ["teamId"])
.index("by_parent", ["parentId"])
.index("by_pipeline", ["pipelineId"])
.index("by_status", ["statusId"])
.index("by_type", ["typeId"]),
So, I essentially want to get the status, priority and type of the flow returned in the same object. I heard about the query convex-helpers/react but I'm not sure if that can be of any help. I was also wondering of abstracting some of the logic to other files and then importing them where I'm going to use it, is a good idea?
7 replies
CCConvex Community
Created by Mathias on 6/11/2024 in #support-community
How to fix Uncaught Error: Not found
I have this getTeams query:
export const getTeams = query({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Unauthenticated")
}

const userId = identity.subject

const user = await ctx.db
.query("users")
.withIndex("by_user", (q) => q.eq("userId", userId))
.first()

if (!user) {
throw new Error("Not found")
}

const teamIds = await ctx.db
.query("teamMembers")
.withIndex("by_user", (q) => q.eq("userId", user._id))
.collect()

const teams = await Promise.all(
teamIds.map((id) => ctx.db.get(id.teamId as Id<"teams">))
)

return teams.filter((team) => team !== null)
},
})
export const getTeams = query({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Unauthenticated")
}

const userId = identity.subject

const user = await ctx.db
.query("users")
.withIndex("by_user", (q) => q.eq("userId", userId))
.first()

if (!user) {
throw new Error("Not found")
}

const teamIds = await ctx.db
.query("teamMembers")
.withIndex("by_user", (q) => q.eq("userId", user._id))
.collect()

const teams = await Promise.all(
teamIds.map((id) => ctx.db.get(id.teamId as Id<"teams">))
)

return teams.filter((team) => team !== null)
},
})
I receive this error when no teams can be found where the user is also a member.
Error: [CONVEX Q(teams:getTeams)] [Request ID: 30092d87adbe25b8] Server Error
Uncaught Error: Not found
at handler (../convex/teams.ts:70:4)

Called by client
Error: [CONVEX Q(teams:getTeams)] [Request ID: 30092d87adbe25b8] Server Error
Uncaught Error: Not found
at handler (../convex/teams.ts:70:4)

Called by client
How can I solve this?
6 replies
CCConvex Community
Created by Mathias on 5/4/2024 in #support-community
How to prevent re-rendering using useMutation?
I'm using a Sheet component from Shadcn/ui in my Next.js project. Currently, the Sheet "closes" automatically whenever there are changes to statuses, assignees, etc. This behavior appears to be linked to the use of useMutation because if I disable the mutation, the Sheet remains open during these changes. I need the Sheet to stay open even while data changes via mutations. It appears that the issue is present either using useState from React or when using Zustand. Is there a method to prevent the Sheet from auto-closing when the mutation is executed?
11 replies
CCConvex Community
Created by Mathias on 3/26/2024 in #support-community
How to group tasks by their column?
I'm trying to group tasks by their respective column by doing a Map(), but I get the following error: Uncaught Error: Map[] is not a supported Convex type. This is what I have tried:
export const getTasksGroupedByColumns = query({
args: { boardId: v.id("boards") },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) {
throw new Error("Not authenticated")
}

const columns = await ctx.db
.query("columns")
.withIndex("by_board", (q) => q.eq("boardId", args.boardId))
.collect()

const columnIds = columns.map((column) => column._id)

const tasksByColumnId = await Promise.all(
columnIds.map(async (columnId) => {
const tasks = await ctx.db
.query("tasks")
.withIndex("by_column", (q) => q.eq("columnId", columnId))
.collect()
return { columnId, tasks }
})
)

const result = new Map()

columns.forEach((column) => {
const { tasks } = tasksByColumnId.find(
({ columnId }) => columnId === column._id
) || { tasks: [] }
result.set(column._id, {
...column,
tasks,
})
})

return result
},
})
export const getTasksGroupedByColumns = query({
args: { boardId: v.id("boards") },
handler: async (ctx, args) => {
const identity = await ctx.auth.getUserIdentity()
if (!identity) {
throw new Error("Not authenticated")
}

const columns = await ctx.db
.query("columns")
.withIndex("by_board", (q) => q.eq("boardId", args.boardId))
.collect()

const columnIds = columns.map((column) => column._id)

const tasksByColumnId = await Promise.all(
columnIds.map(async (columnId) => {
const tasks = await ctx.db
.query("tasks")
.withIndex("by_column", (q) => q.eq("columnId", columnId))
.collect()
return { columnId, tasks }
})
)

const result = new Map()

columns.forEach((column) => {
const { tasks } = tasksByColumnId.find(
({ columnId }) => columnId === column._id
) || { tasks: [] }
result.set(column._id, {
...column,
tasks,
})
})

return result
},
})
I would like to end up with data like this:
[
{ column: { /* column data */ }, tasks: [ /* array of tasks */ ] },
column: { /* column data */ }, tasks: [ /* array of tasks */ ] },
]
[
{ column: { /* column data */ }, tasks: [ /* array of tasks */ ] },
column: { /* column data */ }, tasks: [ /* array of tasks */ ] },
]
So I can render it like this:
<div>
{Array.from(columns).map(([columnId, { name, tasks }]) => (
<div key={columnId}>
<h3>{name}</h3>
{tasks.map((task) => (
<div key={task.id}>{task.title}</div>
))}
</div>
))}
</div>
<div>
{Array.from(columns).map(([columnId, { name, tasks }]) => (
<div key={columnId}>
<h3>{name}</h3>
{tasks.map((task) => (
<div key={task.id}>{task.title}</div>
))}
</div>
))}
</div>
5 replies
CCConvex Community
Created by Mathias on 3/10/2024 in #support-community
How to query and filter with multiple arguments?
I'm trying to query workspaceMembers, where both userId and workspaceId are true. I tried below but it returned null:
const getWorkspaceMemberItem = await ctx.db
.query("workspaceMembers")
.filter((q) =>
q.and(
q.eq("userId", args.userId),
q.eq("workspaceId", args.workspaceId)
)
)
.first()
const getWorkspaceMemberItem = await ctx.db
.query("workspaceMembers")
.filter((q) =>
q.and(
q.eq("userId", args.userId),
q.eq("workspaceId", args.workspaceId)
)
)
.first()
I also tried with withIndex but it seems you can only use one. What is a good way to query with multiple arguments for filtering or indexing? Update: I got the following to work for my use case, but I'm still not sure if that is the most efficient or correct way to do it:
const workspaceMembers = await ctx.db
.query("workspaceMembers")
.withIndex("by_userId", (q) => q.eq("userId", args.userId))
.collect()

const getWorkspaceMemberItem = workspaceMembers.find(
(member) => member.workspaceId === args.workspaceId
)
const workspaceMembers = await ctx.db
.query("workspaceMembers")
.withIndex("by_userId", (q) => q.eq("userId", args.userId))
.collect()

const getWorkspaceMemberItem = workspaceMembers.find(
(member) => member.workspaceId === args.workspaceId
)
3 replies
CCConvex Community
Created by Mathias on 3/9/2024 in #support-community
How to call a function within another function?
I would like to get an id from another function, instead of providing the id via args. My use case is that I want to get all users from a group using the current users activeGroupId. This query gives me the Id that I need:
export const getActiveSpace = internalQuery({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Unauthenticated")
}

const userId = identity.subject

const user = await ctx.db
.query("users")
.withIndex("by_user", (q) => q.eq("userId", userId))
.first()

if (!user) {
throw new Error("Not found")
}

return user.activeSpace
},
})
export const getActiveSpace = internalQuery({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Unauthenticated")
}

const userId = identity.subject

const user = await ctx.db
.query("users")
.withIndex("by_user", (q) => q.eq("userId", userId))
.first()

if (!user) {
throw new Error("Not found")
}

return user.activeSpace
},
})
Now, I don't know how to use it in my query to get all members. This is the current code, where the groupId is an empty string, which is the one I want to get replaced by the output of getActiveSpace:
export const getGroupMembers = query({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Unauthenticated")
}

const groupId = ""

const groupMembers = await ctx.db
.query("groupMembers")
.withIndex("by_groupId", (q) => q.eq("groupId", groupId))
.collect()

const userIds = groupMembers.map((member) => member.userId)

const users = await Promise.all(
userIds.map((id) => ctx.db.get(id as Id<"users">))
)

return users.filter((user) => user !== null)
},
})
export const getGroupMembers = query({
handler: async (ctx) => {
const identity = await ctx.auth.getUserIdentity()

if (!identity) {
throw new Error("Unauthenticated")
}

const groupId = ""

const groupMembers = await ctx.db
.query("groupMembers")
.withIndex("by_groupId", (q) => q.eq("groupId", groupId))
.collect()

const userIds = groupMembers.map((member) => member.userId)

const users = await Promise.all(
userIds.map((id) => ctx.db.get(id as Id<"users">))
)

return users.filter((user) => user !== null)
},
})
Can you point me in towards a ressource or help me what I missed to get it to work? There might also be a simpler solution.
6 replies
CCConvex Community
Created by Mathias on 3/9/2024 in #support-community
Query all users based on an Id
I'm working on optimizing a data query process in our application and could use your insight. Currently, I'm faced with a rather inefficient method of fetching all users associated with a specific group. The process involves multiple steps: initially querying for a user to determine their 'activeGroup' and then using this identifier to fetch the group and its associated users. This approach seems overly cumbersome and I'm convinced there must be a more direct and efficient method. To give you a clearer picture, here's how our data is structured: User Document Example:
{
"about": "",
"activeGroup": "js73fcztnesqdyddnfxrn5j6056mvtd8",
"email": "hello@patrick.com",
"fullName": "Patrick Elvira",
"imageUrl": "be8746d5-da3a-4a37-923f-467ed7911a84.png",
"initials": "MRS",
"status": "active",
"termsAcceptedAt": "2024-03-06T07:03:45.083Z",
"userId": "user_3cWkkgNS5bjSv0dfdsdf333jlyDxyZHfCgu",
"groupIds": ["js7b4ck0xxrhhaxw2kbzyjpv896mswee", "js73fcztnesqdyddnfxrn5j6056mvtd8"]
}
{
"about": "",
"activeGroup": "js73fcztnesqdyddnfxrn5j6056mvtd8",
"email": "hello@patrick.com",
"fullName": "Patrick Elvira",
"imageUrl": "be8746d5-da3a-4a37-923f-467ed7911a84.png",
"initials": "MRS",
"status": "active",
"termsAcceptedAt": "2024-03-06T07:03:45.083Z",
"userId": "user_3cWkkgNS5bjSv0dfdsdf333jlyDxyZHfCgu",
"groupIds": ["js7b4ck0xxrhhaxw2kbzyjpv896mswee", "js73fcztnesqdyddnfxrn5j6056mvtd8"]
}
Group Document Example:
{
"about": "This is an about section for the group.",
"email": "group@group.io",
"logo": "45dcbf78-1461-400e-83c5-b466a8fb9d61.png",
"name": "Group",
"ownerId": "user_3cWkkgNS5bjds0HjldfxyZ43Cgu"
}
{
"about": "This is an about section for the group.",
"email": "group@group.io",
"logo": "45dcbf78-1461-400e-83c5-b466a8fb9d61.png",
"name": "Group",
"ownerId": "user_3cWkkgNS5bjds0HjldfxyZ43Cgu"
}
My goal is to simplify this process, ideally querying for all users within a specific group directly, without the preliminary steps currently required. I'm searching for any resources or advice you may have on achieving this more effectively. Do you know of any methods or tools that could facilitate a more streamlined approach to querying these relationships? Thanks in advance for your help!
8 replies