diff --git a/.gitignore b/.gitignore index 8573590..23cf531 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,4 @@ yarn-debug.log* yarn-error.log* -/src/__data__/host.json +/src/__data__/host*.json diff --git a/.vscode/settings.json b/.vscode/settings.json index a346ec4..a95344d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -28,6 +28,7 @@ "categorised", "chartjs", "clientside", + "commithash", "commonmeta", "cryptocurrency", "dont", diff --git a/craco.config.js b/craco.config.js index 3acb036..471a251 100644 --- a/craco.config.js +++ b/craco.config.js @@ -6,6 +6,14 @@ const AntdDayjsWebpackPlugin = require("antd-dayjs-webpack-plugin"); const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); const WebpackBar = require("webpackbar"); +const GitRevisionPlugin = require("git-revision-webpack-plugin"); +const { DefinePlugin } = require("webpack"); + +const gitRevisionPlugin = new GitRevisionPlugin({ + // Include the '-dirty' suffix if the local tree has been modified, and + // include non-annotated tags. + versionCommand: "describe --always --tags --dirty" +}); module.exports = { style: { @@ -59,7 +67,13 @@ ...(process.env.NODE_ENV === "development" || process.env.FORCE_ANALYZE ? [new BundleAnalyzerPlugin({ openAnalyzer: false })] : []), - new AntdDayjsWebpackPlugin() + new AntdDayjsWebpackPlugin(), + gitRevisionPlugin, + new DefinePlugin({ + "__GIT_VERSION__": JSON.stringify(gitRevisionPlugin.version()), + "__GIT_COMMIT_HASH__": JSON.stringify(gitRevisionPlugin.commithash()), + "__BUILD_TIME__": DefinePlugin.runtimeValue(Date.now) + }) ], optimization: { diff --git a/package.json b/package.json index a10f796..366aaca 100644 --- a/package.json +++ b/package.json @@ -108,6 +108,7 @@ "eslint-plugin-react": "^7.22.0", "eslint-plugin-react-hooks": "^4.2.0", "eslint-plugin-tsdoc": "^0.2.11", + "git-revision-webpack-plugin": "^3.0.6", "react-refresh": "^0.9.0", "react-scripts": "^4.0.3", "redux-devtools-extension": "^2.13.8", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f6c9ab6..b3df7b7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,6 +67,7 @@ eslint-plugin-react: 7.22.0_eslint@7.21.0 eslint-plugin-react-hooks: 4.2.0_eslint@7.21.0 eslint-plugin-tsdoc: 0.2.11 + git-revision-webpack-plugin: 3.0.6 react-refresh: 0.9.0 react-scripts: 4.0.3_react@17.0.1+typescript@4.1.5 redux-devtools-extension: 2.13.8_redux@4.0.5 @@ -6153,6 +6154,12 @@ dev: true resolution: integrity: sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + /git-revision-webpack-plugin/3.0.6: + dev: true + engines: + node: '>=6.11.5' + resolution: + integrity: sha512-vW/9dBahGbpKPcccy3xKkHgdWoH/cAPPc3lQw+3edl7b4j29JfNGVrja0a1d8ZoRe4nTN8GCPrF9aBErDnzx5Q== /glob-parent/3.1.0: dependencies: is-glob: 3.1.0 @@ -13854,6 +13861,7 @@ eslint-plugin-react-hooks: ^4.2.0 eslint-plugin-tsdoc: ^0.2.11 file-saver: ^2.0.5 + git-revision-webpack-plugin: ^3.0.6 i18next: ^19.9.1 i18next-browser-languagedetector: ^6.0.1 i18next-http-backend: ^1.1.1 diff --git a/public/locales/en.json b/public/locales/en.json index 6e8c026..e768c53 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -300,13 +300,20 @@ "credits": { "title": "Credits", "madeBy": "Made by <1>{{authorName}}1>", + "hostedBy": "Hosted by <1>{{host}}1>", "supportersTitle": "Supporters", "supportersDescription": "This project was made possible by the following amazing supporters:", "supportButton": "Support KristWeb", "translatorsTitle": "Translators", "translatorsDescription": "This project was translated by the following amazing contributors:", "translateButton": "Translate KristWeb", - "tmpim": "Created by tmpim" + "tmpim": "Created by tmpim", + + "versionInfo": { + "version": "Version", + "commitHash": "Commit", + "buildTime": "Build time" + } }, "settings": { diff --git a/src/layout/AppLayout.less b/src/layout/AppLayout.less index 87523cc..965ef8d 100644 --- a/src/layout/AppLayout.less +++ b/src/layout/AppLayout.less @@ -174,159 +174,6 @@ } } -.site-sidebar { - background: @kw-sidebar-bg; - border-right: 1px solid @kw-border-color-darker; - - width: @kw-sidebar-width; - - position: fixed; - top: @layout-header-height; - bottom: 0; - - // Above the mobile collapse backdrop, below the modals - z-index: 910; - - transition: left @kw-sidebar-collapse-duration ease; - left: 0; - - &.collapsed { - left: -@kw-sidebar-width; - } - - .site-sidebar-header { - padding: 0.5rem 1rem; - - background: @kw-sidebar-header-bg; - border-bottom: 1px solid @kw-border-color-darker; - - line-height: 1.25; - - user-select: none; - - h5 { - margin-bottom: 0; - - font-size: @font-size-sm; - font-weight: bolder; - - color: @text-color-secondary; - } - - &.site-sidebar-update { - padding: 1rem; - - background: @primary-color; - color: @kw-darkest; - - h5 { - color: @kw-darkest; - margin-bottom: @padding-xs; - } - } - - &.site-sidebar-total-balance { - .anticon svg { - width: 14px; - height: 14px; - - position: relative; - bottom: 0.15em; - - color: @text-color-secondary; - } - - .krist-value { - font-size: 20px; - } - } - } - - .ant-layout-sider-children { - display: flex; - flex-direction: column; - - & > .ant-menu { - overflow-y: auto; - height: 100%; - } - } - - .ant-menu-item-group .ant-menu-item-group-title { - font-size: 0.8em; - font-weight: bold; - text-transform: uppercase; - - color: @kw-text-secondary; - - // margin: 1rem 0 0 0; - margin: 0; - padding: 1rem 1rem 0.5rem 1rem; - - border-top: 1px solid @kw-border-color-division; - - user-select: none; - } - - .ant-menu-item { - margin-top: 0; - margin-bottom: 0 !important; - padding: 0; - - line-height: @kw-sidebar-item-height; - height: @kw-sidebar-item-height; - vertical-align: middle; - - user-select: none; - - .anticon { - line-height: @kw-sidebar-item-height; - vertical-align: middle; - - font-size: 18px; - } - } - - .site-sidebar-footer { - user-select: none; - - padding: 0.5rem; - - text-align: center; - font-size: 75%; - - color: @text-color-secondary; - - a { - color: @text-color; - } - } -} - -.site-sidebar-backdrop { - position: fixed; - top: 0; - right: 0; - bottom: 0; - left: 0; - - transition: opacity @kw-sidebar-collapse-duration ease; - - background: @kw-sidebar-backdrop-bg; - opacity: 1; - - pointer-events: all; - cursor: pointer; - - // Below the sidebar and modals - z-index: 900; - - &.collapsed { - pointer-events: none; - opacity: 0; - } -} - .site-layout { min-height: calc(100vh - @layout-header-height); diff --git a/src/layout/sidebar/Sidebar.less b/src/layout/sidebar/Sidebar.less new file mode 100644 index 0000000..3a164da --- /dev/null +++ b/src/layout/sidebar/Sidebar.less @@ -0,0 +1,168 @@ +// 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"; + +.site-sidebar { + background: @kw-sidebar-bg; + border-right: 1px solid @kw-border-color-darker; + + width: @kw-sidebar-width; + + position: fixed; + top: @layout-header-height; + bottom: 0; + + // Above the mobile collapse backdrop, below the modals + z-index: 910; + + transition: left @kw-sidebar-collapse-duration ease; + left: 0; + + &.collapsed { + left: -@kw-sidebar-width; + } + + .site-sidebar-header { + padding: 0.5rem 1rem; + + background: @kw-sidebar-header-bg; + border-bottom: 1px solid @kw-border-color-darker; + + line-height: 1.25; + + user-select: none; + + h5 { + margin-bottom: 0; + + font-size: @font-size-sm; + font-weight: bolder; + + color: @text-color-secondary; + } + + &.site-sidebar-update { + padding: 1rem; + + background: @primary-color; + color: @kw-darkest; + + h5 { + color: @kw-darkest; + margin-bottom: @padding-xs; + } + } + + &.site-sidebar-total-balance { + .anticon svg { + width: 14px; + height: 14px; + + position: relative; + bottom: 0.15em; + + color: @text-color-secondary; + } + + .krist-value { + font-size: 20px; + } + } + } + + .ant-layout-sider-children { + display: flex; + flex-direction: column; + + & > .ant-menu { + overflow-y: auto; + height: 100%; + } + } + + .ant-menu-item-group .ant-menu-item-group-title { + font-size: 0.8em; + font-weight: bold; + text-transform: uppercase; + + color: @kw-text-secondary; + + // margin: 1rem 0 0 0; + margin: 0; + padding: 1rem 1rem 0.5rem 1rem; + + border-top: 1px solid @kw-border-color-division; + + user-select: none; + } + + .ant-menu-item { + margin-top: 0; + margin-bottom: 0 !important; + padding: 0; + + line-height: @kw-sidebar-item-height; + height: @kw-sidebar-item-height; + vertical-align: middle; + + user-select: none; + + .anticon { + line-height: @kw-sidebar-item-height; + vertical-align: middle; + + font-size: 18px; + } + } + + .site-sidebar-footer { + user-select: none; + + padding: 0.5rem; + + text-align: center; + font-size: 75%; + + color: @text-color-secondary; + + a { + color: @text-color; + } + + .site-sidebar-footer-version { + margin: @padding-xs -0.5rem 0 -0.5rem; + padding-top: @padding-xs; + + border-top: 1px solid @kw-border-color-division; + + font-size: 90%; + font-weight: 500; + line-height: 1; + } + } +} + +.site-sidebar-backdrop { + position: fixed; + top: 0; + right: 0; + bottom: 0; + left: 0; + + transition: opacity @kw-sidebar-collapse-duration ease; + + background: @kw-sidebar-backdrop-bg; + opacity: 1; + + pointer-events: all; + cursor: pointer; + + // Below the sidebar and modals + z-index: 900; + + &.collapsed { + pointer-events: none; + opacity: 0; + } +} diff --git a/src/layout/sidebar/Sidebar.tsx b/src/layout/sidebar/Sidebar.tsx index ba585f2..798350d 100644 --- a/src/layout/sidebar/Sidebar.tsx +++ b/src/layout/sidebar/Sidebar.tsx @@ -14,6 +14,8 @@ import { ConditionalLink } from "@comp/ConditionalLink"; +import "./Sidebar.less"; + const { Sider } = Layout; type SidebarItemProps = MenuItemProps & { diff --git a/src/layout/sidebar/SidebarFooter.tsx b/src/layout/sidebar/SidebarFooter.tsx index ac491c8..1e90d0b 100644 --- a/src/layout/sidebar/SidebarFooter.tsx +++ b/src/layout/sidebar/SidebarFooter.tsx @@ -1,32 +1,23 @@ // 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 React, { useState } from "react"; +import React from "react"; import { useTranslation, Trans } from "react-i18next"; -import { useMountEffect } from "@utils"; -import { getAuthorInfo } from "@utils/credits"; +import { getAuthorInfo, useHostInfo } from "@utils/credits"; import { ConditionalLink } from "@comp/ConditionalLink"; +declare const __GIT_VERSION__: string; + export function SidebarFooter(): JSX.Element { const { t } = useTranslation(); - const [host, setHost] = useState<{ host: { name: string; url: string } } | undefined>(); - - useMountEffect(() => { - (async () => { - try { - // Add the host information if host.json exists - const hostFile = "host-attribution"; // Trick webpack into dynamic importing - const hostData = await import("../../__data__/" + hostFile + ".json"); - setHost(hostData); - } catch (ignored) { - // Ignored - } - })(); - }); const { authorName, authorURL, gitURL } = getAuthorInfo(); + const host = useHostInfo(); + + // Replaced by webpack DefinePlugin and git-revision-webpack-plugin + const gitVersion: string = __GIT_VERSION__; return (