tracePreferences.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import * as Sentry from '@sentry/react';
  2. import clamp from 'sentry/utils/number/clamp';
  3. import {traceReducerExhaustiveActionCheck} from 'sentry/views/performance/newTraceDetails/traceState';
  4. type TraceLayoutPreferences = 'drawer left' | 'drawer bottom' | 'drawer right';
  5. type TracePreferencesAction =
  6. | {payload: TraceLayoutPreferences; type: 'set layout'}
  7. | {
  8. payload: number;
  9. type: 'set drawer dimension';
  10. }
  11. | {payload: number; type: 'set list width'}
  12. | {payload: boolean; type: 'minimize drawer'};
  13. type TraceDrawerPreferences = {
  14. layoutOptions: TraceLayoutPreferences[];
  15. minimized: boolean;
  16. sizes: {
  17. [key in TraceLayoutPreferences]: number;
  18. };
  19. };
  20. export type TracePreferencesState = {
  21. drawer: TraceDrawerPreferences;
  22. layout: TraceLayoutPreferences;
  23. list: {
  24. width: number;
  25. };
  26. };
  27. export const TRACE_DRAWER_DEFAULT_SIZES: TraceDrawerPreferences['sizes'] = {
  28. 'drawer left': 0.33,
  29. 'drawer right': 0.33,
  30. 'drawer bottom': 0.5,
  31. };
  32. export const DEFAULT_TRACE_VIEW_PREFERENCES: TracePreferencesState = {
  33. drawer: {
  34. minimized: false,
  35. sizes: {
  36. 'drawer left': 0.33,
  37. 'drawer right': 0.33,
  38. 'drawer bottom': 0.5,
  39. },
  40. layoutOptions: ['drawer left', 'drawer right', 'drawer bottom'],
  41. },
  42. layout: 'drawer right',
  43. list: {
  44. width: 0.5,
  45. },
  46. };
  47. export function storeTraceViewPreferences(
  48. key: string,
  49. state: TracePreferencesState
  50. ): void {
  51. // Make sure we dont fire this during a render phase
  52. window.requestAnimationFrame(() => {
  53. try {
  54. localStorage.setItem(key, JSON.stringify(state));
  55. } catch (e) {
  56. Sentry.captureException(e);
  57. }
  58. });
  59. }
  60. function isInt(value: any): value is number {
  61. return typeof value === 'number' && !isNaN(value);
  62. }
  63. function correctListWidth(state: TracePreferencesState): TracePreferencesState {
  64. if (state.list.width < 0.1 || state.list.width > 0.9) {
  65. state.list.width = 0.5;
  66. }
  67. return state;
  68. }
  69. export function loadTraceViewPreferences(key: string): TracePreferencesState | null {
  70. const stored = localStorage.getItem(key);
  71. if (stored) {
  72. try {
  73. const parsed = JSON.parse(stored);
  74. // We need a more robust way to validate the stored preferences.
  75. // Since we dont have a schema validation lib, just do it manually for now.
  76. if (
  77. parsed?.drawer &&
  78. typeof parsed.drawer.minimized === 'boolean' &&
  79. Array.isArray(parsed.drawer.layoutOptions) &&
  80. parsed.drawer.sizes &&
  81. isInt(parsed.drawer.sizes['drawer left']) &&
  82. isInt(parsed.drawer.sizes['drawer right']) &&
  83. isInt(parsed.drawer.sizes['drawer bottom']) &&
  84. parsed.layout &&
  85. typeof parsed.layout === 'string' &&
  86. parsed.list &&
  87. isInt(parsed.list.width)
  88. ) {
  89. correctListWidth(parsed);
  90. return parsed;
  91. }
  92. } catch (e) {
  93. Sentry.captureException(e);
  94. }
  95. }
  96. return null;
  97. }
  98. export function tracePreferencesReducer(
  99. state: TracePreferencesState,
  100. action: TracePreferencesAction
  101. ): TracePreferencesState {
  102. switch (action.type) {
  103. case 'minimize drawer':
  104. return {...state, drawer: {...state.drawer, minimized: action.payload}};
  105. case 'set layout':
  106. return {
  107. ...state,
  108. layout: action.payload,
  109. drawer: {...state.drawer, minimized: false},
  110. };
  111. case 'set drawer dimension':
  112. return {
  113. ...state,
  114. drawer: {
  115. ...state.drawer,
  116. sizes: {
  117. ...state.drawer.sizes,
  118. [state.layout]: clamp(action.payload, 0, 1),
  119. },
  120. },
  121. };
  122. case 'set list width':
  123. return {
  124. ...state,
  125. list: {
  126. width: clamp(action.payload, 0.1, 0.9),
  127. },
  128. };
  129. default:
  130. traceReducerExhaustiveActionCheck(action);
  131. return state;
  132. }
  133. }