✨Feature: Floating Video

Chętnie zabrałem się za ten feature z dwóch powodów:

  • jest bardzo przydatny, sam używam floating video na youtubie
  • nigdy czegoś takiego nie pisałem, więc chętnie się pobawię

Na samym początku trzeba rozgryźć jak zhakować wykop aby uzyskać ten efekt.

🐱‍💻 Hakowanie

Zobaczmy jak wygląda kod html gdzie player jest umieszczony.

Hmm widać, że to nie jest zaembedowany youtube, lecz preview złożony z obrazka oraz linka. Po kliknieciu w preview, leci wywołanie ajaxem a obrazek zastępowany jest przez iframe youtuba

Elementy mamy namierzone. Teraz musimy poszukać czegoś co się stanie floatem. .videoWrapper wygląda tutaj bardzo sensownie. F12 i dodajemy w konsoli style

Efekt mnie zaskoczył. Wszystko zninęło. Nawet po ustawieniu Height: z 0 na 1000px nic się nie pojawia

Szukamy dalej hmm

.embed-youtube jest zbyt wysoko, ponieważ już łapie treść posta (Test), a chcemy zrobić floata wyłącznie na playerze. Uderzamy w iframe

Yeah! Mały sukces. Iframe drgnął i osadził się w stałym miejscu (position: fixed)

Jeszcze zmienimy aby player był na pierwszym planie przez dodanie z-index

Świetnie, pierwszy krok zrobiony. Trzeba pomyśleć jak sam feature ma działać.

🐱‍👤 Działanie

  • Jako użytkownik klikam w playera
  • Filmik sie uruchamia
  • Przewijam stronę w dół
  • Player znika z ekranu
  • Player przenosi się w prawy dolny róg ekranu

🐱‍🏍 Piszemy kod!

Co na ten moment mamy? Potrafimy ustawić odpowiedni element (iframe) jako position: fixed. Najważniejszą teraz rzeczą jest wykrycie czy element zniknął nam z ekranu (viewport). Szybka grzebanina po necie, wiadomo SDD (StackOverflow Driven Development)

  function isElementInViewport($element) {
    var rect = $element.getBoundingClientRect();
    return (
      rect.top >= 0 &&
      rect.left >= 0 &&
      rect.bottom <=
        (window.innerHeight || document.documentElement.clientHeight) &&
      rect.right <= (window.innerWidth || document.documentElement.clientWidth)
    );
  }

Wykrywanie czy element jest widoczny działa. Trzeba to przyczepić do window.scrolla

   window.addEventListener("scroll", function () {
        getAllVideoPlayers().forEach(function ($videoPlayer) {
          if (isElementInViewport($videoPlayer)) {
            console.log("is visible")
          }
          else {
            console.log("is NOT visible")
          }
        });
      });

Szybki test… wygląda OK. Brniemy w to dalej!

            function makeFloatVideo($player) {
              $player.setAttribute(
                "style",
                "position: fixed; top: 75%; left: 81%; width: 320px; height: 190px; z-index: 100000"
              );
            }

Niby działa ale coś dziwnie. Przy każdym scrollu cały proces się uruchamia na nowo, iframe mruga, dzieją się rzeczy niestworzone.

Hmm nie ma sensu aby nawet najdelikatniejszy scroll odpalał całą procedurę od nowa. Przecież jak element już jest niewidoczny, to po co znów ustawiać style?

Idziemy w najprostsze rozwiązanie, Robimy flagę, która będzie czuwać nad uruchomieniem makeFloatVideo. Jeśli dojdzie do zmiany stanu (widoczny => niewidoczny) wtedy dopiero uruchomimy makeFloatVideo

 function onVisibilityChange($element, invisibleCallback) {
    var prevVisibility = $element.getAttribute(DATA_VISIBLE_ATTR);

    if (prevVisibility === null) {
      prevVisibility = false; // domyślna wartosc
    } else {
      prevVisibility = Boolean(prevVisibility); // musimy zrobic parsing dla boola, żeby nie operować na stringu: true/false
    }

    var currentVisibity = isElementInViewport($element);

    if (currentVisibity != prevVisibility) { // jeśli poprzednia wartość flagi z obecną się nie zgadzają, znaczy że zaszła zmiana
      $element.setAttribute(DATA_VISIBLE_ATTR, currentVisibity);

      if (!currentVisibity) {
        invisibleCallback();
      }
    }
  }

Działa i to jeszcze jak! 😉 Reszta rzeczy to już kosmetyka i drobnica, o której nie będę pisać, bo nie ma sensu. Nic ciekawego się w dalszej części prac nie dzieje.

👉 Dla zainteresowanych odsyłam do githuba.

🏍️ Światła! Kamera! Akcja!

👉 Klikamy w VIDEO

👉 SCROLL w doł

🔥Ale piękny floating video wyszedł🔥
Później trochę odsunę od krawędzi, bo jest zbyt blisko. Generalnie jestem zadowolony z (d)efektu.

Zapraszam do pobrania rozszerzenia Chrome Web Store i uczestnictwa w projekcie!

to be continued ヘ( ^o^)ノ\(^_^ )

✨Feature: Button wstaw link w edytorze ma wyświetlić prompt na wprowadzenie linka

Zrobienie tego feature’a nie było aż takie proste jak mi się wydawało. Problem w tym, że button w edytorze ma swoje własne zdarzenie onclick.

Fajnie byłoby event wywalić i napisać swój totalnie od zera.

Niestety nie da się tego tak prosto zrobić.
Dlaczego? Przecież jest metoda:

target.removeEventListener(type, listener);

Tak, jest. Ale by pozbyć się eventa musimy w parametrze podać event listender. Nie znamy go. Nie mamy jak go pobrać ani odwołać się do niego.

Jest jeszcze sposób w jQuery: unbind () czy też off(), lecz tutaj mamy kolejny problem. Kod js’a uruchomiony czy też wstrzyknięty rozszerzeniem działa w zupełnie innym kontekscie. Nie mamy możliwości odwołania do jQuery podpiętego na wykopie. Tak na prawdę, to nie możemy się odwołać do żadnych obiektów, funkcji jsowych na stronie, bo to nie nasz kontekst. Jedynie możemy się komunikować via DOM.

Wstrzyknięcie żywego kodu jsa też nie zadziała. Manifest v3 nie pozwala na to:

var script = document.createElement('script');
script.textContent = 'alert("test")';
(document.head||document.documentElement).appendChild(script);
script.remove();

Gdybyśmy użyli manifest v2, to dałoby radę, lecz do końca 2022 chrome przestanie wspierać v2 i rozszerzenie przestałoby działać.

Jedynie co można zrobić w tym przypadku, to:

  • w pliku manifest.json podać w web_accessible_resources z jakich resourceów, będziemy korzystać. Tutaj resourcem będzie plik z jsem: remove-event.js. Jeśli nie podamy pliku w web_accessible_resources dostaniemy komunikat:
    Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
  • teraz można podpiąć plik do strony
var s = document.createElement('script');
s.src = chrome.runtime.getURL('/common/remove-event.js');
s.onload = function() {
  this.remove();
};
(document.head || document.documentElement).appendChild(s);

Wszystko fajnie. Skrypt się podpina i wykonuje.

Tylko czy na prawdę warto coś takiego robić aby pozbyć się eventa? Na pewno nie.
Można pomyśleć nad loadm przeniesieniem obecnych funkcjonalności do podpinania jak wyżej, czyli przez script.

Zamiast ładowania pliku js przez `executeScript`

Dodalibyśmy go przez script

Jak dla mnie rozwiązanie spoko, lecz zrobimy obejście aby trzymać się obecnego mechanizmu w ramach ćwiczeń. Zobaczę ile będzie męki z innymi featureami. Niech sobie obecny event na buttonie żyje jak żył. Dodamy nowego eventa, który się uruchomi zaraz PO obecnym

Kluczem jest (g-ł-u-p-i-e) rozwiązanie z setTimeout(function (), 1);

Głupie a działa zadziwiająco dobrze!

  • po kliknieciu w button pozwalamy odpalić pierwony event:
  • natychmiast pojawia się prompt z miejscem na linka
  • klikamy OK i link wskakuje

Pięknie. Niezbyt to eleganckie rozwiązanie ale działa poprawnie. Trzeba wziąć poprawkę na to, że trochę hackujemy wykop i mniej eleganckie rozwiązania będą się lepiej sprawdzać.

Ostatnia rzecz jaka wymagała zmiany to sposób uruchomienia featurea. Do tej pory miałem założenie: jeden feature na URL. Czyli jak jesteśmy na stronie:
https://www.wykop.pl/wiadomosc-prywatna/konwersacja/…
to na podstawie prostego RegEx’a dopinamy feature:

Ha! Jakże szybko okazało się to błędnym założeniem. Obecnie mamy dwie funkcjonalności, które powinny zostać uruchomione na tej samej stronie. Szybka zmiana:

Dodanie foreach’a:

Działa!

to be continued ヘ( ^o^)ノ\(^_^ )

✨Feature: „Większe pole do wiadomości”

Zgodnie z założeniem, zrobiłem najprostszy feature, aby móc jak najszybciej wrzucić rozszerzenie do Chrome Web Store. Czyli na pierwszy ogień wziąłem: Wieksze pole do wiadomości:

Bez rozszerzenia
Z rozszerzeniem

Postanowiłem inaczej rozplanować strukturę projektu niż w poprzednim extensionie JustJoin.IT, gdzie miałem jeden plik core.js zawierający wszystko co potrzebne.

Tutaj każdy feature będzie jako osoby plik dołączany dynamicznie na podstawie urla.

W pliku background.js mamy prostą funkcję pobierającą nazwę feature’a

function getFeatureName(currentUrl) {
  if (/wykop\.pl\/wiadomosc-prywatna/i.test(currentUrl)) {
    return "bigger-field-messages";
  }
  return "";
}

Natępnie wczytujemy wybrany feature:

    const featureName = getFeatureName(tab.url); // pobieramy nazwe
    if (featureName == "") {
      return;
    }
    
    chrome.scripting
      .executeScript({
        target: {
          tabId: tabId,
          allFrames: false,
        },
        files: [`./features/${featureName}.js`], // dodajemy plik zawierający logikę feature'a
      })
      .catch((err) => console.log(err));
  }

Myślę, że struktura projektu, tutaj jest najciekawsza.

Co do samego feature’a, to nic się tam specjalnego nie dzieje. Szukamy odpowiednich elementów DOM i wyliczamy nową wysokość wiadomości (.pmStreamView), nuda.

Wrzuciłem do sklepu rozszerzenie i czekam aż zostanie opublikowane. Trwa to gdzieś z parę dni.
Dam znać jak już się pojawi do instalacji.

to be continued ヘ( ^o^)ノ\(^_^ )

🧰🔨 Zaczynamy projekt: #naprawmywykop 🔨🧰

O co chodzi w tym projekcie #naprawmywykop?

Tworzymy razem ze społecznością wykop.pl oraz 4programmers rozszerzenie do przegladarki chrome. Rozszerzenie będzie dodawać lub modyfikować istniejące funkcjonalności serwisu.

Chcesz się przyłączyć do zabawy? Masz jakiś pomysł? Napisz:

Jest już kilka propozycji zebrane z https://www.wykop.pl/wpis/61580485/hej-wam-ruszam-z-akcja-naprawmywykop-celem-akcji-j/

  • Większe pole do wiadomości 👈
  • W znaleziskach, jesli jest film youtube/streamable czy inne treści co sa embed, żeby ten div był float i na górże, żeby mógł jednocześnie oglądać filmik i czytać komentarze ( ͡° ͜ʖ ͡°) 
  • Możliwość rozszerzenia filmu, teraz ma stała wartość co jest bardzo głupie, coś jak tryb kinowy na YT
  • Szyfrowanie aby można było omijac punkty regulaminu? Dla użytkowników bez dodatku wyświetlałby się ciąg znaków, dla osób z dodatkiem wyświetlałaby się normalna wiadomość.
  •  Przy pomocy wykop API naprawić przycisk dodawania do ulubionych aby faktycznie dodawał.
  • Możliwość pobierania filmów ze streamable, ewentualnie aby po kliknięciu na przycisk otwierał w nowej karcie link streamable.com/xyz123 na streamabledl.com/xyz123, ewentualnie od razu pobierał.
  • Zwiększenie #czarnolisto
  • Blokowanie po tytule jak ktoś nie dał tagów

Zacznę zupełnie inaczej jak to było w przypadku mojego pierwszego rozszerzenia do JustJoin.IT.

  • Najpierw zrobię najprostszą funkcjonalność
  • Wrzuce od razu do sklepu
  • Stopniowo będę rozwijać rozszerzenie
  • Od razu będzie można zainstalować extensiona i zwracać uwagi/zmiany

Na pierwszy ogien leci: Większe pole do wiadomości 👈

To będzie pierwsza funkcjonalność, a potem polecimy z czymś zabawniejszym 🧰

to be continued ヘ( ^o^)ノ\(^_^ )