fix: update terminology from "Report a Bug" to "Report an Issue" across multiple components
This commit is contained in:
@@ -30,7 +30,7 @@
|
|||||||
"activities": "Activities",
|
"activities": "Activities",
|
||||||
"points": "Points",
|
"points": "Points",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"reportBug": "Report a Bug"
|
"reportBug": "Report an Issue"
|
||||||
},
|
},
|
||||||
"search": {
|
"search": {
|
||||||
"title": "Search",
|
"title": "Search",
|
||||||
|
|||||||
@@ -91,8 +91,8 @@
|
|||||||
"description": "Tell us how Donetick is working for you or request a feature."
|
"description": "Tell us how Donetick is working for you or request a feature."
|
||||||
},
|
},
|
||||||
"bugReport": {
|
"bugReport": {
|
||||||
"title": "Report a Bug",
|
"title": "Report an Issue",
|
||||||
"description": "Something not working right? Send us the details along with a technical snapshot."
|
"description": "Tell us what's not working and we'll attach the technical details for you."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
|
|
||||||
|
import { setServerVersion } from '../service/DiagnosticsSession'
|
||||||
import { GetResource } from '../utils/Fetcher'
|
import { GetResource } from '../utils/Fetcher'
|
||||||
|
|
||||||
// Helper to check if we have a valid token
|
// Helper to check if we have a valid token
|
||||||
@@ -13,10 +15,13 @@ const isTokenValid = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const useResource = () => {
|
export const useResource = () => {
|
||||||
const { data, isLoading, error, refetch } = useQuery({
|
const { data, error, isLoading, refetch } = useQuery({
|
||||||
queryKey: ['resource'],
|
queryKey: ['resource'],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const response = await GetResource()
|
const response = await GetResource()
|
||||||
|
// The backend only names its build here, so this is also where crash
|
||||||
|
// reports learn which server version the user was talking to.
|
||||||
|
setServerVersion(response?.api_version, response?.api_commit)
|
||||||
return response
|
return response
|
||||||
},
|
},
|
||||||
staleTime: 6 * 60 * 60 * 1000, // 6 hours in milliseconds
|
staleTime: 6 * 60 * 60 * 1000, // 6 hours in milliseconds
|
||||||
|
|||||||
@@ -5,7 +5,8 @@
|
|||||||
*
|
*
|
||||||
* Deliberately dependency-free — ApiClient imports it on the request path, so
|
* Deliberately dependency-free — ApiClient imports it on the request path, so
|
||||||
* anything imported here would risk a module cycle. Everything is in memory
|
* anything imported here would risk a module cycle. Everything is in memory
|
||||||
* and dies with the tab; nothing is persisted.
|
* and dies with the tab, except the server build, which is remembered across
|
||||||
|
* launches so a crash before the first API answer still names the backend.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const SESSION_STARTED_AT = Date.now()
|
const SESSION_STARTED_AT = Date.now()
|
||||||
@@ -25,6 +26,7 @@ const routeTrail = []
|
|||||||
const apiFailures = []
|
const apiFailures = []
|
||||||
let backgroundedCount = 0
|
let backgroundedCount = 0
|
||||||
let serverVersion = null
|
let serverVersion = null
|
||||||
|
let serverCommit = null
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// Route trail
|
// Route trail
|
||||||
@@ -67,6 +69,33 @@ export const getPreviousRoute = () =>
|
|||||||
// Server identity
|
// Server identity
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const SERVER_BUILD_KEY = 'diagnostics_server_build'
|
||||||
|
|
||||||
|
// A crash on cold start happens before /resource has answered, and that is
|
||||||
|
// exactly when knowing which backend the user is on matters most. Carrying the
|
||||||
|
// last known build across launches keeps the report from saying "not reported".
|
||||||
|
try {
|
||||||
|
const cached = JSON.parse(localStorage.getItem(SERVER_BUILD_KEY) || 'null')
|
||||||
|
serverVersion = cached?.version ?? null
|
||||||
|
serverCommit = cached?.commit ?? null
|
||||||
|
} catch {
|
||||||
|
// corrupt or unavailable storage just means we start without a known build
|
||||||
|
}
|
||||||
|
|
||||||
|
const rememberServerBuild = (version, commit) => {
|
||||||
|
if (!version && !commit) return
|
||||||
|
serverVersion = version || serverVersion
|
||||||
|
serverCommit = commit || serverCommit
|
||||||
|
try {
|
||||||
|
localStorage.setItem(
|
||||||
|
SERVER_BUILD_KEY,
|
||||||
|
JSON.stringify({ version: serverVersion, commit: serverCommit }),
|
||||||
|
)
|
||||||
|
} catch {
|
||||||
|
// storage full or blocked; the in-memory copy still serves this session
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Picks the server build out of response headers. Costs nothing when the
|
* Picks the server build out of response headers. Costs nothing when the
|
||||||
* server doesn't send them — the field simply stays null.
|
* server doesn't send them — the field simply stays null.
|
||||||
@@ -74,21 +103,24 @@ export const getPreviousRoute = () =>
|
|||||||
export const recordServerVersionFromResponse = response => {
|
export const recordServerVersionFromResponse = response => {
|
||||||
if (serverVersion) return
|
if (serverVersion) return
|
||||||
try {
|
try {
|
||||||
serverVersion =
|
rememberServerBuild(
|
||||||
response?.headers?.get?.('x-donetick-version') ||
|
response?.headers?.get?.('x-donetick-version') ||
|
||||||
response?.headers?.get?.('x-api-version') ||
|
response?.headers?.get?.('x-api-version'),
|
||||||
null
|
null,
|
||||||
|
)
|
||||||
} catch {
|
} catch {
|
||||||
// headers may be inaccessible on opaque responses; not worth reporting
|
// headers may be inaccessible on opaque responses; not worth reporting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setServerVersion = version => {
|
/** Authoritative source: what /resource reports about the backend build. */
|
||||||
if (version) serverVersion = version
|
export const setServerVersion = (version, commit) =>
|
||||||
}
|
rememberServerBuild(version, commit)
|
||||||
|
|
||||||
export const getServerVersion = () => serverVersion
|
export const getServerVersion = () => serverVersion
|
||||||
|
|
||||||
|
export const getServerCommit = () => serverCommit
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// API failures
|
// API failures
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -176,6 +208,7 @@ export const getSessionDiagnostics = async () => {
|
|||||||
navigationType: NAVIGATION_TYPE,
|
navigationType: NAVIGATION_TYPE,
|
||||||
backgroundedCount,
|
backgroundedCount,
|
||||||
serverVersion,
|
serverVersion,
|
||||||
|
serverCommit,
|
||||||
previousRoute: getPreviousRoute(),
|
previousRoute: getPreviousRoute(),
|
||||||
routeTrail: getRouteTrail(),
|
routeTrail: getRouteTrail(),
|
||||||
apiFailures: getApiFailures(),
|
apiFailures: getApiFailures(),
|
||||||
|
|||||||
@@ -153,7 +153,9 @@ export const formatErrorReport = report => {
|
|||||||
session.previousRoute ? `Came from: ${session.previousRoute}` : null,
|
session.previousRoute ? `Came from: ${session.previousRoute}` : null,
|
||||||
'',
|
'',
|
||||||
`App: ${app.appVersion} · ${app.platform}${app.isNative ? ' (native)' : ''}`,
|
`App: ${app.appVersion} · ${app.platform}${app.isNative ? ' (native)' : ''}`,
|
||||||
`Server: ${session.serverVersion ?? 'not reported'}`,
|
`Server: ${session.serverVersion ?? 'not reported'}${
|
||||||
|
session.serverCommit ? ` (${session.serverCommit.slice(0, 8)})` : ''
|
||||||
|
}`,
|
||||||
`Session: ${formatDuration(session.sessionDurationMs)} active · ${
|
`Session: ${formatDuration(session.sessionDurationMs)} active · ${
|
||||||
session.navigationType ?? 'unknown'
|
session.navigationType ?? 'unknown'
|
||||||
} start · backgrounded ${session.backgroundedCount ?? 0}×`,
|
} start · backgrounded ${session.backgroundedCount ?? 0}×`,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { Browser } from '@capacitor/browser'
|
import { Browser } from '@capacitor/browser'
|
||||||
import { Capacitor } from '@capacitor/core'
|
import { Capacitor } from '@capacitor/core'
|
||||||
import {
|
import {
|
||||||
BugReportRounded,
|
|
||||||
CheckRounded,
|
CheckRounded,
|
||||||
ContentCopyRounded,
|
ContentCopyRounded,
|
||||||
ExpandMoreRounded,
|
ExpandMoreRounded,
|
||||||
GitHub,
|
GitHub,
|
||||||
|
ReportProblemRounded,
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -165,7 +165,7 @@ const ErrorReportModal = ({ error, errorInfo, onClose, open }) => {
|
|||||||
<Stack spacing={2}>
|
<Stack spacing={2}>
|
||||||
<Box sx={{ ...enter(0) }}>
|
<Box sx={{ ...enter(0) }}>
|
||||||
<IconHalo
|
<IconHalo
|
||||||
icon={<BugReportRounded />}
|
icon={<ReportProblemRounded />}
|
||||||
color={isBugReport ? 'warning' : 'danger'}
|
color={isBugReport ? 'warning' : 'danger'}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
@@ -175,7 +175,7 @@ const ErrorReportModal = ({ error, errorInfo, onClose, open }) => {
|
|||||||
level='h4'
|
level='h4'
|
||||||
sx={{ fontWeight: 700, letterSpacing: '-0.01em' }}
|
sx={{ fontWeight: 700, letterSpacing: '-0.01em' }}
|
||||||
>
|
>
|
||||||
{isBugReport ? 'Report a bug' : 'Report this problem'}
|
{isBugReport ? 'Report an issue' : 'Report this problem'}
|
||||||
</Typography>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
level='body-sm'
|
level='body-sm'
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import { BugReport, ChevronRight, Feedback, Star } from '@mui/icons-material'
|
import {
|
||||||
|
ChevronRight,
|
||||||
|
Feedback,
|
||||||
|
ReportProblem,
|
||||||
|
Star,
|
||||||
|
} from '@mui/icons-material'
|
||||||
import {
|
import {
|
||||||
Avatar,
|
Avatar,
|
||||||
Box,
|
Box,
|
||||||
@@ -52,7 +57,7 @@ const SettingsOverview = () => {
|
|||||||
id: 'bugreport',
|
id: 'bugreport',
|
||||||
title: t('overview.sections.bugReport.title'),
|
title: t('overview.sections.bugReport.title'),
|
||||||
description: t('overview.sections.bugReport.description'),
|
description: t('overview.sections.bugReport.description'),
|
||||||
icon: <BugReport />,
|
icon: <ReportProblem />,
|
||||||
onSelect: () => setBugReportOpen(true),
|
onSelect: () => setBugReportOpen(true),
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import { Capacitor } from '@capacitor/core'
|
|||||||
import {
|
import {
|
||||||
Archive,
|
Archive,
|
||||||
ArrowBack,
|
ArrowBack,
|
||||||
BugReport,
|
|
||||||
FilterAlt,
|
FilterAlt,
|
||||||
FolderOpen,
|
FolderOpen,
|
||||||
History,
|
History,
|
||||||
@@ -10,6 +9,7 @@ import {
|
|||||||
ListAlt,
|
ListAlt,
|
||||||
Logout,
|
Logout,
|
||||||
MenuRounded,
|
MenuRounded,
|
||||||
|
ReportProblem,
|
||||||
SearchRounded,
|
SearchRounded,
|
||||||
SettingsOutlined,
|
SettingsOutlined,
|
||||||
Toll,
|
Toll,
|
||||||
@@ -305,7 +305,7 @@ const NavBar = () => {
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ListItemDecorator>
|
<ListItemDecorator>
|
||||||
<BugReport />
|
<ReportProblem />
|
||||||
</ListItemDecorator>
|
</ListItemDecorator>
|
||||||
<ListItemContent>{t('navigation.reportBug')}</ListItemContent>
|
<ListItemContent>{t('navigation.reportBug')}</ListItemContent>
|
||||||
</ListItemButton>
|
</ListItemButton>
|
||||||
|
|||||||
Reference in New Issue
Block a user