diff --git a/package.json b/package.json index d44fdee..3edbcad 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ "react-i18next": "^11.8.11", "react-redux": "^7.2.2", "react-router-dom": "^5.2.0", + "react-router-hash-link": "^2.4.0", "react-timeago": "^5.2.0", "react-world-flags": "^1.4.0", "redux": "^4.0.5", @@ -105,6 +106,7 @@ "@types/react-dom": "^17.0.2", "@types/react-redux": "^7.1.16", "@types/react-router-dom": "^5.1.7", + "@types/react-router-hash-link": "^1.2.1", "@types/react-timeago": "^4.1.2", "@types/semver": "^7.3.4", "@types/shallowequal": "^1.1.1", diff --git a/public/locales/en.json b/public/locales/en.json index e185dad..204ee61 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -421,6 +421,16 @@ "buildTime": "Build time", "variant": "Build variant", "license": "License" + }, + + "privacyTitle": "Privacy", + "privacy": { + "kristServer": "Krist Server", + "kristServerDesc": "The only PII that the <1>Krist Server stores is your IP address, User-Agent and Origin, as part of the webserver logs. This information is automatically purged after 30 days.", + "kristweb": "KristWeb", + "kristwebDesc1": "KristWeb uses a self-hosted <1>Sentry server for automatic error reporting. This system stores your IP address, User-Agent, Origin, breadcrumbs, and the details for any errors that get automatically reported. This information is automatically purged after 30 days.", + "kristwebDesc2": "If you have an ad-blocking or tracker-blocking extension such as <1>uBlock Origin (recommended), our Sentry system is already blocked by the built-in lists, so you do not have to worry about your privacy. You can also opt-out of error reporting in the <4>settings page. That said, if you’d like to help us by providing more detailed error reports, then please consider making an exception for KristWeb in your tracker blocker software. This site does not serve ads.", + "kristwebDesc3": "If you have any questions or concerns, please contact the developers." } }, @@ -468,6 +478,7 @@ "tableHotkeys": "Enable table navigation hotkeys (left and right arrows)", "subMenuPrivacy": "Privacy", + "privacyInfo": "Privacy information", "errorReporting": "Enable automatic error reporting (requires refresh)", "messageOnErrorReport": "Show a notification when an error is automatically reported (requires refresh)", diff --git a/src/global/ErrorBoundary.tsx b/src/global/ErrorBoundary.tsx index 66f079c..5b916e0 100644 --- a/src/global/ErrorBoundary.tsx +++ b/src/global/ErrorBoundary.tsx @@ -7,6 +7,7 @@ import { useTFns } from "@utils/i18n"; import * as Sentry from "@sentry/react"; +import { errorReporting } from "@utils"; interface Props { name: string; @@ -37,8 +38,11 @@ description={<>

{tStr("description")}

- {/* TODO: Hide this if Sentry is disabled */} -

{tStr("sentryNote")}

+ {/* If Sentry error reporting is enabled, add a message saying the error + * was automatically reported. */} + {errorReporting && ( +

{tStr("sentryNote")}

+ )} } />; } diff --git a/src/pages/credits/CreditsPage.less b/src/pages/credits/CreditsPage.less index c3e248b..642071e 100644 --- a/src/pages/credits/CreditsPage.less +++ b/src/pages/credits/CreditsPage.less @@ -20,4 +20,11 @@ text-align: left; } + + .credits-privacy { + max-width: 840px; + margin: 0 auto; + + text-align: left; + } } diff --git a/src/pages/credits/CreditsPage.tsx b/src/pages/credits/CreditsPage.tsx index 136f39d..05ee64b 100644 --- a/src/pages/credits/CreditsPage.tsx +++ b/src/pages/credits/CreditsPage.tsx @@ -5,8 +5,11 @@ import { useTranslation, Trans } from "react-i18next"; import { PageLayout } from "@layout/PageLayout"; + import { Supporters } from "./Supporters"; import { Translators } from "./Translators"; +import { Privacy } from "./Privacy"; + import { DateTime } from "@comp/DateTime"; import { getAuthorInfo, useHostInfo } from "@utils"; @@ -98,6 +101,10 @@ {t("credits.translatorsTitle")} + {/* Privacy */} + {t("credits.privacyTitle")} + + {t("credits.tmpim")} {t("credits.tmpim")} diff --git a/src/pages/credits/Privacy.tsx b/src/pages/credits/Privacy.tsx new file mode 100644 index 0000000..147a83d --- /dev/null +++ b/src/pages/credits/Privacy.tsx @@ -0,0 +1,59 @@ +// 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 { Typography } from "antd"; + +import { Trans } from "react-i18next"; +import { useTFns } from "@utils/i18n"; + +import { Link } from "react-router-dom"; + +import { useSyncNode } from "@api"; + +const { Title } = Typography; + +export function Privacy(): JSX.Element { + const { tStr, tKey } = useTFns("credits.privacy."); + + const syncNode = useSyncNode(); + + return
+ {tStr("kristServer")} +

+ The only PII that the + + Krist server + + stores is your IP address, User-Agent and Origin, as part of the webserver + logs. This information is automatically purged after 30 days. +

+ + {tStr("kristweb")} +

+ KristWeb uses a self-hosted + + Sentry + + server for automatic error reporting. This system stores your IP address, + User-Agent, Origin, breadcrumbs, and the details for any errors that get + automatically reported. This information is automatically purged after 30 + days. +

+ +

+ If you have an ad-blocking or tracker-blocking extension such as + + uBlock Origin + + (recommended), our Sentry system is already blocked by + the built-in lists, so you do not have to worry about your privacy. You + can also opt-out of error reporting in the + settings page. That said, if you’d like to + help us by providing more detailed error reports, then please consider + making an exception for KristWeb in your tracker blocker software. This + site does not serve ads. +

+ +

{tStr("kristwebDesc3")}

+
; +} diff --git a/src/pages/settings/SettingLink.tsx b/src/pages/settings/SettingLink.tsx new file mode 100644 index 0000000..5469aa0 --- /dev/null +++ b/src/pages/settings/SettingLink.tsx @@ -0,0 +1,37 @@ +// 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 { Menu } from "antd"; +import { LinkOutlined } from "@ant-design/icons"; + +import { HashLink } from "react-router-hash-link"; + +import { useTranslation } from "react-i18next"; + +import { SettingDescription } from "./SettingDescription"; + +interface Props { + link: string; + title?: string; + titleKey?: string; + description?: string; + descriptionKey?: string; +} + +export function SettingLink({ + link, + title, titleKey, + description, descriptionKey, + ...props +}: Props): JSX.Element { + const { t } = useTranslation(); + + return + +
+ {titleKey ? t(titleKey) : title} + +
+
+
; +} diff --git a/src/pages/settings/SettingsGroup.tsx b/src/pages/settings/SettingsGroup.tsx index 37efa48..bba9058 100644 --- a/src/pages/settings/SettingsGroup.tsx +++ b/src/pages/settings/SettingsGroup.tsx @@ -1,7 +1,7 @@ // 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 React from "react"; +import React, { ComponentType, ReactNode } from "react"; import { Menu } from "antd"; import { useTFns } from "@utils/i18n"; @@ -11,7 +11,7 @@ import { SettingInteger } from "./SettingInteger"; export interface SettingDesc { - component: React.ComponentType<{ + component: ComponentType<{ setting: SettingName; title?: string; titleKey?: string; @@ -22,11 +22,13 @@ setting: SettingName; } +export type GroupItem = SettingDesc | ReactNode; + interface Props { subKey: string; - icon?: React.ReactNode; + icon?: ReactNode; - settings: SettingDesc[]; + settings: GroupItem[]; } export const booleanSetting = (setting: SettingName): SettingDesc => @@ -52,15 +54,19 @@ > {/* Render each setting */} {settings.map(s => ( - - {React.createElement(s.component, { - setting: s.setting, - titleKey: tKey(s.setting), - descriptionKey: i18n.exists(tKey(s.setting + "Description")) - ? tKey(s.setting + "Description") - : undefined - })} - + s && typeof s === "object" && "setting" in s + ? ( + + {React.createElement(s.component, { + setting: s.setting, + titleKey: tKey(s.setting), + descriptionKey: i18n.exists(tKey(s.setting + "Description")) + ? tKey(s.setting + "Description") + : undefined + })} + + ) + : s // Render a ReactNode directly ))} ; } diff --git a/src/pages/settings/SettingsPage.tsx b/src/pages/settings/SettingsPage.tsx index 47e871d..a363481 100644 --- a/src/pages/settings/SettingsPage.tsx +++ b/src/pages/settings/SettingsPage.tsx @@ -13,6 +13,7 @@ import { PageLayout, PageLayoutProps } from "@layout/PageLayout"; import { SettingsGroup, booleanSetting, integerSetting } from "./SettingsGroup"; import { SettingBoolean } from "./SettingBoolean"; +import { SettingLink } from "./SettingLink"; import { getLanguageItems } from "./translations/LanguageItem"; import { useSettingsManage } from "./manage/SettingsManage"; @@ -89,6 +90,8 @@ subKey="Privacy" icon={} settings={[ + , booleanSetting("errorReporting"), booleanSetting("messageOnErrorReport") ]} diff --git a/src/utils/errors.ts b/src/utils/errors.ts index 669024d..639f434 100644 --- a/src/utils/errors.ts +++ b/src/utils/errors.ts @@ -11,17 +11,19 @@ const gitVersion: string = __GIT_VERSION__; const ls = localStorage.getItem("settings.errorReporting"); -const errorReporting = process.env.DISABLE_SENTRY !== "true" && +export const errorReporting = process.env.DISABLE_SENTRY !== "true" && (ls === null || ls === "true"); -const messageOnErrorReport = localStorage.getItem("settings.messageOnErrorReport") === "true"; +export const messageOnErrorReport = localStorage.getItem("settings.messageOnErrorReport") === "true"; Sentry.init({ - dsn: "https://51a018424102449b88f94c795cf62bb7@sentry.lemmmy.pw/2", + dsn: errorReporting + ? "https://51a018424102449b88f94c795cf62bb7@sentry.lemmmy.pw/2" + : undefined, release: "kristweb2-react@" + gitVersion, integrations: [new Integrations.BrowserTracing()], // Disable Sentry error reporting if the setting is disabled: - tracesSampleRate: errorReporting ? 1 : 0, + tracesSampleRate: errorReporting ? 0.2 : 0, beforeSend(event) { // Don't send an error event if error reporting is disabled diff --git a/yarn.lock b/yarn.lock index bdc3d11..79f7464 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2077,7 +2077,7 @@ hoist-non-react-statics "^3.3.0" redux "^4.0.0" -"@types/react-router-dom@^5.1.7": +"@types/react-router-dom@*", "@types/react-router-dom@^5.1.7": version "5.1.7" resolved "https://registry.yarnpkg.com/@types/react-router-dom/-/react-router-dom-5.1.7.tgz#a126d9ea76079ffbbdb0d9225073eb5797ab7271" integrity sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg== @@ -2086,6 +2086,14 @@ "@types/react" "*" "@types/react-router" "*" +"@types/react-router-hash-link@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@types/react-router-hash-link/-/react-router-hash-link-1.2.1.tgz#fba7dc351cef2985791023018b7a5dbd0653c843" + integrity sha512-jdzPGE8jFGq7fHUpPaKrJvLW1Yhoe5MQCrmgeesC+eSLseMj3cGCTYMDA4BNWG8JQmwO8NTYt/oT3uBZ77pmBA== + dependencies: + "@types/react" "*" + "@types/react-router-dom" "*" + "@types/react-router@*": version "5.1.12" resolved "https://registry.yarnpkg.com/@types/react-router/-/react-router-5.1.12.tgz#0f300e09468e7aed86e18241c90238c18c377e51" @@ -10320,6 +10328,13 @@ tiny-invariant "^1.0.2" tiny-warning "^1.0.0" +react-router-hash-link@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/react-router-hash-link/-/react-router-hash-link-2.4.0.tgz#216045d9bb826e5f36f873dea8b04874a0708f83" + integrity sha512-HGbB9kfODHKsHvMVsPbqDr057V4xg4TNNRaQcezsFMKitwHaaU51cM2+gDyX45y9YLLPbovELz2rpNx2C3Frng== + dependencies: + prop-types "^15.7.2" + react-router@5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/react-router/-/react-router-5.2.0.tgz#424e75641ca8747fbf76e5ecca69781aa37ea293"