Skip to content

Commit

Permalink
we bloggin now
Browse files Browse the repository at this point in the history
  • Loading branch information
enkoder committed Jan 12, 2025
1 parent 543f342 commit ec03775
Show file tree
Hide file tree
Showing 24 changed files with 2,029 additions and 26 deletions.
2 changes: 2 additions & 0 deletions api/migrations/0010_blog.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Migration number: 0010 2025-01-12T07:37:01.623Z
ALTER TABLE users ADD COLUMN oldest_blog_post_date TEXT;
2 changes: 2 additions & 0 deletions api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import {
GetUsers,
Me,
PatchMe,
ViewBlogPost,
} from "./routes/users.js";
import type { Database } from "./schema.js";
import type { Env } from "./types.js";
Expand Down Expand Up @@ -88,6 +89,7 @@ router

.get("/users/@me", authenticatedUser, Me)
.patch("/users/@me", authenticatedUser, PatchMe)
.post("/users/@me/view-blog", ViewBlogPost)
.get("/users", GetUsers)
.get("/users/:userID", GetUser)
.get("/users/:user/results", GetUserResults)
Expand Down
22 changes: 22 additions & 0 deletions api/src/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export const UserComponent = z
is_admin: z.coerce.boolean({
description: "Flag indicating that the user is an Admin user",
}),
oldest_blog_post_date: z
.string({ description: "Date of oldest viewed blog post page" })
.date()
.nullable(),
})
.openapi("User");
export type UserComponentType = z.infer<typeof UserComponent>;
Expand Down Expand Up @@ -260,6 +264,24 @@ export const PatchMeSchema = {
},
};

export const ViewBlogPostBody = z.object({
blogDate: z.string(),
});
export type ViewBlogPostBodyType = z.infer<typeof ViewBlogPostBody>;

export const ViewBlogPostSchema = {
tags: ["Blog"],
summary: "Views a blog post",
security: [{ bearerAuth: [] }],
requestBody: ViewBlogPostBody,
responses: {
"200": {
schema: z.object({}),
description: "Empty object indicates success on viewing blog post",
},
},
};

export const GetOAuthLoginURLSchema = {
tags: ["Auth"],
summary: "Fetches the NRDB login url",
Expand Down
29 changes: 29 additions & 0 deletions api/src/routes/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import {
type UpdateUserComponentType,
UserComponent,
UserResultsResponseComponent,
type ViewBlogPostBodyType,
ViewBlogPostSchema,
} from "../openapi.js";
import type { FactionCode, Format } from "../schema.js";
import type { Env, RequestWithDB } from "../types.d.js";
Expand All @@ -36,6 +38,33 @@ export class GetUser extends OpenAPIRoute {
}
}

export class ViewBlogPost extends OpenAPIRoute {
static schema = ViewBlogPostSchema;

@traceDeco("ViewBlogPost")
async handle(req: RequestWithDB, env: Env, ctx: ExecutionContext, data) {
const body = data.body as ViewBlogPostBodyType;
if (!body.blogDate) {
return errorResponse(400, "Blog date is required");
}

const user = await Users.getById(Number(req.user_id));
if (!user) {
return error(500, "No users in table??");
}

if (
!user.oldest_blog_post_date ||
user.oldest_blog_post_date < body.blogDate
) {
await Users.update(user.id, {
oldest_blog_post_date: body.blogDate,
});
}

return json({});
}
}
export class GetUsers extends OpenAPIRoute {
static schema = GetUsersSchema;

Expand Down
1 change: 1 addition & 0 deletions api/src/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ export interface UsersTable {
password: string | null;
is_admin: number;
disabled: number;
oldest_blog_post_date: string | null;
}

export type User = Selectable<UsersTable>;
Expand Down
1 change: 1 addition & 0 deletions app/src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type { UserResultsResponse } from './models/UserResultsResponse';
export { AdminService } from './services/AdminService';
export { AssetsService } from './services/AssetsService';
export { AuthService } from './services/AuthService';
export { BlogService } from './services/BlogService';
export { LeaderboardService } from './services/LeaderboardService';
export { ResultsService } from './services/ResultsService';
export { SeasonsService } from './services/SeasonsService';
Expand Down
4 changes: 4 additions & 0 deletions app/src/client/models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,9 @@ export type User = {
* Flag indicating that the user is an Admin user
*/
is_admin?: boolean | null;
/**
* Date of oldest viewed blog post page
*/
oldest_blog_post_date: string | null;
};

30 changes: 30 additions & 0 deletions app/src/client/services/BlogService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/* generated using openapi-typescript-codegen -- do no edit */
/* istanbul ignore file */
/* tslint:disable */
/* eslint-disable */
import type { CancelablePromise } from '../core/CancelablePromise';
import { OpenAPI } from '../core/OpenAPI';
import { request as __request } from '../core/request';

export class BlogService {

/**
* Views a blog post
* @param requestBody
* @returns any Empty object indicates success on viewing blog post
* @throws ApiError
*/
public static postViewBlogPost(
requestBody?: {
blogDate: string;
},
): CancelablePromise<any> {
return __request(OpenAPI, {
method: 'POST',
url: '/api/users/@me/view-blog',
body: requestBody,
mediaType: 'application/json',
});
}

}
6 changes: 3 additions & 3 deletions app/src/components/Anchor.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HashtagIcon } from "@heroicons/react/24/solid";
import clsx from "clsx";
import type { ReactNode } from "react";
import { useNavigate } from "react-router-dom";
import { twMerge } from "tailwind-merge";

type AnchorProps = {
id: string;
Expand All @@ -12,9 +12,9 @@ type AnchorProps = {
export function Anchor({ id, className, children }: AnchorProps): JSX.Element {
const navigate = useNavigate();
return (
<div className={twMerge(className, "flex flex-row items-center")}>
<div className={clsx(className, "flex flex-row items-center")}>
<HashtagIcon
className={"cursor mr-2 h-6 w-6 text-gray-300 text-sm"}
className={clsx(className, "cursor mr-2 h-6 w-6 text-gray-300 text-sm")}
onClick={() => navigate(`#${id}`)}
/>
<h6 id={id} className={"text-2xl text-gray-300"}>
Expand Down
55 changes: 55 additions & 0 deletions app/src/components/BlogAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { BellIcon } from "@heroicons/react/24/outline";
import moment from "moment";
import { useNavigate } from "react-router-dom";
import { posts } from "../routes/Blog/posts";
import useAuth from "../useAuth";
import { Tooltip, TooltipContent, TooltipTrigger } from "./Tooltip";

export function BlogAlert() {
const { user } = useAuth();
const navigate = useNavigate();

// Get the most recent blog post date
const latestPost = posts
.filter((post) => post.showInList)
.sort((a, b) => moment(b.date).diff(moment(a.date)))[0];

const hasNewPost = Boolean(
user &&
(!user.oldest_blog_post_date ||
moment(latestPost?.date).isAfter(moment(user.oldest_blog_post_date))),
);

const button = (
<button
type="button"
onClick={() => navigate(`/blog/${latestPost?.id}`)}
className="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none"
>
<span className="-inset-1.5 absolute" />
<span className="sr-only">View blog updates</span>
<BellIcon className="h-6 w-6" aria-hidden="true" />
{hasNewPost && (
<span className="absolute top-0 right-0 block h-2 w-2 rounded-full bg-red-600 ring-1 ring-gray-400" />
)}
</button>
);

if (!user) {
return (
<Tooltip>
<TooltipTrigger asChild>{button}</TooltipTrigger>
<TooltipContent
className="mt-2 rounded-lg border border-gray-600 bg-gray-950 p-2 text-cyan-500 text-sm shadow-lg"
arrowClassName={
"fill-gray-950 [&>path:first-of-type]:stroke-gray-600"
}
>
Log in to get notified when new blog posts are published
</TooltipContent>
</Tooltip>
);
}

return button;
}
23 changes: 8 additions & 15 deletions app/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { Link, useLocation, useNavigate } from "react-router-dom";
import greenBeans from "../../assets/ai_beanstalk_royalties.jpeg";
import { AuthService } from "../client";
import useAuth from "../useAuth";
import { BlogAlert } from "./BlogAlert";

export type MenuItem = {
name: string;
Expand Down Expand Up @@ -42,10 +43,8 @@ export function Navbar() {
{ name: "Leaderboard", to: "/", isCurrent: true },
//{ name: "Seasons", to: "/seasons", isCurrent: false },
{ name: "Tags", to: "/tags", isCurrent: false },
{ name: "Beans", to: "/beans", isCurrent: false },
{ name: "Code", to: "/code", isCurrent: false },
{ name: "Sim", to: "/sim", isCurrent: false },
{ name: "FAQ", to: "/faq", isCurrent: false },
{ name: "Blog", to: "/blog", isCurrent: false },
];

const menu: MenuItem[] = [
Expand Down Expand Up @@ -77,7 +76,11 @@ export function Navbar() {

useEffect(() => {
for (const nav of navigation) {
if (location.pathname === nav.to) {
if (location.pathname.startsWith(nav.to) && nav.to !== "/") {
setCurrent(nav.to);
return;
}
if (nav.to === "/" && location.pathname === "/") {
setCurrent(nav.to);
}
}
Expand Down Expand Up @@ -138,17 +141,7 @@ export function Navbar() {
</div>
</div>
<div className="absolute inset-y-0 right-0 flex items-center pr-2 md:static md:inset-auto md:ml-6 md:pr-0">
{/* Bell icon, use this later once there is a blog
<button
type="button"
className="relative rounded-full bg-gray-800 p-1 text-gray-400 hover:text-white focus:outline-none focus:ring-2 focus:ring-white focus:ring-offset-2 focus:ring-offset-gray-800"
>
<span className="absolute -inset-1.5" />
<span className="sr-only">View notifications</span>
<BellIcon className="h-6 w-6" aria-hidden="true" />
</button>
*/}

<BlogAlert />
{/* Profile dropdown */}
<Menu as="div" className="relative ml-3">
<div>
Expand Down
14 changes: 8 additions & 6 deletions app/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@ import { OpenAPI } from "./client";
import { Navbar } from "./components/Navbar";
import { PageHeading } from "./components/PageHeader";
import { Stars } from "./components/Stars";
import { Beans } from "./routes/Beans";
import { Code } from "./routes/Code";
import { Faq } from "./routes/Faq";
import { BlogPage } from "./routes/Blog";
import { Sim } from "./routes/Sim";
import { TagsPage } from "./routes/TagsPage";
import { TournamentPage } from "./routes/TournamentPage";
Expand Down Expand Up @@ -143,13 +141,17 @@ const router = createBrowserRouter([
errorElement: <ErrorPage />,
children: [
{ index: true, element: <Leaderboard /> },
{ path: "/faq", element: <Faq /> },
{
path: "/blog",
children: [
{ index: true, element: <BlogPage /> },
{ path: ":postId", element: <BlogPage /> },
],
},
{ path: "/results/:user", element: <Results /> },
{ path: "/tournament/:tournament", element: <TournamentPage /> },
{ path: "/oauth/callback", element: <OAuth2Callback /> },
{ path: "/beans", element: <Beans /> },
{ path: "/sim", element: <Sim /> },
{ path: "/code", element: <Code /> },
//{ path: "/seasons", element: <Seasons /> },
{ path: "/tags", element: <TagsPage /> },
{ path: "/@me", element: <Profile /> },
Expand Down
Loading

0 comments on commit ec03775

Please sign in to comment.