aboutsummaryrefslogblamecommitdiff
path: root/src/components/App.js
blob: 4688bbb2892b95b5df7358b750823fa905e61cf9 (plain) (tree)
































































































                                                                                                                                                                                                                                                                                                                
import { useEffect, useState } from 'react';
import { CopyTwoTone, PlusCircleFilled, ClearOutlined } from '@ant-design/icons';
import { Button, Card, Col, Form, Input, Layout, message, Popconfirm, Row, Switch } from 'antd';

import useInterval from '../hooks/interval';
import useLocalStorage from '../hooks/local';
import { copyTextToClipboard } from '../utils/clipboard';
import { configFromLink } from '../utils/configFromLink';
import { defaultTimer } from '../utils/timerTemplate';
import { updateTimers } from '../utils/updateTimers';

import Timers from './Timers';

const { Footer } = Layout;

function App() {
  const [browserNotifications, setNotifications] = useLocalStorage("notifications", false); // доступны ли нативные уведомления
  const [sound, setSound] = useLocalStorage("sound", true); // включен ли звук
  const [timers, setTimers] = useLocalStorage("timers", configFromLink()); // хранилище таймеров
  const [url, setUrl] = useState(""); // генерируемая ссылка на кофигурацию

  // Действия при открытии приложения:
  // 1. Инициализация конфигурации из адреса
  // 2. Запрос разрешений на уведомления
  useEffect(() => {
    if (window.location.hash !== "") {
      setTimers(configFromLink());
      window.location.hash = "";
    }
    Notification.requestPermission().then((result) => {
      if (result === 'granted') {
        setNotifications(true);
      } else {
        setNotifications(false);
      }
    });
    // eslint-disable-next-line
  }, []);

  // Формирование ссылки на конфигурацию при обновлении таймеров
  useEffect(() => {
    const d = timers.map(t => [t.name, t.initialTime]);
    setUrl(btoa(unescape(encodeURIComponent(JSON.stringify(d)))));
  }, [timers]);

  // Обновление таймеров
  useInterval(updateTimers.bind(this, timers, setTimers, sound, browserNotifications), 100, true); // интервал опроса таймеров - 100мс потому что если вкладка с таймерами в фоне, браузер начинает заметлять работу js

  return <Layout>
    <Row>
      <Col xs={24} sm={12} md={12} lg={8} xl={6}>
        <Card
          title={<strong><img alt="Мультитаймер" src="/logo192.png" style={{ height: 32, width: 32 }} />&nbsp;Мультитаймер</strong>}
          bodyStyle={{ padding: 4 }}
          style={{ padding: 4, margin: 4 }}
        >
          <Form layout='vertical'>
            <Form.Item>
              <Button type={'primary'} block onClick={() => setTimers([...timers, { ...defaultTimer }])} icon={<PlusCircleFilled />}>Добавить таймер</Button>
            </Form.Item>
            <Form.Item label="Звук таймера">
              <Switch checkedChildren="Включен" unCheckedChildren="Отключен" checked={sound} onChange={x => setSound(x)} />
            </Form.Item>
            <Form.Item label="Ссылка на конфигурацию">
              <Input
                readOnly
                value={`https://timer.neonxp.dev/#${url}`}
                addonAfter={<CopyTwoTone onClick={() => {
                  copyTextToClipboard(`https://timer.neonxp.dev/#${url}`)
                  message.success("Скопировано в буфер обмена")
                }} />}
              />
            </Form.Item>
            <Form.Item>
              <Popconfirm
                title="Очистить таймеры?"
                onConfirm={() => setTimers([])}
                okText="Да"
                cancelText="Нет">
                <Button type={'ghost'} block icon={<ClearOutlined />}>Очистить</Button>
              </Popconfirm>
            </Form.Item>
          </Form>
        </Card>
      </Col>
      <Timers
        items={timers}
        setTimers={setTimers}
      />
    </Row>
    <Footer>
      Сделал в 2022г <a href="https://neonxp.dev/">Александр NeonXP Кирюхин</a>.
    </Footer>
  </Layout>;
}

export default App;