diff --git a/.vscode/settings.json b/.vscode/settings.json index f900d9e..d2a2b46 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,6 +49,7 @@ "motd", "multiline", "optimisation", + "personalise", "pnpm", "precaching", "privatekeys", diff --git a/public/locales/en.json b/public/locales/en.json index b019080..f2b5aa9 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -58,6 +58,7 @@ "hostedBy": "Hosted by <1>{{host}}", "github": "GitHub", "credits": "Credits", + "whatsNew": "What's new", "updateTitle": "Update available!", "updateDescription": "A new version of KristWeb is available. Please reload.", @@ -333,7 +334,28 @@ "motdDebugMode": "This server is an unofficial development server. Balances and transactions can be manipulated. Proceed with caution.", "whatsNewCardTitle": "What's new", - "whatsNewButton": "What's new" + "whatsNewButton": "What's new", + + "tipsCardTitle": "Tip of the day", + "tipsPrevious": "Prev", + "tipsNext": "Next", + "tips": { + "0": "Check out what's new in Krist and KristWeb on the [What's New page](/whatsnew)!", + "1": "You can quickly navigate through tables with the arrow keys on desktop.", + "2": "You can click on table headers to sort them.", + "3": "You can filter by categories in the [My Wallets](/wallets) page by clicking the filter icon in the table header.", + "4": "The [settings page](/settings) has many advanced options to personalise your KristWeb experience.", + "5": "Generate pre-filled transaction links with the new [Request page](/request).", + "6": "Be sure to backup [your wallets](/wallets)!", + "7": "Quickly search the Krist network with the keyboard shortcut Ctrl+K (Cmd+K on macOS).", + "8": "Add contacts in the [address book](/contacts) to quickly send them transactions.", + "9": "A 'bumped' transaction is a transaction sent to and from the same address.", + "10": "The 'block difficulty' chart can be shown with a logarithmic scale to see small changes easier at lower difficulties.", + "11": "The date format can be changed in the [advanced settings](/settings).", + "12": "You can see the [lowest mined block hashes](/network/blocks/lowest).", + "13": "The most recently purchased names can be seen on the [Network Names page](/network/names/new).", + "14": "The block value increases when [names](/network/names) are purchased." + } }, "credits": { diff --git a/src/components/krist/MarkdownLink.tsx b/src/components/krist/MarkdownLink.tsx index 0077028..94e1b87 100644 --- a/src/components/krist/MarkdownLink.tsx +++ b/src/components/krist/MarkdownLink.tsx @@ -2,6 +2,7 @@ // This file is part of KristWeb 2 under AGPL-3.0. // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import { FC } from "react"; +import { Link } from "react-router-dom"; import { useSyncNode } from "@api"; @@ -27,3 +28,11 @@ ; }; } + +export function useRelativeMarkdownLink(): FC { + return ({ title, href, children }) => { + return + {children} + ; + }; +} diff --git a/src/global/AppServices.tsx b/src/global/AppServices.tsx index 9bfc73d..7cc8254 100644 --- a/src/global/AppServices.tsx +++ b/src/global/AppServices.tsx @@ -9,6 +9,7 @@ import { AppHotkeys } from "./AppHotkeys"; import { StorageBroadcast } from "./StorageBroadcast"; import { PurchaseKristHandler } from "./PurchaseKrist"; +import { AdvanceTip } from "@pages/dashboard/TipsCard"; export function AppServices(): JSX.Element { return <> @@ -18,7 +19,7 @@ - + ; } diff --git a/src/layout/sidebar/SidebarFooter.tsx b/src/layout/sidebar/SidebarFooter.tsx index 8413be7..c189bb9 100644 --- a/src/layout/sidebar/SidebarFooter.tsx +++ b/src/layout/sidebar/SidebarFooter.tsx @@ -31,6 +31,10 @@
{t("sidebar.github")}  –  + + {t("sidebar.whatsNew")} + +  –  {t("sidebar.credits")} diff --git a/src/pages/dashboard/DashboardPage.less b/src/pages/dashboard/DashboardPage.less index d29f33b..7d19b31 100644 --- a/src/pages/dashboard/DashboardPage.less +++ b/src/pages/dashboard/DashboardPage.less @@ -120,4 +120,28 @@ margin-bottom: 0; } } + + .dashboard-card-tips { + .ant-card-extra { + padding: 0; + margin-top: @margin-sm; + } + + .dashboard-tips-pagination { + user-select: none; + + .ant-btn.ant-btn-link { + &:not(:last-child) { + margin-right: @margin-lg; + } + + padding: 0; + // height: auto; + font-size: 90%; + + &:hover, &:active, &:focus { background: transparent; } + span { color: @text-color-secondary; } + } + } + } } diff --git a/src/pages/dashboard/DashboardPage.tsx b/src/pages/dashboard/DashboardPage.tsx index 9cb7ca4..4a9f063 100644 --- a/src/pages/dashboard/DashboardPage.tsx +++ b/src/pages/dashboard/DashboardPage.tsx @@ -12,7 +12,7 @@ import { BlockValueCard } from "./BlockValueCard"; import { BlockDifficultyCard } from "./BlockDifficultyCard"; import { MOTDCard } from "./MOTDCard"; -import { WhatsNewCard } from "./WhatsNewCard"; +import { TipsCard } from "./TipsCard"; import { SyncDetailedWork } from "@global/ws/SyncDetailedWork"; @@ -40,7 +40,7 @@ - + ; } diff --git a/src/pages/dashboard/TipsCard.tsx b/src/pages/dashboard/TipsCard.tsx new file mode 100644 index 0000000..d55174a --- /dev/null +++ b/src/pages/dashboard/TipsCard.tsx @@ -0,0 +1,87 @@ +// Copyright (c) 2020-2021 Drew Lemmy +// This file is part of KristWeb 2 under AGPL-3.0. +// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt +import { Card, Button, Typography } from "antd"; +import { CaretLeftOutlined, CaretRightOutlined } from "@ant-design/icons"; + +import { useSelector, useDispatch } from "react-redux"; +import { RootState } from "@store"; +import { setTip } from "@actions/MiscActions"; + +import { mod, useMountEffect } from "@utils"; +import { useTFns } from "@utils/i18n"; + +import Markdown from "markdown-to-jsx"; +import { useRelativeMarkdownLink } from "@comp/krist/MarkdownLink"; + +import Debug from "debug"; +const debug = Debug("kristweb:tips-card"); + +// All the tips must exist in `public/locales/en.json` under `dashboard.tips`. +export const TIP_COUNT = 15; + +function saveTip(tip: number) { + localStorage.setItem("tip", tip.toString()); +} + +/** Advance the tip on app start. */ +export function AdvanceTip(): JSX.Element | null { + const dispatch = useDispatch(); + const currentTip = useSelector((s: RootState) => s.misc.tip); + + useMountEffect(() => { + const next = mod(currentTip + 1, TIP_COUNT); + + debug("AdvanceTip setting tip from %d to %d", currentTip, next); + + dispatch(setTip(next)); + saveTip(next); + }); + + return null; +} + +export function TipsCard(): JSX.Element { + const { tStr } = useTFns("dashboard."); + + const dispatch = useDispatch(); + const rawTip = useSelector((s: RootState) => s.misc.tip); + const currentTip = mod(rawTip, TIP_COUNT); + + const changeTip = (tip: number) => { + dispatch(setTip(tip)); + saveTip(tip); + }; + const previousTip = () => changeTip(mod(currentTip - 1, TIP_COUNT)); + const nextTip = () => changeTip(mod(currentTip + 1, TIP_COUNT)); + + const tipText = tStr(`tips.${currentTip}`); + + const MarkdownLink = useRelativeMarkdownLink(); + + return + + + +
} + > +

+ + {tipText} + +

+ ; +} diff --git a/src/store/actions/MiscActions.ts b/src/store/actions/MiscActions.ts index 509a9d0..37fba20 100644 --- a/src/store/actions/MiscActions.ts +++ b/src/store/actions/MiscActions.ts @@ -6,3 +6,5 @@ export const incrementNameTableLock = createAction(constants.INCR_NAME_TABLE_LOCK)(); export const decrementNameTableLock = createAction(constants.DECR_NAME_TABLE_LOCK)(); + +export const setTip = createAction(constants.SET_TIP)(); diff --git a/src/store/constants.ts b/src/store/constants.ts index 12765e6..66c0036 100644 --- a/src/store/constants.ts +++ b/src/store/constants.ts @@ -61,3 +61,4 @@ // --- export const INCR_NAME_TABLE_LOCK = "INCR_NAME_TABLE_LOCK"; export const DECR_NAME_TABLE_LOCK = "DECR_NAME_TABLE_LOCK"; +export const SET_TIP = "SET_TIP"; diff --git a/src/store/reducers/MiscReducer.ts b/src/store/reducers/MiscReducer.ts index b7b9ca4..6e97588 100644 --- a/src/store/reducers/MiscReducer.ts +++ b/src/store/reducers/MiscReducer.ts @@ -3,15 +3,19 @@ // Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt import { createReducer } from "typesafe-actions"; import { - incrementNameTableLock, decrementNameTableLock + incrementNameTableLock, decrementNameTableLock, setTip } from "@actions/MiscActions"; export interface State { readonly nameTableLock: number; + readonly tip: number; } const initialState: State = { - nameTableLock: 0 + nameTableLock: 0, + tip: localStorage.getItem("tip") !== null + ? parseInt(localStorage.getItem("tip")!) + : -1 }; export const MiscReducer = createReducer(initialState) @@ -22,4 +26,7 @@ .handleAction(decrementNameTableLock, (state, _) => ({ ...state, nameTableLock: state.nameTableLock - 1 + })) + .handleAction(setTip, (state, { payload }) => ({ + ...state, tip: payload })); diff --git a/src/utils/index.ts b/src/utils/index.ts index 27aad1a..fcf6d6e 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -150,3 +150,5 @@ return [state, wrappedSetState]; } + +export const mod = (n: number, m: number): number => ((n % m) + m) % m;