diff --git a/craco.config.js b/craco.config.js index 74f2e4c..5801b38 100644 --- a/craco.config.js +++ b/craco.config.js @@ -9,6 +9,7 @@ const WebpackBar = require("webpackbar"); const GitRevisionPlugin = require("git-revision-webpack-plugin"); const { DefinePlugin } = require("webpack"); +const { commits } = require("./tools/commitLog"); const gitRevisionPlugin = new GitRevisionPlugin({ // Include the "-dirty" suffix if the local tree has been modified, and @@ -77,7 +78,8 @@ new DefinePlugin({ "__GIT_VERSION__": JSON.stringify(gitRevisionPlugin.version()), "__GIT_COMMIT_HASH__": JSON.stringify(gitRevisionPlugin.commithash()), - "__BUILD_TIME__": DefinePlugin.runtimeValue(Date.now) + "__BUILD_TIME__": DefinePlugin.runtimeValue(Date.now), + "__GIT_COMMITS__": JSON.stringify(commits) }) ], diff --git a/package.json b/package.json index 81ae191..69fb91f 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "json5": "^2.2.0", "lodash-es": "^4.17.21", "lru-cache": "^6.0.0", + "markdown-to-jsx": "^7.1.2", "rc-menu": "^8.10.6", "react": "^17.0.1", "react-chartjs-2": "^2.11.1", @@ -112,6 +113,7 @@ "eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-tsdoc": "^0.2.11", "git-revision-webpack-plugin": "^3.0.6", + "gitlog": "^4.0.4", "less": "4.1.1", "less-loader": "7.3.0", "react-refresh": "^0.9.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9ee79b2..3322b3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,6 +19,7 @@ json5: 2.2.0 lodash-es: 4.17.21 lru-cache: 6.0.0 + markdown-to-jsx: 7.1.2_react@17.0.1 rc-menu: 8.10.6_react-dom@17.0.1+react@17.0.1 react: 17.0.1 react-chartjs-2: 2.11.1_6c446a34f83b2a92e3214f8b711c141a @@ -71,6 +72,7 @@ eslint-plugin-react-hooks: 4.2.0_eslint@7.21.0 eslint-plugin-tsdoc: 0.2.11 git-revision-webpack-plugin: 3.0.6 + gitlog: 4.0.4 less: 4.1.1 less-loader: 7.3.0_less@4.1.1 react-refresh: 0.9.0 @@ -6159,6 +6161,15 @@ node: '>=6.11.5' resolution: integrity: sha512-vW/9dBahGbpKPcccy3xKkHgdWoH/cAPPc3lQw+3edl7b4j29JfNGVrja0a1d8ZoRe4nTN8GCPrF9aBErDnzx5Q== + /gitlog/4.0.4: + dependencies: + debug: 4.3.1 + tslib: 1.14.1 + dev: true + engines: + node: '>= 10.x' + resolution: + integrity: sha512-jeY2kO7CVyTa6cUM7ZD2ZxIyBkna1xvW2esV/3o8tbhiUneX1UBQCH4D9aMrHgGiohBjyXbuZogyjKXslnY5Yg== /glob-parent/3.1.0: dependencies: is-glob: 3.1.0 @@ -8234,6 +8245,16 @@ node: '>=0.10.0' resolution: integrity: sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= + /markdown-to-jsx/7.1.2_react@17.0.1: + dependencies: + react: 17.0.1 + dev: false + engines: + node: '>= 4' + peerDependencies: + react: '>= 0.14.0' + resolution: + integrity: sha512-O8DMCl32V34RrD+ZHxcAPc2+kYytuDIoQYjY36RVdsLK7uHjgNVvFec4yv0X6LgB4YEZgSvK5QtFi5YVqEpoMA== /md5.js/1.3.5: dependencies: hash-base: 3.1.0 @@ -13885,6 +13906,7 @@ eslint-plugin-tsdoc: ^0.2.11 file-saver: ^2.0.5 git-revision-webpack-plugin: ^3.0.6 + gitlog: ^4.0.4 i18next: ^19.9.1 i18next-browser-languagedetector: ^6.0.1 i18next-http-backend: ^1.1.1 @@ -13893,6 +13915,7 @@ less-loader: 7.3.0 lodash-es: ^4.17.21 lru-cache: ^6.0.0 + markdown-to-jsx: ^7.1.2 rc-menu: ^8.10.6 react: ^17.0.1 react-chartjs-2: ^2.11.1 diff --git a/public/locales/en.json b/public/locales/en.json index ff8a922..d3b2b19 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -820,6 +820,8 @@ "cardWhatsNewTitle": "What's New", "cardCommitsTitle": "Commits", - "cardCommitsSeeMore": "See more" + "cardCommitsSeeMore": "See more", + + "new": "New!" } } diff --git a/src/pages/whatsnew/WhatsNewCard.tsx b/src/pages/whatsnew/WhatsNewCard.tsx new file mode 100644 index 0000000..057930c --- /dev/null +++ b/src/pages/whatsnew/WhatsNewCard.tsx @@ -0,0 +1,116 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under GPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import { FC } from "react"; +import classNames from "classnames"; +import { Card, Skeleton, Row, Tag } from "antd"; + +import { useTranslation } from "react-i18next"; + +import { WhatsNewItem } from "./types"; + +import Markdown from "markdown-to-jsx"; +import { DateTime } from "@comp/DateTime"; + +import { slice } from "lodash-es"; + +interface Props { + loading?: boolean; + whatsNew?: WhatsNewItem[]; + baseURL?: string; + repoURL: string; + className?: string; +} + +export function WhatsNewCard({ + loading, + whatsNew, + baseURL, + repoURL, + className +}: Props): JSX.Element { + const { t } = useTranslation(); + + const classes = classNames("kw-card", "whats-new-card-whats-new", className); + + return + + {!loading && whatsNew && <> + {/* Display the first 5 whats new items */} + {slice(whatsNew, 0, 5).map((w, i) => ( + // I'm hesitant to use an index here, but there's nothing better + + ))} + } + + ; +} + +interface WhatsNewProps { + whatsNew: WhatsNewItem; + baseURL?: string; + repoURL: string; +} + +function WhatsNew({ whatsNew, baseURL, repoURL }: WhatsNewProps): JSX.Element { + const { t } = useTranslation(); + + // Allow overriding a link to make it open in a new tab and start with baseURL + const OverrideLink: FC = ({ title, href, children }) => { + // Force the link to start with baseURL if it's relative + const absLink = href.startsWith("/") + ? baseURL + href + : href; + + return + {children} + ; + }; + + return
+
+ {/* "New!" tag */} + {whatsNew.new && ( + {t("whatsNew.new")} + )} + + {/* What's new item body */} + + {whatsNew.body} + +
+ +
+ {/* Author avatar */} + {whatsNew.authorUsername && ( + + )} + + {/* Author name */} + {whatsNew.authorName && <> + {whatsNew.authorName} + + } + + {/* Date and link to commits */} + + + +
+
; +} diff --git a/src/pages/whatsnew/WhatsNewPage.less b/src/pages/whatsnew/WhatsNewPage.less index cc73a18..6344a69 100644 --- a/src/pages/whatsnew/WhatsNewPage.less +++ b/src/pages/whatsnew/WhatsNewPage.less @@ -22,21 +22,24 @@ } } - .whats-new-card-commits { - .commit { + .whats-new-card-commits, .whats-new-card-whats-new { + .commit, .whats-new-item { display: block; color: @text-color; - .commit-type-tag { + .commit-type-tag, .whats-new-new-tag { border: none; background: @kw-light; color: @text-color; &.commit-type-fix { background: @kw-primary; } - &.commit-type-feat { background: desaturate(@kw-green, 10%); } + &.commit-type-feat, &.whats-new-new-tag { + background: desaturate(@kw-green, 10%); + } + &.commit-type-i18n { background: @kw-purple; } } - .commit-author { + .commit-author, .whats-new-footer { display: flex; flex-direction: row; align-items: center; @@ -46,7 +49,11 @@ color: @text-color-secondary; font-size: @font-size-sm; - .commit-avatar { + a { + color: @text-color-secondary; + } + + .commit-avatar, .whats-new-avatar { display: inline-block; border-radius: @border-radius-base; margin-right: @padding-xs; diff --git a/src/pages/whatsnew/WhatsNewPage.tsx b/src/pages/whatsnew/WhatsNewPage.tsx index 6c4b47b..812bd61 100644 --- a/src/pages/whatsnew/WhatsNewPage.tsx +++ b/src/pages/whatsnew/WhatsNewPage.tsx @@ -11,17 +11,21 @@ import { RootState } from "@store"; import * as api from "@api"; -import { WhatsNewResponse } from "./types"; +import { WhatsNewResponse, Commit } from "./types"; import { getAuthorInfo } from "@utils/credits"; import { PageLayout } from "@layout/PageLayout"; +import { WhatsNewCard } from "./WhatsNewCard"; import { CommitsCard } from "./CommitsCard"; import "./WhatsNewPage.less"; const { Title } = Typography; +declare const __GIT_COMMITS__: Commit[]; +const kristWebCommits: Commit[] = __GIT_COMMITS__; + export function WhatsNewPage(): JSX.Element { const { t } = useTranslation(); @@ -60,7 +64,13 @@ {/* KristWeb commits */} - + + + {/* Krist */} @@ -71,7 +81,14 @@ {/* Krist What's new */} - + + + {/* Krist commits */} diff --git a/tools/commitLog.js b/tools/commitLog.js new file mode 100644 index 0000000..2a55624 --- /dev/null +++ b/tools/commitLog.js @@ -0,0 +1,51 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under GPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt + +const gitlog = require("gitlog").default; + +// Based on the Krist code +const messageTypeRe = /^(\w+): (.+)/; +function formatCommits(commits) { + const newCommits = []; + + for (const commit of commits) { + if (!commit.subject) continue; + + const [, type, rest] = messageTypeRe.exec(commit.subject) || []; + if (type) { + commit.type = type; + commit.subject = rest; + } + + // Not possible until async is figured out + // commit.avatar = await getAvatar(commit); + + newCommits.push({ + type: commit.type, + subject: commit.subject, + body: commit.body, + hash: commit.hash, + authorName: commit.authorName, + authorEmail: commit.authorEmail, + authorDate: commit.authorDate, + authorDateRel: commit.authorDateRel, + avatar: commit.avatar, + }); + } + + return newCommits; +} + +// This is performed synchronously until I can find a way to do async defines +// in webpack. This unfortunately also means that there won't be any avatars. +const commits = formatCommits(gitlog({ + repo: __dirname, + number: 5, + fields: [ + "subject", "body", "hash", + "authorName", "authorEmail", "authorDate", "authorDateRel" + ] +})); + +module.exports = { commits };