Как я обновил и официально запустил свое первое расширение для Chrome

Я НИКОГДА не завершаю личные проекты, которые начинаю.

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

Это само по себе является проблемой, которую я ДОЛЖЕН решить, прежде чем делать что-либо еще.

Освежитель…

2 июня я опубликовал на Medium статью под названием Я создал расширение для Chrome, позволяющее мгновенно находить акции eToro в инструменте проверки акций FINVIZ.

Расширение для Chrome будет сканировать тикеры с Finviz, передавать их на серверную часть, чтобы проверить, действительно ли тикер существует на eToro или нет.

Затем я передал бы информацию из бэкенда пользователю вот так…

Это было достаточно приличное усилие для БМВП (едва ли минимально жизнеспособный продукт)…

Но этого было недостаточно, чтобы быть выпущенным в качестве официального расширения. Сначала я думал отполировать его и выложить в Chrome Web Store, но потом пал жертвой собственных черт характера.

Я отложил расширение Finviz x eToro и начал мозговой штурм над следующим приложением, которое хотел создать.

И тут меня осенило. Я навсегда застряну в этом бесконечном цикле запуска проектов и никогда их не выпускаю.

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

Я должен был измениться.

Я решил правильно завершить расширение Finviz X eToro и официально запустить его в Интернет-магазине Chrome.

Вот как это было.

План игры

  1. Устраните все ошибки — сделайте расширение на 100% функциональным
  2. CSS, иконки и шрифты — сделайте расширение презентабельным
  3. Подготовьте его к производству
  4. Развернуть чертову вещь

Устранение ошибок и обеспечение работоспособности расширения

Несмотря на то, что у меня был план игры высокого уровня, у меня не было четкого плана выполнения, когда дело доходило до устранения ошибок или улучшения функциональности.

Я в основном полагался на свою интуицию, чтобы все заработало. Может быть, есть лучший способ сделать это (Дайте мне знать, ребята, как вы распределяете свое время, когда дело доходит до этого…).

1. Улучшение: Первое, что я хотел сделать, это сохранить результаты каждого сканирования.

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

Это означало, что мне приходилось постоянно возвращаться в Finviz, чтобы заново сканировать одни и те же элементы.

Раздражающий АФ.

Я думал, что это будет так же сложно, как работать с IndexedDB. Удивительно (и к счастью), это не так.

Все, что мне нужно было сделать, это вызвать метод set из Chrome Storage API, когда я сделал HTTP-запрос к серверной части. Вот так…

chrome.storage.sync.set({
      'last_scanned_tickers': tickersStatuses,
    }, function () {
      console.log('Value is set to ' + tickersStatuses);
      resolve(); 
    });

И тогда я мог бы просто вызвать соответствующий метод get, чтобы вернуть сохраненные результаты…

// defined a global object that I can call anywhere from the file 
const extensionState = {
  'last_scanned_tickers': {}, 
}; 
// get the results from the chrome store
chrome.storage.sync.get([
  'last_scanned_tickers'
], function (result) {
  // console.log('last_scanned_tickers on load ', result.last_scanned_tickers);
  extensionState.last_scanned_tickers = result.last_scanned_tickers ?? {}; 
  console.log('state: ', extensionState.last_scanned_tickers); 
});

Теперь пользователь мог просто отсканировать страницу результатов Finviz, перейти на другую вкладку и по-прежнему иметь доступ к результатам предыдущего сканирования.

Результат

2. Исправление ошибки: исправление гиперссылок «Просмотр»

Сначала я думал, что якорный тег выполнит свою работу, но оказалось, что я ошибался.

Теги привязки на самом деле не работают в расширении Chrome из-за всей этой изолированной среды.

Ошибка🐞:

Исправить🔧:

Покопавшись некоторое время, я обнаружил, что мне пришлось использовать API chrome.tabs для программного создания новой вкладки при нажатии гиперссылки.

Вот логика высокого уровня:

  • Добавьте событие клика к каждой из динамически создаваемых гиперссылок
  • Когда пользователь щелкает ссылку, программно создайте новую вкладку, используя новый метод chrome.tabs.create().

Код:

// add the event listener to each of the hyperlinks
let etoroAssetViewLinks = document.querySelectorAll('.etoro__asset--hyperlink'); 
  Array.from(etoroAssetViewLinks).map(viewLink => {
    viewLink.addEventListener('click', handleEtoroAssetViewLinkClickEvent); 
  });
// handling the click event
function handleEtoroAssetViewLinkClickEvent (e) {
  console.log('handleEtoroAssetViewLinkClickEvent', e); 
  let linkHref = e.srcElement.href; 
  chrome.tabs.create({
    url: linkHref, 
    active: false // setting the active flag to false, makes sure the popup stays open
  });
}

Большой. Решил это довольно быстро. 😀

Результат:

3. Исправление ошибки: предотвращение внедрения скрипта содержимого, когда пользователь находится на других вкладках.

Это не мешает показу, но я подумал, что было бы странно разрешить пользователю нажимать кнопку «Найти на eToro», когда он не находится на странице скринера Finviz.

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

Ошибка🐞:

Исправить🔧:

Довольно просто. Я использовал API chrome.tabs, чтобы получить активный объект вкладки, выполнил поиск строки, а затем динамически добавил кнопку, если это было совпадение:

let queryOptions = { 
    active: true, 
    lastFocusedWindow: true 
  };
  // `tab` will either be a `tabs.Tab` instance or `undefined`.
  let [tab] = await chrome.tabs.query(queryOptions);
  console.log('tab: ', tab);
if (
    tab.url.includes('finviz') && 
    tab.url.includes('screener')
  ) {
changeColorButtonWrapper.innerHTML = `<button id="changeColor">Find On eToro</button>`;
}

Результат:

Кнопка появляется только тогда, когда пользователь находится на странице Screener. Но если они откроют расширение на любой другой странице, кнопка будет скрыта.

Думаю, я устранил большинство ошибок шоу-стоппера. Теперь переходим к шагу 2.

Пользовательский интерфейс.

Подтяжка лица пристройки

В отличие от Стива Джобса, я не разбираюсь в дизайне. На самом деле, я ужасен в этом.

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

В поисках вдохновения я просмотрела Dribble и Uplabs и в конце концов решила остановиться на черно-белой теме 😆

Вероятно, добавьте несколько шрифтов и значков, чтобы сделать вещи более привлекательными.

  1. Создание черной панели навигации

Чтобы ускорить работу с моей стороны, я направился в CodePen, чтобы посмотреть, смогу ли я найти ручку с черной панелью навигации. Я нашел один здесь: https://codepen.io/sukhsingh2729/pen/zbGJOM

Я просто скопировал HTML и классы, которые мне были нужны, чтобы все заработало.

2. Добавление шрифтов и значков

Для этого полагался на Google Fonts & Icons.

Я использовал шрифт Poppins вместо стандартного: https://fonts.google.com/specimen/Poppins.

И для иконок: https://fonts.google.com/icons

Инструкции по шрифтам Google были довольно простыми, поэтому установка и запуск не заняли много времени.

3. Добавление загрузчика

Для этого я зашел в Lottiefiles, искал деньги и наткнулся на понравившуюся анимацию. Я скачал это как gif и добавил в свой html. Также использовал Flexbox для центрирования gif в расширении.

4. Стилизация элемента списка

Раньше я просто отображал тикеры в виде таблицы:

С помощью Flexbox мне удалось смоделировать html для прилично выглядящего элемента списка для одного из тикеров.

Выглядит достаточно прилично.

Затем мне пришлось динамически генерировать html в файле popup.js вместо жесткого кодирования вещей на html-странице.

Вот как мне это удалось:

// generate the html for list items 
let listItemsHtml = '';
for (let key in results) {
	listItemsHtml += `<div class="list__wrapper-item">//content</div>`
} 
let listWrapperHtml = `<div class="list__wrapper">${listItemsHtml}</div>`

Результат

Большой! Выглядит намного лучше. Вы так не думаете?

Я сломал расширение

Как только я подумал, что готов идти вперед и развернуть расширение, я понял, что в итоге сломал все, что создал.

Ошибка🐞№1: я сломал все, что делал в пункте 1.

Ошибка🐞№2: также нарушила логику загрузчика.

Ошибка🐞№3: Кроме того, я создал новую ошибку. По сути, когда вы нажимаете кнопку, она правильно выделяет элементы на Finviz с первого раза.

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

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

Решение ошибки №1🔧: По сути, я динамически отображал 2 набора HTML-тегов из 2 разных мест моего JS-кода. Мне просто нужно было извлечь правильный HTML в метод, а затем ссылаться на этот метод в обоих местах.

Решение ошибки №2🔧: здесь пришлось переработать логику. Я сделал это простым, я извлек HTML в метод JS и просто вызывал метод всякий раз, когда хотел отобразить загрузчик.

Решение ошибки №3🔧: Вероятно, это самое простое решение на данный момент. Я ссылался на один из классов eToro по гиперссылке, и это испортило один из методов querySelectorAll, на которые я ссылался. Извлеките стили из CSS во встроенные, и все заработало как положено 😀

Подведение итогов

Хорошо, все функционально, и все выглядит достаточно красиво. Пришло время подготовить расширение к производству.

1. Подготовьте ресурсы CSS

Проще говоря, упакуйте активы вместе с расширением вместо вызова стороннего URL-адреса (в данном случае Google Fonts).

Выгода? Работает в автономном режиме, а значки и шрифты загружаются намного быстрее.

Я нашел это руководство здесь, чтобы помочь мне самостоятельно разместить шрифты: https://support.google.com/webdesigner/answer/6163074?hl=en

Я следовал этому руководству здесь, чтобы самостоятельно разместить значки:

https://www.angularfix.com/2022/02/how-to-host-material-icons-offline.html

2. Добавление полных названий бегущих строк

Когда я открыл расширение через несколько дней, я понял, что не знаю, что означает каждый из тикеров.

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

Я создал хеш-карту тикеров и названий компаний (также скопированную со страницы Finviz Screener с помощью скрипта контента) и передал ее вверх по цепочке.

Сценарий контента

let tickersToFullNames = Array.from(document.querySelectorAll('.screener-link-primary'))
  .reduce((tickersHashMap, tickerElement) => {
    let ticker = tickerElement.innerHTML.replace('Open on eToro', '');
    let nextSiblingText = tickerElement.parentElement.nextElementSibling.innerText.replace('Open on eToro', ''); 
    tickersHashMap[ticker] = nextSiblingText; 
    return tickersHashMap; 
  }, {});

Результат

3. Размещение серверной части БЕСПЛАТНО

Обычно я бы просто использовал Fortrabbit (стоит мне около 5 долларов США за приложение), но для этого расширения я решил вместо этого использовать Heroku (поскольку у них есть бесплатный уровень, и я еще не зарабатываю $$$) .

У меня есть серверная часть Laravel, поэтому должно быть довольно просто заставить все двигаться.

Я пошел дальше и создал новое приложение на Heroku и выполнил шаги, описанные здесь:

И в истинном стиле разработчика меня приветствовала эта ошибка:

После нескольких минут лихорадочного гугления я нашел решение ошибки в этом уроке здесь:

https://appdividend.com/2022/03/01/how-to-deploy-laravel-project-on-heroku/

Оказалось, что мне пришлось добавить Procfile (шаг 3 в руководстве), и мне пришлось вручную добавить значения конфигурации в консоль Heroku, и все, наконец, заработало.

Затем мне пришлось перенести базу данных. (Я вижу финишную черту)

Мне потребовалось немного проб и ошибок, чтобы понять. Вот как я наконец заработал:

  1. На панели инструментов Heroku щелкните вкладку «Ресурсы», нажмите кнопку «Найти дополнительные надстройки» и выберите JawsDB MySQL.
  2. Подключился к MySQL Workbench и сделал импорт данных из дампа SQL
  3. На вкладке «Настройки» (Heroku) я нажал кнопку «Показать переменные конфигурации» и добавил каждую из переменных базы данных одну за другой.

Бум, серверная конечная точка жива!

Результат

Последний шаг, подключите расширение к недавно развернутому бэкенду.

Для этого мне пришлось добавить URL-адрес в файл manifest.json, а также я изменил жестко заданные ссылки в файле popup.js.

Все работает так, как ожидалось. Идем дальше…

Публикация расширения для Chrome

Инструкции для этого были достаточно хорошо задокументированы. Ссылка: https://developer.chrome.com/docs/webstore/publish/

Одной из первых вещей, которые мне пришлось сделать, было создать для себя учетную запись разработчика, и это стоило мне единовременной платы в размере 5 долларов США. Жесткий.

Я просто продолжал нажимать «Далее» (не читая никаких условий и положений), пока не попал на панель инструментов.

Я щелкнул параметр «Элементы» слева и нажал кнопку «Новый элемент».

Я просто заархивировал всю кодовую базу для расширения Chrome и загрузил ее.

Следующим шагом было заполнение формы списка магазинов.

Это было тяжело. Единственное, что хуже кодинга, — это писать описания кода.

Мне также пришлось загрузить официальную иконку в разных размерах в форме списка магазинов.

Большой. Еще одно узкое место, учитывая, что я инвалид, когда дело доходит до дизайна.

Я зашла в Canva, нашла шаблон логотипа, торговую иконку, иконку с лупой и сгруппировала все вместе 😂

Конечный результат:

Выглядит ужасно. Но это придется сделать.

Затем мне нужно было загрузить иконку в разных размерах в соответствии с требованиями Google (16x16, 32x32, 48x48, 128x128).

К сожалению, я не знал, как это сделать на самой Canva, поэтому я скачал версию 128x128 с Canva, а затем изменил их размер на https://onlinepngtools.com/resize-png.

После того, как я заполнил список магазинов, форму правил конфиденциальности и форму платежей и распространения (да, они довольно подробные), я смог отправить расширение на рассмотрение.

Официально опубликовано!

Через несколько дней я зарегистрировался, и мое расширение для Chrome было официально опубликовано в Интернет-магазине Chrome.

Вы можете попробовать расширение, нажав на эту ссылку: https://chrome.google.com/webstore/detail/finviz-stock-screener-x-e/pihbgfnhlgejamcnfocjobbhefofkfng.

Проблемы

Хуже всего в этом опыте было то, что я столкнулся с отсутствием дисциплины (и решимости) в своих собственных проектах.

Не поймите меня неправильно. Я очень предан своему делу, когда дело доходит до моей повседневной работы (вероятно, потому что у меня есть начальник), но я склонен много расслабляться, когда приходит время завершить свои личные проекты.

И на этот раз ничем не отличался.

Я тратил дни на прокрастинацию, беспокоясь о лучших практиках, «отдыхая» и буквально делая что-то еще, кроме работы над завершением дел.

При этом я определенно доволен тем, что, несмотря на то, что мне понадобился целый месяц (вместо выходных, как предполагалось), мне все же удалось пересечь финишную черту.

Я могу с гордостью сказать, что официально запустил свое первое расширение для Chrome. Возможно, для вас это не будет огромным достижением, но для меня это определенно стало большим испытанием, и я рад, что довел дело до конца.

Лично я решил всегда представлять работу, которую я делаю, публично. Таким образом, я могу перенастроить себя, чтобы всегда завершать любимые проекты, которые начинаю.

Также читайте: я впервые попытался автоматизировать свои файлы и папки с помощью PHP

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

Также, если вам нравится моя работа и вы хотите поддержать то, что я делаю, вы можете рассмотреть возможность купить мне здесь кофе.