Files
upstream-jitsi-meet/react/features/calendar-sync/components/CalendarListContent.native.tsx
upstream-mirror 1d2bfcac0c
Some checks failed
Prosody Plugin Tests / test (push) Failing after 10m11s
Close stale issues and PRs / stale (push) Failing after 1s
snapshot of https://github.com/jitsi/jitsi-meet.git @ fd1749dd28f4138aa80234fc7f311b19aaffa0e7 (2026-09-15)
2026-09-15 15:56:32 +05:00

271 lines
7.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { Component } from 'react';
import { WithTranslation } from 'react-i18next';
import { connect } from 'react-redux';
import { createCalendarClickedEvent, createCalendarSelectedEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { appNavigate } from '../../app/actions.native';
import { IReduxState, IStore } from '../../app/types';
import { getLocalizedDateFormatter } from '../../base/i18n/dateUtil';
import { translate } from '../../base/i18n/functions';
import NavigateSectionList from '../../base/react/components/native/NavigateSectionList';
import { setCalendarTimerDuration } from '../../time-timer/actions';
import { computeCalendarTimerDuration } from '../../time-timer/functions';
import { openUpdateCalendarEventDialog, refreshCalendar } from '../actions.native';
/**
* The type of the React {@code Component} props of
* {@link CalendarListContent}.
*/
interface IProps extends WithTranslation {
/**
* The calendar event list.
*/
_eventList: Array<any>;
/**
* Indicates if the list is disabled or not.
*/
disabled: boolean;
/**
* The Redux dispatch function.
*/
dispatch: IStore['dispatch'];
/**
*
*/
listEmptyComponent: React.ReactElement<any> | null;
}
/**
* Component to display a list of events from a connected calendar.
*/
class CalendarListContent extends Component<IProps> {
/**
* Default values for the component's props.
*/
static defaultProps = {
_eventList: []
};
/**
* Initializes a new {@code CalendarListContent} instance.
*
* @inheritdoc
*/
constructor(props: IProps) {
super(props);
// Bind event handlers so they are only bound once per instance.
this._onPress = this._onPress.bind(this);
this._onRefresh = this._onRefresh.bind(this);
this._onSecondaryAction = this._onSecondaryAction.bind(this);
this._toDateString = this._toDateString.bind(this);
this._toDisplayableItem = this._toDisplayableItem.bind(this);
this._toDisplayableList = this._toDisplayableList.bind(this);
this._toTimeString = this._toTimeString.bind(this);
}
/**
* Implements React's {@link Component#componentDidMount()}. Invoked
* immediately after this component is mounted.
*
* @inheritdoc
* @returns {void}
*/
override componentDidMount() {
sendAnalytics(createCalendarSelectedEvent());
}
/**
* Implements React's {@link Component#render}.
*
* @inheritdoc
*/
override render() {
const { disabled, listEmptyComponent } = this.props;
return (
<NavigateSectionList
disabled = { disabled }
onPress = { this._onPress }
onRefresh = { this._onRefresh }
onSecondaryAction = { this._onSecondaryAction }
renderListEmptyComponent
= { listEmptyComponent }
sections = { this._toDisplayableList() } />
);
}
/**
* Handles the list's navigate action.
*
* @private
* @param {string} url - The url string to navigate to.
* @param {string} analyticsEventName - Тhe name of the analytics event
* associated with this action.
* @returns {void}
*/
_onPress(url: string, analyticsEventName = 'meeting.tile') {
sendAnalytics(createCalendarClickedEvent(analyticsEventName));
// Find the event being joined and, if it has a parseable start + end,
// record its duration for the time-timer keyed to this URL. The
// middleware only applies it when the joined conference matches that
// URL, so opening a meeting and bailing out cannot leak its duration
// into a different meeting joined afterwards.
const event = this.props._eventList?.find((e: any) => e.url === url);
const duration = computeCalendarTimerDuration(event);
if (duration) {
this.props.dispatch(setCalendarTimerDuration(
duration.durationSeconds,
{
startTimeUnix: duration.startTimeUnix,
url
}));
} else {
this.props.dispatch(setCalendarTimerDuration(undefined));
}
this.props.dispatch(appNavigate(url));
}
/**
* Callback to execute when the list is doing a pull-to-refresh.
*
* @private
* @returns {void}
*/
_onRefresh() {
this.props.dispatch(refreshCalendar(true));
}
/**
* Handles the list's secondary action.
*
* @private
* @param {string} id - The ID of the item on which the secondary action was
* performed.
* @returns {void}
*/
_onSecondaryAction(id: string) {
this.props.dispatch(openUpdateCalendarEventDialog(id));
}
/**
* Generates a date string for a given event.
*
* @param {Object} event - The event.
* @private
* @returns {string}
*/
_toDateString(event: any) {
const startDateTime
= getLocalizedDateFormatter(event.startDate).format('MMM Do, YYYY');
return `${startDateTime}`;
}
/**
* Creates a displayable object from an event.
*
* @param {Object} event - The calendar event.
* @private
* @returns {Object}
*/
_toDisplayableItem(event: any) {
return {
id: event.id,
key: `${event.id}-${event.startDate}`,
lines: [
event.url,
this._toTimeString(event)
],
title: event.title,
url: event.url
};
}
/**
* Transforms the event list to a displayable list with sections.
*
* @private
* @returns {Array<Object>}
*/
_toDisplayableList() {
const { _eventList, t } = this.props;
const now = new Date();
const { createSection } = NavigateSectionList;
const TODAY_SECTION = 'today';
const sectionMap = new Map();
for (const event of _eventList) {
const displayableEvent = this._toDisplayableItem(event);
const startDate = new Date(event.startDate).getDate();
if (startDate === now.getDate()) {
let todaySection = sectionMap.get(TODAY_SECTION);
if (!todaySection) {
todaySection
= createSection(t('calendarSync.today'), TODAY_SECTION);
sectionMap.set(TODAY_SECTION, todaySection);
}
todaySection.data.push(displayableEvent);
} else if (sectionMap.has(startDate)) {
const section = sectionMap.get(startDate);
if (section) {
section.data.push(displayableEvent);
}
} else {
const newSection
= createSection(this._toDateString(event), startDate);
sectionMap.set(startDate, newSection);
newSection.data.push(displayableEvent);
}
}
return Array.from(sectionMap.values());
}
/**
* Generates a time (interval) string for a given event.
*
* @param {Object} event - The event.
* @private
* @returns {string}
*/
_toTimeString(event: any) {
const startDateTime
= getLocalizedDateFormatter(event.startDate).format('lll');
const endTime
= getLocalizedDateFormatter(event.endDate).format('LT');
return `${startDateTime} - ${endTime}`;
}
}
/**
* Maps redux state to component props.
*
* @param {Object} state - The redux state.
* @returns {IProps}
*/
function _mapStateToProps(state: IReduxState) {
return {
_eventList: state['features/calendar-sync'].events
};
}
export default translate(connect(_mapStateToProps)(CalendarListContent));