Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/features/grammar-point/loader/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export class GrammarPointsLoaderError extends Error {
constructor(
message: string,
public code?: number,
) {
super(message);
this.name = "GrammarPointsLoaderError";
}
}
export class GrammarPointNotFoundError extends GrammarPointsLoaderError {
constructor(message: string) {
super(message);
this.name = "GrammarPointNotFoundError";
this.code = 404;
}
}
82 changes: 82 additions & 0 deletions src/features/grammar-point/loader/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import type { LiveLoader } from "astro/loaders";
import {
type Context,
type GrammarPoint,
fetchAllGrammarPoints,
fetchGrammarPoint,
fetchGrammarPoints,
} from "grammar-sdk";
import logger from "libs/logger";
import { GrammarPointNotFoundError, GrammarPointsLoaderError } from "./errors";

const grammarPointsToEntries = (grammarPoints: GrammarPoint[]) => ({
entries: grammarPoints.map(grammarPointToEntry),
});

const grammarPointToEntry = (grammarPoint: GrammarPoint) => ({
id: grammarPoint.id,
data: grammarPoint,
cacheHint: {
tags: [`grammar-point-${grammarPoint.id}`],
},
});

export function grammarPointsLoader(): LiveLoader<
GrammarPoint,
{ id: string; context: Context },
{ ids: string[]; context: Context } | { context: Context }
> {
return {
name: "grammarPoints",
loadCollection: async ({ filter }) => {
try {
if (filter && "ids" in filter) {
return {
...grammarPointsToEntries(
await fetchGrammarPoints(filter.ids, filter.context),
),
cacheHint: {
tags: [
`grammar-points-${filter.context.user.role}-${filter.ids.join(",")}`,
],
},
};
}

const context = filter?.context ?? { user: { role: "guest" } };

return {
...grammarPointsToEntries(await fetchAllGrammarPoints(context)),
cacheHint: {
tags: [`grammar-points-${context.user.role}`],
},
};
} catch (error) {
const message = "Failed to load grammar points";
logger.error(error, message);
return {
error: new GrammarPointsLoaderError(message, 500),
};
}
},
loadEntry: async ({ filter: { id, context } }) => {
try {
const entry = await fetchGrammarPoint(id, context);
if (!entry) {
return {
error: new GrammarPointNotFoundError(
`Grammar point with id ${id} not found`,
),
};
}
return grammarPointToEntry(entry);
} catch (error) {
const message = `Failed to load grammar point with id ${id}`;
logger.error(error, message);
return {
error: new GrammarPointsLoaderError(message, 500),
};
}
},
};
}
8 changes: 8 additions & 0 deletions src/live.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { defineLiveCollection } from "astro:content";
import { grammarPointsLoader } from "./features/grammar-point/loader";

const grammarPoints = defineLiveCollection({
loader: grammarPointsLoader(),
});

export const collections = { grammarPoints };
2 changes: 2 additions & 0 deletions src/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { isUserAdmin } from "libs/auth/admin";

const PATHS_TO_IGNORE = [
"signin",
"signup",
"register",
"auth",
"login",
Expand All @@ -12,6 +13,7 @@ const PATHS_TO_IGNORE = [
"help",
"privacy-policy",
"sitemap.xml",
"error",
];

export const onRequest = defineMiddleware(
Expand Down
14 changes: 14 additions & 0 deletions src/pages/error.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
import Layout from "~/layouts/Layout.astro";
---

<Layout title="Error">
<div class="grid place-items-center w-full h-full">
<div class="flex flex-col items-center gap-4">
<p class="text-xl text-secondary">
We're experiencing technical difficulties. Please try again later.
</p>
<a href="/" class="text-blue-500 hover:underline">Go back to home</a>
</div>
</div>
</Layout>
25 changes: 21 additions & 4 deletions src/pages/grammar/[id]/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,41 @@
import { GET as fetchGpInReview } from "@api/sr/review/list";
import { GrammarPoint } from "@components/solid/grammar-point/GrammarPoint";
import { ReviewToggleButton } from "@components/solid/grammar-point/ReviewToggleButton";
import { fetchGrammarPoint } from "grammar-sdk";
import { Button } from "packages/ui/button";
import { Anchor } from "ui/anchor";
import Layout from "../../../layouts/Layout.astro";
import { contextFromAstro } from "~/libs/context";
import { getLiveEntry } from "astro:content";

const { id } = Astro.params;
const backTo = Astro.url.searchParams.get("backTo") ?? undefined;

const grammarPoint = id
? await fetchGrammarPoint(id, contextFromAstro(Astro))
: undefined;
if (!id || isNaN(Number(id))) {
return Astro.redirect("/grammar");
}

const { entry: grammarPointEntry, error: grammarPointError } =
await getLiveEntry("grammarPoints", {
id,
context: contextFromAstro(Astro),
});

if (grammarPointError) {
return Astro.redirect("/error");
}

if (!grammarPointEntry) {
return Astro.redirect("/grammar");
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

const inReview = id
? ((
(await (await fetchGpInReview(Astro)).json()) ?? ([] as string[])
)?.includes?.(id) ?? false)
: false;
const user = Astro.locals.user;

const grammarPoint = grammarPointEntry.data;
---

<Layout title="Grammar">
Expand Down
19 changes: 16 additions & 3 deletions src/pages/grammar/[id]/practice.astro
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
---
import { fetchGrammarPoints } from "grammar-sdk";
import { simpleShuffle } from "utils/array";
import { Exercises } from "../../../components/solid/Exercise/Exercises";
import Layout from "../../../layouts/Layout.astro";
import { contextFromAstro } from "~/libs/context";
import { getLiveCollection } from "astro:content";

const ids = Astro.params.id?.split("-") ?? [];

const grammarPoints = await fetchGrammarPoints(ids, contextFromAstro(Astro));
const exercises = simpleShuffle(grammarPoints.flatMap((g) => g.exercises));
const { entries: grammarPoints, error } = await getLiveCollection(
"grammarPoints",
{
ids,
context: contextFromAstro(Astro),
},
);

if (error) {
return Astro.redirect("/error");
}

const exercises = simpleShuffle(
(grammarPoints ?? []).flatMap((g) => g.data.exercises),
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
---

<Layout title="Practice">
Expand Down
18 changes: 15 additions & 3 deletions src/pages/grammar/index.astro
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
---
import { GET as fetchGpInReview } from "@api/sr/review/list";
import { fetchAllGrammarPoints } from "grammar-sdk";
import { Grammar } from "../../components/solid/grammar/Grammar";
import Layout from "../../layouts/Layout.astro";
import { contextFromAstro } from "~/libs/context";
import { getLiveCollection } from "astro:content";

const { entries, error } = await getLiveCollection("grammarPoints", {
context: contextFromAstro(Astro),
});

if (error) {
return Astro.redirect("/error");
}

const grammar = await fetchAllGrammarPoints(contextFromAstro(Astro));
const inReview = (await (await fetchGpInReview(Astro)).json()) as string[];
const maybeMode = Astro.url.searchParams.get("mode");
const mode = maybeMode === "cram" ? "cram" : "nav";
---

<Layout title="Grammar" topPadding={false} transition:persist>
<Grammar client:load grammar={grammar} inReview={inReview} mode={mode} />
<Grammar
client:load
grammar={entries?.map((entry) => entry.data) ?? []}
inReview={inReview}
mode={mode}
/>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
</Layout>
12 changes: 9 additions & 3 deletions src/pages/grammar/practice/result.astro
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
---
import { SessionResult } from "@components/solid/Exercise/SessionResult";
import Layout from "@layouts/Layout.astro";
import { fetchAllGrammarPoints } from "grammar-sdk";
import { getLiveCollection } from "astro:content";
import { contextFromAstro } from "~/libs/context";

const grammar = await fetchAllGrammarPoints(contextFromAstro(Astro));
const { entries: grammar, error } = await getLiveCollection("grammarPoints", {
context: contextFromAstro(Astro),
});

if (error) {
return Astro.redirect("/error");
}
---

<Layout title="Practice results">
<SessionResult
client:only="solid-js"
sessionResult={undefined}
grammar={grammar}
grammar={grammar?.map((entry) => entry.data) ?? []}
/>
</Layout>
15 changes: 12 additions & 3 deletions src/pages/sr/review/[sessionId]/result.astro
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,30 @@
import { SessionResult } from "@components/solid/Exercise/SessionResult";
import Layout from "@layouts/Layout.astro";
import { actions } from "astro:actions";
import { fetchAllGrammarPoints } from "grammar-sdk";
import { getLiveCollection } from "astro:content";
import { contextFromAstro } from "~/libs/context";

const sessionResult = Astro.params.sessionId
? await Astro.callAction(actions.sessionResult, {
sessionId: Astro.params.sessionId,
})
: undefined;
const grammar = await fetchAllGrammarPoints(contextFromAstro(Astro));
const { entries: grammar, error } = await getLiveCollection("grammarPoints", {
context: contextFromAstro(Astro),
});

if (error) {
return Astro.redirect("/error");
}
---

<Layout title="Review results">
{
sessionResult?.data ? (
<SessionResult sessionResult={sessionResult.data} grammar={grammar} />
<SessionResult
sessionResult={sessionResult.data}
grammar={grammar?.map((entry) => entry.data) ?? []}
/>
) : (
<div>
No session result found, probably it hasn't been calculated yet. Please
Expand Down
Loading