diff --git a/lib/components/debug-logs-context-provider.react.js b/lib/components/debug-logs-context-provider.react.js --- a/lib/components/debug-logs-context-provider.react.js +++ b/lib/components/debug-logs-context-provider.react.js @@ -5,6 +5,7 @@ import { type DebugLog, DebugLogsContext, + defaultLosFilter, type LogType, } from './debug-logs-context.js'; import { useIsCurrentUserStaff } from '../shared/staff-utils.js'; @@ -16,6 +17,8 @@ function DebugLogsContextProvider(props: Props): React.Node { const [logs, setLogs] = React.useState<$ReadOnlyArray>([]); + const [logsFilter, setLogsFilter] = + React.useState<$ReadOnlyMap>(defaultLosFilter); const isCurrentUserStaff = useIsCurrentUserStaff(); const addLog = React.useCallback( @@ -39,15 +42,40 @@ [isCurrentUserStaff], ); - const clearLogs = React.useCallback(() => setLogs([]), []); + const isLogEnabled = React.useCallback( + (log: DebugLog) => log.logTypes.find(logType => logsFilter.get(logType)), + [logsFilter], + ); + + const clearLogs = React.useCallback( + () => setLogs(prev => prev.filter(log => !isLogEnabled(log))), + [isLogEnabled], + ); + + const filteredLogs = React.useMemo( + () => logs.filter(isLogEnabled), + [isLogEnabled, logs], + ); + + const setFilter = React.useCallback( + (logType: LogType, value: boolean) => + setLogsFilter(prev => { + const newFilters = new Map(prev); + newFilters.set(logType, value); + return newFilters; + }), + [], + ); const contextValue = React.useMemo( () => ({ - logs, + logsFilter, + logs: filteredLogs, addLog, clearLogs, + setFilter, }), - [addLog, clearLogs, logs], + [addLog, clearLogs, filteredLogs, logsFilter, setFilter], ); return ( diff --git a/lib/components/debug-logs-context.js b/lib/components/debug-logs-context.js --- a/lib/components/debug-logs-context.js +++ b/lib/components/debug-logs-context.js @@ -23,21 +23,29 @@ +logTypes: $ReadOnlyArray, }; +const defaultLosFilter: $ReadOnlyMap = new Map([ + [logTypes.ERROR, true], +]); + export type DebugLogsContextType = { +logs: $ReadOnlyArray, + +logsFilter: $ReadOnlyMap, +addLog: ( title: string, message: string, logTypes: $ReadOnlyArray, ) => mixed, +clearLogs: () => mixed, + +setFilter: (logType: LogType, value: boolean) => mixed, }; const DebugLogsContext: React.Context = React.createContext({ + logsFilter: defaultLosFilter, logs: [], addLog: () => {}, clearLogs: () => {}, + setFilter: () => {}, }); function useDebugLogs(): DebugLogsContextType { @@ -139,4 +147,10 @@ ); } -export { DebugLogsContext, useDebugLogs, useOlmDebugLogs, logTypes }; +export { + DebugLogsContext, + useDebugLogs, + useOlmDebugLogs, + logTypes, + defaultLosFilter, +}; diff --git a/native/profile/debug-logs-screen.react.js b/native/profile/debug-logs-screen.react.js --- a/native/profile/debug-logs-screen.react.js +++ b/native/profile/debug-logs-screen.react.js @@ -2,12 +2,15 @@ import Clipboard from '@react-native-clipboard/clipboard'; import * as React from 'react'; -import { FlatList, View, Text } from 'react-native'; +import { FlatList, View, Text, Switch } from 'react-native'; import { useDebugLogs, type DebugLog, + logTypes, + LogType, } from 'lib/components/debug-logs-context.js'; +import { values } from 'lib/utils/objects.js'; import type { ProfileNavigationProp } from './profile.react.js'; import PrimaryButton from '../components/primary-button.react.js'; @@ -21,7 +24,7 @@ // eslint-disable-next-line no-unused-vars function DebugLogsScreen(props: Props): React.Node { - const { logs, clearLogs } = useDebugLogs(); + const { logs, clearLogs, logsFilter, setFilter } = useDebugLogs(); const copyLogs = React.useCallback(() => { Clipboard.setString(JSON.stringify(logs, null, 2)); @@ -44,11 +47,39 @@ [styles.item, styles.message, styles.timestamp, styles.title], ); + const toggleLogsFilter = React.useCallback( + (logType: LogType) => setFilter(logType, !logsFilter.get(logType)), + [logsFilter, setFilter], + ); + + const logTypesList = React.useMemo( + () => + values(logTypes).map(logType => ( + + {logType} + toggleLogsFilter(logType)} + /> + + )), + [logsFilter, styles.submenuButton, styles.submenuText, toggleLogsFilter], + ); + return ( + {logTypesList} - - + + ); } @@ -77,6 +108,17 @@ message: { color: 'panelForegroundSecondaryLabel', }, + submenuButton: { + flexDirection: 'row', + paddingHorizontal: 10, + paddingVertical: 2, + alignItems: 'center', + }, + submenuText: { + color: 'panelForegroundLabel', + flex: 1, + fontSize: 16, + }, }; export default DebugLogsScreen; diff --git a/web/settings/debug-logs-modal.react.js b/web/settings/debug-logs-modal.react.js --- a/web/settings/debug-logs-modal.react.js +++ b/web/settings/debug-logs-modal.react.js @@ -2,15 +2,18 @@ import * as React from 'react'; -import { useDebugLogs } from 'lib/components/debug-logs-context.js'; +import { logTypes, useDebugLogs } from 'lib/components/debug-logs-context.js'; +import type { LogType } from 'lib/components/debug-logs-context.js'; import { useModalContext } from 'lib/components/modal-provider.react.js'; +import { values } from 'lib/utils/objects.js'; import css from './debug-logs-modal.css'; import Button, { buttonThemes } from '../components/button.react.js'; +import EnumSettingsOption from '../components/enum-settings-option.react.js'; import Modal from '../modals/modal.react.js'; function DebugLogsModal(): React.Node { - const { logs, clearLogs } = useDebugLogs(); + const { logs, clearLogs, logsFilter, setFilter } = useDebugLogs(); const { popModal } = useModalContext(); const messageList = React.useMemo( @@ -33,13 +36,35 @@ await navigator.clipboard.writeText(JSON.stringify(logs, null, 2)); }, [logs]); + const toggleLogsFilter = React.useCallback( + (logType: LogType) => setFilter(logType, !logsFilter.get(logType)), + [logsFilter, setFilter], + ); + + const logTypesList = React.useMemo( + () => + values(logTypes).map(logType => ( + toggleLogsFilter(logType)} + icon={null} + title={logType} + type="checkbox" + statements={[{ statement: logType }]} + /> + )), + [logsFilter, toggleLogsFilter], + ); + return (
+
{logTypesList}
{messageList}