146 lines
		
	
	
	
		
			4.3 KiB
			
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			146 lines
		
	
	
	
		
			4.3 KiB
			
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
// Copyright 2021 Signal Messenger, LLC
 | 
						|
// SPDX-License-Identifier: AGPL-3.0-only
 | 
						|
 | 
						|
import React, { useState } from 'react';
 | 
						|
import classNames from 'classnames';
 | 
						|
import lodash from 'lodash';
 | 
						|
import { Button, ButtonVariant } from './Button.js';
 | 
						|
import type { LocalizerType } from '../types/Util.js';
 | 
						|
import { Modal } from './Modal.js';
 | 
						|
import type { PresentedSource, PresentableSource } from '../types/Calling.js';
 | 
						|
import { Theme } from '../util/theme.js';
 | 
						|
import { strictAssert } from '../util/assert.js';
 | 
						|
 | 
						|
const { groupBy } = lodash;
 | 
						|
 | 
						|
export type PropsType = {
 | 
						|
  i18n: LocalizerType;
 | 
						|
  presentingSourcesAvailable: ReadonlyArray<PresentableSource>;
 | 
						|
  selectPresentingSource: (id: string) => void;
 | 
						|
  cancelPresenting: () => void;
 | 
						|
};
 | 
						|
 | 
						|
function Source({
 | 
						|
  onSourceClick,
 | 
						|
  source,
 | 
						|
  sourceToPresent,
 | 
						|
}: {
 | 
						|
  onSourceClick: (source: PresentedSource) => void;
 | 
						|
  source: PresentableSource;
 | 
						|
  sourceToPresent?: PresentedSource;
 | 
						|
}): JSX.Element {
 | 
						|
  return (
 | 
						|
    <button
 | 
						|
      className={classNames({
 | 
						|
        'module-CallingSelectPresentingSourcesModal__source': true,
 | 
						|
        'module-CallingSelectPresentingSourcesModal__source--selected':
 | 
						|
          sourceToPresent?.id === source.id,
 | 
						|
      })}
 | 
						|
      key={source.id}
 | 
						|
      onClick={() => {
 | 
						|
        onSourceClick({
 | 
						|
          id: source.id,
 | 
						|
          name: source.name,
 | 
						|
        });
 | 
						|
      }}
 | 
						|
      type="button"
 | 
						|
    >
 | 
						|
      <img
 | 
						|
        alt={source.name}
 | 
						|
        className="module-CallingSelectPresentingSourcesModal__name--screenshot"
 | 
						|
        src={source.thumbnail}
 | 
						|
      />
 | 
						|
      <div className="module-CallingSelectPresentingSourcesModal__name--container">
 | 
						|
        {source.appIcon ? (
 | 
						|
          <img
 | 
						|
            alt={source.name}
 | 
						|
            className="module-CallingSelectPresentingSourcesModal__name--icon"
 | 
						|
            height={16}
 | 
						|
            src={source.appIcon}
 | 
						|
            width={16}
 | 
						|
          />
 | 
						|
        ) : null}
 | 
						|
        <span className="module-CallingSelectPresentingSourcesModal__name--text">
 | 
						|
          {source.name}
 | 
						|
        </span>
 | 
						|
      </div>
 | 
						|
    </button>
 | 
						|
  );
 | 
						|
}
 | 
						|
 | 
						|
export function CallingSelectPresentingSourcesModal({
 | 
						|
  i18n,
 | 
						|
  presentingSourcesAvailable,
 | 
						|
  selectPresentingSource,
 | 
						|
  cancelPresenting,
 | 
						|
}: PropsType): JSX.Element | null {
 | 
						|
  const [sourceToPresent, setSourceToPresent] = useState<
 | 
						|
    PresentedSource | undefined
 | 
						|
  >(undefined);
 | 
						|
 | 
						|
  if (!presentingSourcesAvailable.length) {
 | 
						|
    throw new Error('No sources available for presenting');
 | 
						|
  }
 | 
						|
 | 
						|
  const sources = groupBy(
 | 
						|
    presentingSourcesAvailable,
 | 
						|
    source => source.isScreen
 | 
						|
  );
 | 
						|
 | 
						|
  const footer = (
 | 
						|
    <>
 | 
						|
      <Button onClick={cancelPresenting} variant={ButtonVariant.Secondary}>
 | 
						|
        {i18n('icu:cancel')}
 | 
						|
      </Button>
 | 
						|
      <Button
 | 
						|
        disabled={!sourceToPresent}
 | 
						|
        onClick={() => {
 | 
						|
          strictAssert(sourceToPresent, 'No source to present');
 | 
						|
          selectPresentingSource(sourceToPresent.id);
 | 
						|
        }}
 | 
						|
      >
 | 
						|
        {i18n('icu:calling__SelectPresentingSourcesModal--confirm')}
 | 
						|
      </Button>
 | 
						|
    </>
 | 
						|
  );
 | 
						|
 | 
						|
  return (
 | 
						|
    <Modal
 | 
						|
      modalName="CallingSelectPresentingSourcesModal"
 | 
						|
      hasXButton
 | 
						|
      i18n={i18n}
 | 
						|
      moduleClassName="module-CallingSelectPresentingSourcesModal"
 | 
						|
      onClose={cancelPresenting}
 | 
						|
      theme={Theme.Dark}
 | 
						|
      title={i18n('icu:calling__SelectPresentingSourcesModal--title')}
 | 
						|
      modalFooter={footer}
 | 
						|
    >
 | 
						|
      <div className="module-CallingSelectPresentingSourcesModal__title">
 | 
						|
        {i18n('icu:calling__SelectPresentingSourcesModal--entireScreen')}
 | 
						|
      </div>
 | 
						|
      <div className="module-CallingSelectPresentingSourcesModal__sources">
 | 
						|
        {(sources.true ?? []).map(source => (
 | 
						|
          <Source
 | 
						|
            key={source.id}
 | 
						|
            onSourceClick={selectedSource => setSourceToPresent(selectedSource)}
 | 
						|
            source={source}
 | 
						|
            sourceToPresent={sourceToPresent}
 | 
						|
          />
 | 
						|
        ))}
 | 
						|
      </div>
 | 
						|
      <div className="module-CallingSelectPresentingSourcesModal__title">
 | 
						|
        {i18n('icu:calling__SelectPresentingSourcesModal--window')}
 | 
						|
      </div>
 | 
						|
      <div className="module-CallingSelectPresentingSourcesModal__sources">
 | 
						|
        {(sources.false ?? []).map(source => (
 | 
						|
          <Source
 | 
						|
            key={source.id}
 | 
						|
            onSourceClick={selectedSource => setSourceToPresent(selectedSource)}
 | 
						|
            source={source}
 | 
						|
            sourceToPresent={sourceToPresent}
 | 
						|
          />
 | 
						|
        ))}
 | 
						|
      </div>
 | 
						|
    </Modal>
 | 
						|
  );
 | 
						|
}
 |