diff --git a/.vscode/settings.json b/.vscode/settings.json index bea3982..54466c7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -32,6 +32,7 @@ "commithash", "commonmeta", "cryptocurrency", + "desaturate", "dont", "firstseen", "jwalelset", diff --git a/public/locales/en.json b/public/locales/en.json index 9092d82..ff8a922 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -819,6 +819,7 @@ "tooltipGitHub": "View on GitHub", "cardWhatsNewTitle": "What's New", - "cardCommitsTitle": "Commits" + "cardCommitsTitle": "Commits", + "cardCommitsSeeMore": "See more" } } diff --git a/src/global/ws/SyncMOTD.tsx b/src/global/ws/SyncMOTD.tsx index c897d28..6e9fecf 100644 --- a/src/global/ws/SyncMOTD.tsx +++ b/src/global/ws/SyncMOTD.tsx @@ -25,6 +25,7 @@ const data = await api.get("motd"); debug("motd: %s", data.motd); + store.dispatch(nodeActions.setPackage(data.package)); store.dispatch(nodeActions.setCurrency(data.currency)); store.dispatch(nodeActions.setConstants(data.constants)); diff --git a/src/krist/api/types.ts b/src/krist/api/types.ts index b4f046a..9849dba 100644 --- a/src/krist/api/types.ts +++ b/src/krist/api/types.ts @@ -113,11 +113,27 @@ debug_mode: boolean; last_block?: KristBlock; - + package: KristMOTDPackage; constants: KristConstants; currency: KristCurrency; } +export interface KristMOTDPackage { + name: string; + version: string; + author: string; + licence: string; + repository: string; +} + +export const DEFAULT_PACKAGE = { + "name": "krist", + "version": "0.0.0", + "author": "Lemmmy", + "licence": "GPL-3.0", + "repository": "https://github.com/tmpim/Krist" +}; + export type APIResponse> = T & { ok: boolean; error?: string; diff --git a/src/pages/whatsnew/CommitsCard.tsx b/src/pages/whatsnew/CommitsCard.tsx new file mode 100644 index 0000000..206220e --- /dev/null +++ b/src/pages/whatsnew/CommitsCard.tsx @@ -0,0 +1,88 @@ +// 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 classNames from "classnames"; +import { Card, Skeleton, Row, Tag } from "antd"; + +import { useTranslation } from "react-i18next"; + +import { Commit } from "./types"; + +import { DateTime } from "@comp/DateTime"; + +import { slice } from "lodash-es"; + +interface Props { + loading?: boolean; + commits?: Commit[]; + repoURL: string; + className?: string; +} + +export function CommitsCard({ + loading, + commits, + repoURL, + className +}: Props): JSX.Element { + const { t } = useTranslation(); + + const classes = classNames("kw-card", "whats-new-card-commits", className); + + return + + {!loading && commits && <> + {/* Display the first 5 commits */} + {slice(commits, 0, 5).map(c => ( + + ))} + + {/* 'See more' link */} + + + {t("whatsNew.cardCommitsSeeMore")} + + + } + + ; +} + +interface CommitItemProps { + commit: Commit; + repoURL: string; +} + +function CommitItem({ commit, repoURL }: CommitItemProps): JSX.Element { + return + {/* Conventional commits type tag */} + {commit.type && ( + + {commit.type} + + )} + + {/* Commit subject */} + {commit.subject} + + {/* Commit author */} +
+ {/* Author avatar */} + {commit.avatar && ( + + )} + + {/* Author name */} + {commit.authorName} + + + + {/* Commit time */} + +
+
; +} diff --git a/src/pages/whatsnew/WhatsNewPage.less b/src/pages/whatsnew/WhatsNewPage.less new file mode 100644 index 0000000..cc73a18 --- /dev/null +++ b/src/pages/whatsnew/WhatsNewPage.less @@ -0,0 +1,65 @@ +// 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 (reference) "../../App.less"; + +.whats-new-page { + .whats-new-github-link { + display: inline-flex; + margin-left: @margin-sm; + + color: @text-color; + + align-items: center; + justify-content: center; + + &:hover { + color: @text-color-secondary; + } + + .anticon.anticon-github { + font-size: 1.5rem; + } + } + + .whats-new-card-commits { + .commit { + display: block; + color: @text-color; + + .commit-type-tag { + border: none; + background: @kw-light; + color: @text-color; + + &.commit-type-fix { background: @kw-primary; } + &.commit-type-feat { background: desaturate(@kw-green, 10%); } + } + + .commit-author { + display: flex; + flex-direction: row; + align-items: center; + + margin-top: @padding-xss; + + color: @text-color-secondary; + font-size: @font-size-sm; + + .commit-avatar { + display: inline-block; + border-radius: @border-radius-base; + margin-right: @padding-xs; + width: 16px; + height: 16px; + } + } + + .sep { + display: inline-block; + margin: 0 @padding-xs; + color: fade(@text-color-secondary, 50%); + } + } + } +} diff --git a/src/pages/whatsnew/WhatsNewPage.tsx b/src/pages/whatsnew/WhatsNewPage.tsx index 60ab7d2..6c4b47b 100644 --- a/src/pages/whatsnew/WhatsNewPage.tsx +++ b/src/pages/whatsnew/WhatsNewPage.tsx @@ -2,15 +2,24 @@ // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import { useState, useEffect } from "react"; -import { Row, Col, Card, Typography } from "antd"; +import { Row, Col, Typography, Tooltip } from "antd"; +import { GithubOutlined } from "@ant-design/icons"; import { useTranslation } from "react-i18next"; +import { useSelector } from "react-redux"; +import { RootState } from "@store"; + import * as api from "@api"; import { WhatsNewResponse } from "./types"; +import { getAuthorInfo } from "@utils/credits"; import { PageLayout } from "@layout/PageLayout"; +import { CommitsCard } from "./CommitsCard"; + +import "./WhatsNewPage.less"; + const { Title } = Typography; export function WhatsNewPage(): JSX.Element { @@ -21,9 +30,14 @@ const [kristData, setKristData] = useState(); const [loading, setLoading] = useState(true); + // Get the repository URL for KristWeb + const kristWebRepo = getAuthorInfo().gitURL; + // Get the repository URL for the sync node + const kristPackage = useSelector((s: RootState) => s.node.package); + useEffect(() => { // Fetch the 'whats new' and commits from the Krist sync node - api.get("/whatsnew") + api.get("whatsnew") .then(setKristData) .catch(console.error) // TODO: show errors to the user .finally(() => setLoading(false)); @@ -35,18 +49,52 @@ className="whats-new-page" > - {/* KristWeb row */} - {t("whatsNew.titleKristWeb")} + {/* KristWeb */} + + {t("whatsNew.titleKristWeb")} + <GithubLink repoURL={kristWebRepo} /> + + + {/* KristWeb What's new */} + + {/* KristWeb commits */} - {/* Krist row */} - {t("whatsNew.titleKrist")} + {/* Krist */} + + {t("whatsNew.titleKrist")} + <GithubLink repoURL={kristPackage.repository} /> + + + {/* Krist What's new */} - + + {/* Krist commits */} + + + ; } + +function GithubLink({ repoURL }: { repoURL: string }): JSX.Element { + const { t } = useTranslation(); + + return + + + + ; +} diff --git a/src/store/actions/NodeActions.ts b/src/store/actions/NodeActions.ts index a61049e..b357d67 100644 --- a/src/store/actions/NodeActions.ts +++ b/src/store/actions/NodeActions.ts @@ -2,7 +2,10 @@ // This file is part of KristWeb 2 under GPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import { createAction } from "typesafe-actions"; -import { KristWorkDetailed, KristCurrency, KristConstants, KristMOTDBase } from "@api/types"; +import { + KristWorkDetailed, KristCurrency, KristConstants, KristMOTDBase, + KristMOTDPackage +} from "@api/types"; import * as constants from "../constants"; @@ -15,6 +18,7 @@ export const setSyncNode = createAction(constants.SYNC_NODE)(); export const setDetailedWork = createAction(constants.DETAILED_WORK)(); +export const setPackage = createAction(constants.PACKAGE)(); export const setCurrency = createAction(constants.CURRENCY)(); export const setConstants = createAction(constants.CONSTANTS)(); export const setMOTD = createAction(constants.MOTD)(); diff --git a/src/store/constants.ts b/src/store/constants.ts index 31a24fc..7ff91d8 100644 --- a/src/store/constants.ts +++ b/src/store/constants.ts @@ -44,6 +44,7 @@ // --- export const SYNC_NODE = "SYNC_NODE"; export const DETAILED_WORK = "DETAILED_WORK"; +export const PACKAGE = "PACKAGE"; export const CURRENCY = "CURRENCY"; export const CONSTANTS = "CONSTANTS"; export const MOTD = "MOTD"; diff --git a/src/store/reducers/NodeReducer.ts b/src/store/reducers/NodeReducer.ts index c2ee065..4e9d91e 100644 --- a/src/store/reducers/NodeReducer.ts +++ b/src/store/reducers/NodeReducer.ts @@ -3,13 +3,16 @@ // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import { createReducer } from "typesafe-actions"; import { - KristWorkDetailed, KristCurrency, DEFAULT_CURRENCY, KristConstants, - DEFAULT_CONSTANTS, KristMOTDBase, DEFAULT_MOTD_BASE + KristWorkDetailed, + KristCurrency, DEFAULT_CURRENCY, + KristConstants, DEFAULT_CONSTANTS, + KristMOTDBase, DEFAULT_MOTD_BASE, + KristMOTDPackage, DEFAULT_PACKAGE } from "@api/types"; import { setLastBlockID, setLastTransactionID, setLastNonMinedTransactionID, setLastOwnTransactionID, setLastNameTransactionID, setLastOwnNameTransactionID, - setSyncNode, setDetailedWork, setCurrency, setConstants, setMOTD + setSyncNode, setDetailedWork, setPackage, setCurrency, setConstants, setMOTD } from "@actions/NodeActions"; import packageJson from "../../../package.json"; @@ -26,6 +29,7 @@ // Info from the MOTD readonly syncNode: string; readonly detailedWork?: KristWorkDetailed; + readonly package: KristMOTDPackage; readonly currency: KristCurrency; readonly constants: KristConstants; readonly motd: KristMOTDBase; @@ -43,6 +47,7 @@ // Info from the MOTD syncNode: localStorage.getItem("syncNode") || packageJson.defaultSyncNode, + package: DEFAULT_PACKAGE, currency: DEFAULT_CURRENCY, constants: DEFAULT_CONSTANTS, motd: DEFAULT_MOTD_BASE @@ -61,6 +66,7 @@ // Info from the MOTD .handleAction(setSyncNode, (state, action) => ({ ...state, syncNode: action.payload })) .handleAction(setDetailedWork, (state, action) => ({ ...state, detailedWork: action.payload })) + .handleAction(setPackage, (state, action) => ({ ...state, package: action.payload })) .handleAction(setCurrency, (state, action) => ({ ...state, currency: action.payload })) .handleAction(setConstants, (state, action) => ({ ...state, constants: action.payload })) .handleAction(setMOTD, (state, action) => ({ ...state, motd: action.payload }));