Skip to content

Commit

Permalink
Merge pull request #3149 from quantified-uncertainty/export-page-roll…
Browse files Browse the repository at this point in the history
…back-2

Export page versions
  • Loading branch information
berekuk authored Apr 6, 2024
2 parents f03a1fa + 0cf2d8b commit e058f4b
Show file tree
Hide file tree
Showing 10 changed files with 300 additions and 47 deletions.
27 changes: 27 additions & 0 deletions packages/hub/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ enum MembershipRole {
type Model implements Node {
createdAtTimestamp: Float!
currentRevision: ModelRevision!
exportRevisions(after: String, before: String, first: Int, last: Int, variableId: String!): ModelExportRevisionsConnection!
id: ID!
isEditable: Boolean!
isPrivate: Boolean!
Expand Down Expand Up @@ -183,6 +184,31 @@ type ModelExport implements Node {
variableType: String!
}

type ModelExportConnection {
edges: [ModelExportEdge!]!
pageInfo: PageInfo!
}

type ModelExportEdge {
cursor: String!
node: ModelExport!
}

input ModelExportQueryInput {
modelId: String
variableName: String
}

type ModelExportRevisionsConnection {
edges: [ModelExportRevisionsConnectionEdge!]!
pageInfo: PageInfo!
}

type ModelExportRevisionsConnectionEdge {
cursor: String!
node: ModelExport!
}

type ModelRevision implements Node {
author: User
comment: String!
Expand Down Expand Up @@ -494,6 +520,7 @@ type Query {
groups(after: String, before: String, first: Int, input: GroupsQueryInput, last: Int): GroupConnection!
me: Me!
model(input: QueryModelInput!): QueryModelResult!
modelExports(after: String, before: String, first: Int, input: ModelExportQueryInput, last: Int): ModelExportConnection!
models(after: String, before: String, first: Int, last: Int): ModelConnection!

"""Admin-only query for listing models in /admin UI"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,57 @@
"use client";
import { FC } from "react";
import { graphql } from "react-relay";
import clsx from "clsx";
import { format } from "date-fns";
import { FC, useState } from "react";
import { graphql, usePaginationFragment } from "react-relay";

import { LoadMore } from "@/components/LoadMore";
import { extractFromGraphqlErrorUnion } from "@/lib/graphqlHelpers";
import { SerializablePreloadedQuery } from "@/relay/loadPageQuery";
import { usePageQuery } from "@/relay/usePageQuery";

import { SquiggleModelExportPage } from "./SquiggleModelExportPage";

import {
ModelExportPage$data,
ModelExportPage$key,
} from "@/__generated__/ModelExportPage.graphql";
import { ModelExportPageQuery } from "@/__generated__/ModelExportPageQuery.graphql";

type ExportRevisions = ModelExportPage$data["exportRevisions"];

const RevisionsPanel: FC<{
exportRevisions: ExportRevisions;
selected: string;
changeId: (id: string) => void;
loadNext?: (count: number) => void;
}> = ({ exportRevisions, selected, changeId, loadNext }) => {
return (
<div className="w-[150px] ml-4 bg-gray-50 rounded-sm py-2 px-3 flex flex-col">
<h3 className="text-sm font-medium text-gray-700 border-b mb-1 pb-0.5">
Revisions
</h3>
<ul>
{exportRevisions.edges.map(({ node: revision }) => (
<li
key={revision.id}
onClick={() => changeId(revision.id)}
className={clsx(
"hover:text-gray-800 cursor-pointer hover:underline text-sm pt-0.5 pb-0.5",
revision.id === selected ? "text-blue-900" : "text-gray-400"
)}
>
{format(
new Date(revision.modelRevision.createdAtTimestamp),
"MMM dd, yyyy"
)}
</li>
))}
</ul>
{loadNext && <LoadMore loadNext={loadNext} size="small" />}
</div>
);
};

export const ModelExportPage: FC<{
params: {
owner: string;
Expand All @@ -20,39 +62,98 @@ export const ModelExportPage: FC<{
}> = ({ query, params }) => {
const [{ model: result }] = usePageQuery(
graphql`
query ModelExportPageQuery($input: QueryModelInput!) {
query ModelExportPageQuery(
$input: QueryModelInput!
$variableName: String!
) {
model(input: $input) {
__typename
... on Model {
id
slug
currentRevision {
...ModelExportPage @arguments(variableName: $variableName)
}
}
}
`,
query
);

const model = extractFromGraphqlErrorUnion(result, "Model");

const {
data: { exportRevisions },
loadNext,
} = usePaginationFragment<ModelExportPageQuery, ModelExportPage$key>(
graphql`
fragment ModelExportPage on Model
@argumentDefinitions(
cursor: { type: "String" }
count: { type: "Int", defaultValue: 20 }
variableName: { type: "String!" }
)
@refetchable(queryName: "ModelExportPagePaginationQuery") {
exportRevisions(
first: $count
after: $cursor
variableId: $variableName
) @connection(key: "ModelExportPage_exportRevisions") {
edges {
node {
id
content {
__typename
...SquiggleModelExportPage
variableName
modelRevision {
id
createdAtTimestamp
content {
__typename
...SquiggleModelExportPage
}
}
}
}
pageInfo {
hasNextPage
}
}
}
`,
query
model
);

const model = extractFromGraphqlErrorUnion(result, "Model");
const content = model.currentRevision.content;

switch (content.__typename) {
case "SquiggleSnippet": {
return (
<SquiggleModelExportPage
variableName={params.variableName}
contentRef={content}
/>
);
const [selected, changeId] = useState<string>(
exportRevisions.edges.at(0)?.node.id || ""
);

const content = exportRevisions.edges.find(
(edge) => edge.node.id === selected
)?.node.modelRevision.content;

if (content) {
switch (content.__typename) {
case "SquiggleSnippet": {
return (
<div className="flex">
<div className="flex-1 w-full">
<SquiggleModelExportPage
key={selected}
variableName={params.variableName}
contentRef={content}
/>
</div>
<RevisionsPanel
exportRevisions={exportRevisions}
selected={selected}
changeId={changeId}
loadNext={
exportRevisions.pageInfo.hasNextPage ? loadNext : undefined
}
/>
</div>
);
}
default:
return <div>Unknown model type {content.__typename}</div>;
}
default:
return <div>Unknown model type {content.__typename}</div>;
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ type Props = {
export default async function OuterModelExportPage({ params }: Props) {
const query = await loadPageQuery<ModelExportPageQuery>(QueryNode, {
input: { owner: params.owner, slug: params.slug },
variableName: params.variableName,
});

return (
Expand Down
9 changes: 7 additions & 2 deletions packages/hub/src/components/LoadMore.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ import { FC } from "react";

import { Button } from "@quri/ui";

import { ButtonProps } from "../../../ui/dist/components/Button";

type Props = {
loadNext: (count: number) => void;
size?: ButtonProps["size"];
};

export const LoadMore: FC<Props> = ({ loadNext }) => {
export const LoadMore: FC<Props> = ({ loadNext, size }) => {
return (
<div className="mt-4">
<Button onClick={() => loadNext(20)}>Load more</Button>
<Button size={size} onClick={() => loadNext(20)}>
Load more
</Button>
</div>
);
};
51 changes: 51 additions & 0 deletions packages/hub/src/graphql/queries/exports.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { builder } from "@/graphql/builder";
import { prisma } from "@/prisma";

import { ModelExport, ModelExportConnection } from "../types/ModelExport";

const ModelExportQueryInput = builder.inputType("ModelExportQueryInput", {
fields: (t) => ({
modelId: t.string(), // Add this field to filter by model ID
variableName: t.string(),
}),
});

builder.queryField("modelExports", (t) =>
t.prismaConnection(
{
type: ModelExport,
cursor: "id",
args: {
input: t.arg({ type: ModelExportQueryInput }),
},
resolve: (query, _, { input }, { session }) => {
const modelId = input?.modelId;
if (!modelId) {
return [];
}
return prisma.modelExport.findMany({
...query,
where: {
...(input.modelId && {
modelRevision: {
modelId: modelId,
},
}),
...(input.variableName && {
variableName: input.variableName,
}),
},
orderBy: {
modelRevision: {
createdAt: "desc",
},
},
include: {
modelRevision: true,
},
});
},
},
ModelExportConnection
)
);
1 change: 1 addition & 0 deletions packages/hub/src/graphql/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "./queries/group";
import "./queries/groups";
import "./queries/me";
import "./queries/model";
import "./queries/exports";
import "./queries/models";
import "./queries/modelsByVersion";
import "./queries/relativeValuesDefinition";
Expand Down
46 changes: 46 additions & 0 deletions packages/hub/src/graphql/types/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { prismaConnectionHelpers } from "@pothos/plugin-prisma";
import { builder } from "@/graphql/builder";

import { decodeGlobalIdWithTypename } from "../utils";
import { ModelExport } from "./ModelExport";
import { ModelRevision, ModelRevisionConnection } from "./ModelRevision";
import { Owner } from "./Owner";

Expand Down Expand Up @@ -62,6 +63,7 @@ export const Model = builder.prismaNode("Model", {
currentRevision: t.relation("currentRevision", {
nullable: false,
}),

revision: t.field({
type: ModelRevision,
args: {
Expand Down Expand Up @@ -97,6 +99,19 @@ export const Model = builder.prismaNode("Model", {
},
ModelRevisionConnection
),
exportRevisions: t.connection({
type: ModelExport,
args: exportRevisionConnectionHelpers.getArgs(),
select: (args, ctx, nestedSelection) => ({
revisions: exportRevisionConnectionHelpers.getQuery(
args,
ctx,
nestedSelection
),
}),
resolve: (model, args, ctx) =>
exportRevisionConnectionHelpers.resolve(model.revisions, args, ctx),
}),
}),
});

Expand All @@ -110,3 +125,34 @@ export const modelConnectionHelpers = prismaConnectionHelpers(
"Model",
{ cursor: "id" }
);

const exportRevisionConnectionHelpers = prismaConnectionHelpers(
builder,
"ModelRevision",
{
cursor: "id",
args: (t) => ({
variableId: t.string({ required: true }),
}),
select: (nodeSelection, args) => ({
exports: nodeSelection({
where: {
variableName: args.variableId,
},
}),
}),
query: (args) => ({
where: {
exports: {
some: {
variableName: args.variableId,
},
},
},
orderBy: {
createdAt: "desc" as const,
},
}),
resolveNode: (revision) => revision.exports[0],
}
);
Loading

0 comments on commit e058f4b

Please sign in to comment.