Создание приложения-напоминания с нуля

Electron - мощный инструмент для создания кроссплатформенных настольных приложений. В сочетании со Svelte вы можете создавать высокомасштабируемые реактивные приложения, используя только языки, которые вы уже знаете. Это HTML, CSS и JavaScript.

Сегодня мы проведем оба теста и создадим напоминание, которое будет рассылать push-уведомления. Вы сможете создавать, удалять или изменять их по своему вкусу. Это макет приложения:

Что такое Электрон?

Electron - изначально созданный для редактора Atom - это фреймворк с открытым исходным кодом, разработанный GitHub. Это позволяет нам создавать настольные приложения, используя только веб-технологии. Вы можете писать свои приложения на HTML, CSS и JavaScript. Это достигается путем объединения движка рендеринга Chromium с Node.js. По сути, у нас есть окно браузера, которое работает как отдельное приложение.

Настройка Svelte

Поскольку в основном мы будем работать в Svelte, а Electron будет только оболочкой, начнем с первого. После настройки мы позже настроим вокруг него Electron. Запустите npx degit sveltejs/template, чтобы инициализировать пустой проект Svelte. Затем запустите npm i, чтобы установить зависимости. Кроме того, вы также можете скачать zip-файл с GitHub.

Добавление поддержки SCSS

Пока здесь, давайте также добавим поддержку SCSS. Запустите npm i rollup-plugin-scss. Затем откройте файл конфигурации Rollup и добавьте эти две новые строки:

Настройка Electron

Следующим шагом будет настройка Electron. Запустите npm i electron, затем добавьте файл index.js в корневую папку проекта. Это будет входной файл для окна нашего приложения.

Важными частями здесь являются строка: 9 и строка: 14. Чтобы использовать узел внутри приложения, нам нужно включить nodeIntegration. В конце функции мы устанавливаем индексный файл так, чтобы он указывал на общедоступный каталог. Здесь Svelte сгенерирует для нас активы.

Чтобы начать работу, измените сценарий запуска в вашем package.json файле на следующий, затем запустите npm run start.

Откроется пустое окно. Ничего не появляется. Что-то сломано. Чтобы открыть DevTools, выберите «Просмотр» → «Переключить инструменты разработчика» или нажмите ctrl + shift + i.

Нам нужно изменить пути к активам. Перейдите в свой index.html файл и поставьте перед ними точку.

Если вы перезапустите приложение, вы должны увидеть «Hello World!» появление.

К сожалению, если мы внесем какие-то изменения, мы не увидим никаких обновлений. Это потому, что мы запустили только electronic, а не Svelte через Rollup. Это можно исправить, запустив npm run dev вместо npm run start.

Важно, чтобы у нас был elecron . в качестве стартового скрипта. Если вы посмотрите на свою конфигурацию Rollup, вы увидите, что в конце концов она порождает дочерний процесс. Он вызовет npm run start.

Таким образом, несмотря на то, что мы запускаем среду разработки Svelte по умолчанию с npm run dev, электрон также будет начинать с него. И это то, что мы ищем.

Однако, если вы попытаетесь внести какие-либо изменения, вам нужно будет перезапустить электрон, чтобы это увидеть. Давайте обойдем это, добавив горячую перезагрузку.

Добавление горячей перезагрузки

Установите модуль электронной перезагрузки, запустив npm i electron-reload. Внутри вашего index.js файла сразу после первой строки добавьте следующее:

Это будет перезагружать электрон при каждом изменении в папке проекта.

Настройка магазина

В нашем приложении мы разделим представление на два модуля. Боковая панель и главное окно. И все же мы хотим иметь один источник истины, только одно глобальное состояние. В Svelte, если вы хотите использовать одно и то же состояние для разных компонентов, вы можете использовать магазины среди других опций.

Хранилище - это не что иное, как объект с subscribe методом, который позволяет компонентам получать уведомления всякий раз, когда значение изменяется внутри хранилища. В корне папки src добавьте файл store.js со следующим содержимым:

Это создаст для нас доступное для записи хранилище с начальным значением пустого массива. Здесь мы будем хранить информацию о каждом уведомлении.

Создание боковой панели

Что касается компонентов, давайте начнем с боковой панели. Создайте папку components внутри папки src и создайте подпапку для sidebar. Внутри создайте новый компонент с именем sidebarComponent.svelte. Вы также можете сразу же импортировать его в App.svelte.

Кроме пяти вышеперечисленных строк, вы можете удалить все остальное. Перейдя к компоненту боковой панели, заполните его следующим содержимым:

Чтобы уменьшить количество скриптов в шаблоне, я создал контроллер рядом с компонентом. Я также добавил немного CSS и импортировал магазин. Вы можете видеть, что в цикле #each я добавил к переменной знак доллара. Это говорит Svelte рассматривать значение как реактивное и обновлять DOM всякий раз, когда в него вносятся изменения.

Для каждого сувенира мы хотим отобразить элемент списка. Как видите, если элемент активен, мы добавим активный класс в li. Это эквивалентно высказыванию:

Единственная цель метода selectMemento заключается в следующем: после щелчка по элементу он становится активным. С помощью некоторого CSS мы получили вот что:

Добавление контроллера боковой панели

Для контроллера у нас будет два метода. Один для добавления сувениров, а другой для установки активного состояния.

Чтобы использовать магазин, нам также нужно импортировать его сюда. Начнем с метода addMemento.

Чтобы обновить магазин в Svelte, мы должны использовать store.update. Он принимает функцию обратного вызова с текущим состоянием, в данном случае в форме memento. То, что мы вернем из функции, будет новым состоянием.

Поскольку мы хотим по существу поместить новый объект в массив, сохраняя при этом существующие элементы, мы можем использовать оператор распространения в текущем состоянии. Объект будет новым элементом, который мы добавляем.

Вы можете видеть, что каждое уведомление имеет в общей сложности 7 свойств. Например, их день и время или нужно ли повторять уведомление каждый день.

Теперь мы можем добавить столько памятных вещей, сколько захотим, но не можем их выбрать. Давай быстро это исправим. Добавьте следующее для метода selectMemento.

Опять же, нам нужно использовать store.update. Мы можем использовать простую map функцию, чтобы изменить active состояние того, где идентификатор совпадает. Во всем остальном мы хотим избавиться от состояния active. У нас может быть только один активный элемент одновременно. Теперь у нас не должно возникнуть проблем с выбором сувенира.

Создание основного каркаса

На боковой панели все. Наш следующий шаг - сделать их доступными для редактирования. Создайте новую папку с именем main рядом с sidebar. Как и в случае с боковой панелью, у нас будет три разных файла. Один для компонента, один для контроллера и еще один для стилей.

Вы можете импортировать его в App.svelte сразу после боковой панели.

Давайте посмотрим, что у нас внутри шаблона.

Помимо импорта, у нас также есть дополнительная строка в теге script. Мы определяем активный сувенир, который хотим использовать в шаблоне. Здесь мы снова используем знак доллара, чтобы сделать объявление реактивным.

Мы будем использовать это, чтобы решить, есть ли у нас активный сувенир. Если это так, мы можем отобразить элементы управления. Начнем сначала с карты.

Добавление макета для карточки уведомления

Внутри memento-wrapper добавьте следующий контент:

Это создает белую карточку для уведомления. У нас есть пара элементов управления. Во-первых, у нас есть if, проверяющий, есть ли у нас значок, связанный с сувениром. Внутри if у нас есть два метода. Один для удаления и один для загрузки изображения.

Еще у нас есть привязки. Редактируемое содержимое, помеченное знаком contenteditable, поддерживает использование привязок textContent и innerHTML. И, наконец, у нас есть кнопка удаления.

Если вы создадите сувенир и нажмете на него, теперь вас встретит карточка.

Обработка изображений

Давайте начнем реализовывать эту функциональность с обработки изображений. Внутри тегов сценария добавьте две новые функции.

Первый будет вызываться всякий раз, когда мы выбираем изображение. Второй будет вызван событием щелчка. Поскольку memento является реактивным, рендерингом будет заниматься Svelte.

Удаление сувенира

В этой части отсутствует только функция удаления. Добавьте эту новую функцию между тегами сценария:

Поскольку мы меняем состояние магазина, нам нужно вызвать update. Все, что нам нужно сделать, это вернуть новый массив, в котором мы отфильтровываем идентификатор выбранного сувенира.

Теперь мы можем перейти к реализации настроек и рассылке уведомлений.

Добавление шаблона настроек

После вашего memento div расширьте компонент следующим шаблоном:

Чтобы обрабатывать изменения различных свойств, мы можем снова использовать привязки. Для привязки флажков вы можете использовать директиву bind:checked. У нас также есть две функции. Но на этот раз они исходят от контроллера. Давайте посмотрим, как можно предварительно просмотреть уведомления.

Предварительный просмотр уведомлений

Для этого нам понадобится новый модуль. Запустите npm i node-notifier. Это позволит нам использовать push-уведомления. Включите его в свой главный контроллер и добавьте следующую функцию для preview:

Это создаст push-уведомление со свойствами текущего выбранного сувенира. Давай попробуем.

Как видите, привязки автоматически обновляют значения свойств. Нам не нужно реализовывать какие-либо пользовательские функции для обновления заголовка или тела.

Сохранение сувенира

Все, что осталось сделать, это сохранить сувенир, чтобы мы могли перенести его на более поздний день. Для этого мы воспользуемся заданиями cron. Если вы не знакомы с заданиями cron, вы можете ознакомиться с моим руководством о том, как с его помощью планировать задачи.

Чтобы использовать задания cron в узле, запустите npm i cron и также включите его в свой контроллер.

Теперь, если вы добавите метод save сразу после preview и выйдете из памяти, переданной в качестве параметра, вы заметите, что у нас есть правильное время и дата. Однако мы хотим преобразовать это в шаблон cron.

Создание шаблонов заданий cron

Для этого давайте создадим в контроллере новую функцию, которую мы сможем использовать.

К счастью для нас, все, что нам нужно сделать, это разделить строки и объединить различные части, чтобы получить желаемый узор. Затем мы можем вызвать эту функцию внутри метода save с соответствующими параметрами.

Следующим шагом будет составление расписания работы. Нам понадобится массив, в котором мы можем хранить отдельные задания cron. Мы также хотим убедиться, что если мы нажимаем кнопку «Сохранить» более одного раза на одном и том же памятке, мы не добавляем новое задание, а обновляем существующее. Для этого расширите метод save следующим:

Во-первых, нам нужно узнать, существует ли задание. Это можно сделать довольно просто, ища идентификатор, соответствующий тому, который мы передаем в функцию. Если это так, мы можем обновить все свойства задания. В противном случае мы можем отправить новое задание в массив.

Планирование заданий

Наконец, нам нужно запустить cron. Для этого я создал отдельную функцию и новый массив вне метода save.

Это сначала остановит все запущенные задания. Очистите массив и создайте новые на основе заданий, которые мы выполняем с помощью метода save. Для каждого задания мы создаем new cronJob с шаблоном и свойствами. Например, заголовок, сообщение или значок. В конце функции мы можем перезапустить их. Вы можете вызвать эту функцию как самый последний шаг внутри метода save.

Последние штрихи

И мы в основном закончили. Давайте добавим пару последних штрихов к электрону, чтобы закончить. Во-первых, мы хотим удалить меню приложения и предотвратить открытие DevTools.

Вернитесь к своему index.js файлу и добавьте следующее:

Скрыть приложение в системном трее

Наконец, давайте также спрячем приложение в трее. Мы не хотим, чтобы приложение все время занимало экран. Но мы по-прежнему хотим, чтобы он работал в фоновом режиме, чтобы можно было отправлять уведомления. Для этого мы должны подключиться к событиям minimize и close электрона. Добавьте два новых прослушивателя событий в вашу функцию createWindow.

Это предотвратит закрытие приложения и только скроет окно. Проблема только в том, что мы не можем его восстановить. Итак, давайте создадим меню для лотка.

Нам нужно импортировать Tray из Electron. В жизненном цикле app.ready мы можем создать его и прикрепить к нему новое контекстное меню. Чтобы выйти из приложения, мы сначала должны уничтожить window. В противном случае мы сталкиваемся с preventDefault внутри события close.

Заключение

И все готово. Если вы уже знакомы с основными веб-технологиями, создавать кроссплатформенные приложения проще, чем вы думаете. После того, как ваш проект настроен, это похоже на разработку веб-приложения. Но здесь у вас есть доступ к низкоуровневым API. Если вы не знаете, как получить к ним доступ, вы все равно можете поискать доступные пакеты на npmjs. Точно так же, как мы сделали для push-уведомлений и cron-заданий.

Если вы хотите поработать с проектом - с включенным CSS -, вы можете клонировать его из GitHub. Спасибо за ваше время, удачного кодирования!

Примечание из JavaScript In Plain English

Мы запустили три новых издания! Проявите любовь к нашим новым публикациям, подписавшись на них: AI на простом английском, UX на простом английском, Python на простом английском - спасибо и продолжайте учиться!

Мы также всегда заинтересованы в содействии продвижению качественного контента. Если у вас есть статья, которую вы хотите отправить в какую-либо из наших публикаций, отправьте нам электронное письмо по адресу [email protected] с вашим именем пользователя Medium, и мы добавим вас в качестве автора. Также сообщите нам, к каким публикациям вы хотите быть добавлены.