You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

reducer.ts 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. import { getURLWithoutParamsNormalized } from '../base/connection/utils';
  2. import PersistenceRegistry from '../base/redux/PersistenceRegistry';
  3. import ReducerRegistry from '../base/redux/ReducerRegistry';
  4. import {
  5. DELETE_RECENT_LIST_ENTRY,
  6. _STORE_CURRENT_CONFERENCE,
  7. _UPDATE_CONFERENCE_DURATION
  8. } from './actionTypes';
  9. import { isRecentListEnabled } from './functions';
  10. interface IRecent {
  11. conference: string;
  12. date: number;
  13. duration: number;
  14. }
  15. export type IRecentListState = IRecent[];
  16. /**
  17. * The default/initial redux state of the feature {@code recent-list}.
  18. *
  19. * @type {IRecentListState}
  20. */
  21. const DEFAULT_STATE: IRecentListState = [];
  22. /**
  23. * The max size of the list.
  24. *
  25. * @type {number}
  26. */
  27. export const MAX_LIST_SIZE = 30;
  28. /**
  29. * The redux subtree of this feature.
  30. */
  31. const STORE_NAME = 'features/recent-list';
  32. /**
  33. * Sets up the persistence of the feature {@code recent-list}.
  34. */
  35. PersistenceRegistry.register(STORE_NAME);
  36. /**
  37. * Reduces redux actions for the purposes of the feature {@code recent-list}.
  38. */
  39. ReducerRegistry.register<IRecentListState>(STORE_NAME, (state = DEFAULT_STATE, action): IRecentListState => {
  40. if (isRecentListEnabled()) {
  41. switch (action.type) {
  42. case DELETE_RECENT_LIST_ENTRY:
  43. return _deleteRecentListEntry(state, action.entryId);
  44. case _STORE_CURRENT_CONFERENCE:
  45. return _storeCurrentConference(state, action);
  46. case _UPDATE_CONFERENCE_DURATION:
  47. return _updateConferenceDuration(state, action);
  48. default:
  49. return state;
  50. }
  51. }
  52. return state;
  53. });
  54. /**
  55. * Deletes a recent list entry based on the url and date of the item.
  56. *
  57. * @param {IRecentListState} state - The Redux state.
  58. * @param {Object} entryId - The ID object of the entry.
  59. * @returns {IRecentListState}
  60. */
  61. function _deleteRecentListEntry(
  62. state: Array<IRecent>, entryId: { date: number; url: string; }): Array<IRecent> {
  63. return state.filter(entry =>
  64. entry.conference !== entryId.url || entry.date !== entryId.date);
  65. }
  66. /**
  67. * Adds a new list entry to the redux store.
  68. *
  69. * @param {IRecentListState} state - The redux state of the feature {@code recent-list}.
  70. * @param {Object} action - The redux action.
  71. * @returns {Object}
  72. */
  73. function _storeCurrentConference(state: IRecentListState, { locationURL }: { locationURL: { href: string; }; }) {
  74. const conference = locationURL.href;
  75. // If the current conference is already in the list, we remove it to re-add
  76. // it to the top.
  77. const nextState
  78. = state.filter(e => !_urlStringEquals(e.conference, conference));
  79. // The list is a reverse-sorted (i.e. the newer elements are at the end).
  80. nextState.push({
  81. conference,
  82. date: Date.now(),
  83. duration: 0 // We don't have the duration yet!
  84. });
  85. // Ensure the list doesn't exceed a/the maximum size.
  86. nextState.splice(0, nextState.length - MAX_LIST_SIZE);
  87. return nextState;
  88. }
  89. /**
  90. * Updates the conference length when left.
  91. *
  92. * @param {IRecentListState} state - The redux state of the feature {@code recent-list}.
  93. * @param {Object} action - The redux action.
  94. * @returns {Object} The next redux state of the feature {@code recent-list}.
  95. */
  96. function _updateConferenceDuration(state: IRecentListState, { locationURL }: { locationURL: { href: string; }; }) {
  97. if (locationURL?.href && state.length) {
  98. const mostRecentIndex = state.length - 1;
  99. const mostRecent = state[mostRecentIndex];
  100. if (_urlStringEquals(mostRecent.conference, locationURL.href)) {
  101. // The last conference start was stored so we need to update the
  102. // length.
  103. const nextMostRecent = {
  104. ...mostRecent,
  105. duration: Date.now() - mostRecent.date
  106. };
  107. // Shallow copy to avoid in-place modification.
  108. const nextState = state.slice();
  109. nextState[mostRecentIndex] = nextMostRecent;
  110. return nextState;
  111. }
  112. }
  113. return state;
  114. }
  115. /**
  116. * Determines whether two specific URL {@code strings} are equal in the sense
  117. * that they identify one and the same conference resource (irrespective of
  118. * time) for the purposes of the feature {@code recent-list}.
  119. *
  120. * @param {string} a - The URL {@code string} to test for equality to {@code b}.
  121. * @param {string} b - The URL {@code string} to test for equality to {@code a}.
  122. * @returns {boolean}
  123. */
  124. function _urlStringEquals(a: string, b: string) {
  125. const aHref = getURLWithoutParamsNormalized(new URL(a));
  126. const bHref = getURLWithoutParamsNormalized(new URL(b));
  127. return aHref === bHref;
  128. }