Skip to content

Commit

Permalink
Merge pull request #27 from prtcl/spike/project-details
Browse files Browse the repository at this point in the history
spike/project details
  • Loading branch information
prtcl authored Nov 8, 2024
2 parents fcfae7f + 8d5b1db commit 2a67cb9
Show file tree
Hide file tree
Showing 43 changed files with 3,987 additions and 2,891 deletions.
2 changes: 2 additions & 0 deletions convex/_generated/api.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import type {
FilterApi,
FunctionReference,
} from "convex/server";
import type * as details from "../details.js";
import type * as features from "../features.js";
import type * as previews from "../previews.js";
import type * as projects from "../projects.js";
Expand All @@ -26,6 +27,7 @@ import type * as projects from "../projects.js";
* ```
*/
declare const fullApi: ApiFromModules<{
details: typeof details;
features: typeof features;
previews: typeof previews;
projects: typeof projects;
Expand Down
161 changes: 161 additions & 0 deletions convex/details.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import { ConvexError, v } from 'convex/values';
import type { Doc, Id } from './_generated/dataModel';
import { internalMutation, type MutationCtx, query } from './_generated/server';

export const loadProjectDetails = query({
args: { projectId: v.id('projects') },
handler: async (ctx, { projectId }) => {
const project = await ctx.db.get(projectId);

if (!project || project.deletedAt !== null) {
throw new ConvexError({
message: 'Project not found',
code: 404,
});
}

const details = await ctx.db
.query('details')
.withIndex('project', (q) => q.eq('projectId', projectId))
.filter((q) => q.eq(q.field('deletedAt'), null))
.unique();

if (!details) {
throw new ConvexError({
message: 'Project details not found',
code: 404,
});
}

const { title } = project;
const { coverImageId, embedId } = details;
const coverImageUrl: string | null = coverImageId
? await ctx.storage.getUrl(coverImageId)
: null;
const embed: Doc<'embeds'> | null = embedId
? await ctx.db.get(embedId)
: null;

return {
...details,
coverImage: coverImageUrl
? {
alt: title,
url: coverImageUrl,
}
: null,
embed: embed
? {
...embed,
title,
}
: null,
};
},
});

const services = v.union(
v.literal('bandcamp'),
v.literal('youtube'),
v.literal('soundcloud'),
);

const getOrInsertProjectDetails = async (
ctx: MutationCtx,
projectId: Id<'projects'>,
) => {
let details = await ctx.db
.query('details')
.withIndex('project', (q) => q.eq('projectId', projectId))
.filter((q) => q.eq(q.field('deletedAt'), null))
.unique();

if (!details) {
const insertedId = await ctx.db.insert('details', {
content: null,
coverImageId: null,
deletedAt: null,
embedId: null,
projectId,
});
details = await ctx.db.get(insertedId);
}

if (!details) {
throw new ConvexError({
message: 'Details not found',
code: 500,
});
}

return details;
};

export const attachProjectEmbed = internalMutation({
args: {
projectId: v.id('projects'),
service: services,
src: v.string(),
},
handler: async (ctx, { projectId, service, src }) => {
const project = await ctx.db.get(projectId);

if (!project) {
throw new ConvexError({
message: 'Project not found',
code: 500,
});
}

const existingDetails = await getOrInsertProjectDetails(ctx, projectId);
const existingEmbed = existingDetails?.embedId
? await ctx.db.get(existingDetails?.embedId)
: null;

if (existingEmbed) {
await ctx.db.delete(existingEmbed._id);
}

const embedId = await ctx.db.insert('embeds', {
deletedAt: null,
service,
src,
});

return await ctx.db.patch(existingDetails._id, {
embedId,
});
},
});

export const attachProjectCoverImage = internalMutation({
args: {
coverImageId: v.id('_storage'),
projectId: v.id('projects'),
},
handler: async (ctx, { coverImageId, projectId }) => {
const project = await ctx.db.get(projectId);

if (!project) {
throw new ConvexError({
message: 'Project not found',
code: 500,
});
}

const existingCoverImage = await ctx.storage.getUrl(coverImageId);

if (!existingCoverImage) {
throw new ConvexError({
message: 'Cover image does not exist',
code: 500,
});
}

const existingDetails = await getOrInsertProjectDetails(ctx, projectId);

return await ctx.db.patch(existingDetails._id, {
coverImageId,
});
},
});
2 changes: 1 addition & 1 deletion convex/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const loadFeatureFlags = query({
export const populateFeatureFlags = internalMutation({
args: {},
handler: async (ctx) => {
const initialFlags = ['isProjectPreviewsEnabled'];
const initialFlags = ['isProjectPreviewsEnabled', 'isProjectViewerEnabled'];
const existingFeatures = await ctx.db.query('features').collect();
const existingKeys = new Set(existingFeatures.map((f) => f.key));
const res: string[] = [];
Expand Down
32 changes: 31 additions & 1 deletion convex/projects.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { paginationOptsValidator } from 'convex/server';
import { v } from 'convex/values';
import { ConvexError, v } from 'convex/values';
import { type Id } from './_generated/dataModel';
import { internalMutation, query } from './_generated/server';

Expand All @@ -17,13 +17,43 @@ export const loadProjects = query({
},
});

export const loadProjectIds = query({
args: {},
handler: async (ctx) => {
const projects = await ctx.db
.query('projects')
.withIndex('deletedByOrder', (q) => q.eq('deletedAt', null))
.filter((q) => q.neq(q.field('publishedAt'), null))
.order('asc')
.collect();

return projects?.map((p) => p._id);
},
});

export const loadAllProjects = query({
args: {},
handler: async (ctx) => {
return await ctx.db.query('projects').order('asc').collect();
},
});

export const loadProject = query({
args: { projectId: v.id('projects') },
handler: async (ctx, { projectId }) => {
const project = await ctx.db.get(projectId);

if (!project || project.deletedAt !== null) {
throw new ConvexError({
message: 'Project not found',
code: 404,
});
}

return project;
},
});

export const reorderProjects = internalMutation({
args: {
id: v.id('projects'),
Expand Down
22 changes: 22 additions & 0 deletions convex/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@ const projects = defineTable({
url: v.string(),
}).index('deletedByOrder', ['deletedAt', 'order']);

const details = defineTable({
content: v.union(v.string(), v.null()),
coverImageId: v.union(v.id('_storage'), v.null()),
deletedAt: v.union(v.number(), v.null()),
embedId: v.union(v.id('embeds'), v.null()),
projectId: v.id('projects'),
}).index('project', ['projectId']);

const services = v.union(
v.literal('bandcamp'),
v.literal('youtube'),
v.literal('soundcloud'),
);

const embeds = defineTable({
deletedAt: v.union(v.number(), v.null()),
service: services,
src: v.string(),
});

const previews = defineTable({
deletedAt: v.union(v.number(), v.null()),
projectId: v.id('projects'),
Expand All @@ -30,6 +50,8 @@ export const features = defineTable({
});

export default defineSchema({
details,
embeds,
features,
previews,
projects,
Expand Down
Loading

0 comments on commit 2a67cb9

Please sign in to comment.