BTHLABS-62: Display progress in extension popup

Co-authored-by: Tomek Wójcik <labs@tomekwojcik.pl>
Co-committed-by: Tomek Wójcik <labs@tomekwojcik.pl>
This commit is contained in:
Tomek Wójcik 2025-10-13 18:48:00 +00:00 committed by Tomek Wójcik
parent 8b86145519
commit 7b67a2f758
9 changed files with 200 additions and 72 deletions

View File

@ -0,0 +1,28 @@
#!/usr/bin/env bash
set -e
set +x
set -o pipefail
cat >"./docker-compose-ci-${COMPOSE_PROJECT}.yaml" <<EOF
services:
postgres:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/postgres:15.13-${COMPOSE_PROJECT}"
keycloak:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/keycloak:22.0.3-${COMPOSE_PROJECT}"
rabbitmq:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/rabbitmq:3.10.8-${COMPOSE_PROJECT}"
apple-ci:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/apple:ci-${COMPOSE_PROJECT}"
backend-ci:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:ci-${COMPOSE_PROJECT}"
extension-ci:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/extension:ci-${COMPOSE_PROJECT}"
packages-ci:
image: "docker-hosted.nexus.bthlabs.pl/hotpocket/packages:ci-${COMPOSE_PROJECT}"
EOF

View File

@ -11,18 +11,17 @@ on:
- "public" - "public"
jobs: jobs:
setup: run-checks:
name: "Setup" name: "Checks"
runs-on: "ubuntu-latest" runs-on: "ubuntu-latest"
outputs:
SHORT_SHA: ${{ steps.get-build-options.outputs.SHORT_SHA }}
BUILD_ARCH: ${{ steps.get-build-options.outputs.BUILD_ARCH }}
BUILD_PLATFORM: ${{ steps.get-build-options.outputs.BUILD_PLATFORM }}
HOTPOCKET_BACKEND_VERSION: ${{ steps.get-backend-version.outputs.HOTPOCKET_BACKEND_VERSION }}
HOTPOCKET_BACKEND_BUILD: ${{ steps.get-backend-version.outputs.HOTPOCKET_BACKEND_BUILD }}
steps: steps:
- name: "Checkout the code" - name: "Checkout the code"
uses: "actions/checkout@v2" uses: "actions/checkout@v2"
- name: "Get run info"
id: "get-run-info"
run: |
set -x
echo "COMPOSE_PROJECT=${{ vars.COMPOSE_PROJECT_BASE }}-${GITHUB_RUN_NUMBER}" >> $GITHUB_OUTPUT
- name: "Get build options" - name: "Get build options"
id: "get-build-options" id: "get-build-options"
run: | run: |
@ -37,28 +36,6 @@ jobs:
echo "SHORT_SHA=$SHORT_SHA" >> $GITHUB_OUTPUT echo "SHORT_SHA=$SHORT_SHA" >> $GITHUB_OUTPUT
echo "BUILD_ARCH=$BUILD_ARCH" >> $GITHUB_OUTPUT echo "BUILD_ARCH=$BUILD_ARCH" >> $GITHUB_OUTPUT
echo "BUILD_PLATFORM=$BUILD_PLATFORM" >> $GITHUB_OUTPUT echo "BUILD_PLATFORM=$BUILD_PLATFORM" >> $GITHUB_OUTPUT
- name: "Get `backend` version"
id: "get-backend-version"
run: |
set -x
if [ "${GITHUB_REF_NAME}" = "development" ]; then
VERSION="${GITHUB_SHA::8}"
BUILD="${GITHUB_RUN_NUMBER}"
else
VERSION="v$(grep -Po '(?<=^version\s=\s")[^"]+' services/backend/pyproject.toml)"
BUILD="01"
fi
echo "HOTPOCKET_BACKEND_VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "HOTPOCKET_BACKEND_BUILD=$BUILD" >> $GITHUB_OUTPUT
run-checks:
name: "Checks"
runs-on: "ubuntu-latest"
needs:
- "setup"
steps:
- name: "Checkout the code"
uses: "actions/checkout@v2"
- name: "Set up Docker Buildx" - name: "Set up Docker Buildx"
id: "setup-docker-buildx" id: "setup-docker-buildx"
uses: "docker/setup-buildx-action@v3" uses: "docker/setup-buildx-action@v3"
@ -76,8 +53,10 @@ jobs:
context: "services/" context: "services/"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/postgres:15.13-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/postgres:15.13-${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
platforms: "${{ needs.setup.outputs.BUILD_PLATFORM }}" platforms: "${{ steps.get-build-options.outputs.BUILD_PLATFORM }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `keycloak` image" - name: "Build `keycloak` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -85,8 +64,10 @@ jobs:
context: "services/" context: "services/"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/keycloak:22.0.3-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/keycloak:22.0.3-${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
platforms: "${{ needs.setup.outputs.BUILD_PLATFORM }}" platforms: "${{ steps.get-build-options.outputs.BUILD_PLATFORM }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `rabbitmq` image" - name: "Build `rabbitmq` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -94,8 +75,10 @@ jobs:
context: "services/" context: "services/"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/rabbitmq:3.10.8-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/rabbitmq:3.10.8-${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
platforms: "${{ needs.setup.outputs.BUILD_PLATFORM }}" platforms: "${{ steps.get-build-options.outputs.BUILD_PLATFORM }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `backend-ci` image" - name: "Build `backend-ci` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -104,8 +87,10 @@ jobs:
target: "ci" target: "ci"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:ci-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/backend:ci-${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
platforms: "${{ needs.setup.outputs.BUILD_PLATFORM }}" platforms: "${{ steps.get-build-options.outputs.BUILD_PLATFORM }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `packages-ci` image" - name: "Build `packages-ci` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -114,8 +99,10 @@ jobs:
target: "ci" target: "ci"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/packages:ci-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/packages:ci-${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
platforms: "${{ needs.setup.outputs.BUILD_PLATFORM }}" platforms: "${{ steps.get-build-options.outputs.BUILD_PLATFORM }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `extension-ci` image" - name: "Build `extension-ci` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -124,8 +111,10 @@ jobs:
target: "ci" target: "ci"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/extension:ci-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/extension:ci-${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
platforms: "${{ needs.setup.outputs.BUILD_PLATFORM }}" platforms: "${{ steps.get-build-options.outputs.BUILD_PLATFORM }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Build `apple-ci` image" - name: "Build `apple-ci` image"
uses: docker/build-push-action@v6 uses: docker/build-push-action@v6
with: with:
@ -134,29 +123,79 @@ jobs:
target: "ci" target: "ci"
push: false push: false
load: true load: true
tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/apple:ci-local" tags: "docker-hosted.nexus.bthlabs.pl/hotpocket/apple:ci-${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
platforms: "${{ needs.setup.outputs.BUILD_PLATFORM }}" platforms: "${{ steps.get-build-options.outputs.BUILD_PLATFORM }}"
cache-from: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket"
cache-to: "type=registry,ref=nexus.bthlab.bthlabs.net:8001/hotpocket,mode=max"
- name: "Prepare the build"
id: "prepare"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
run: |
set -x
./.gitea/tools/render-docker-compose-ci.sh
- name: "Run `backend` checks" - name: "Run `backend` checks"
if: "steps.prepare.conclusion == 'success'"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml run --rm backend-ci inv ci docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
run --rm \
backend-ci inv ci
- name: "Run `packages` checks" - name: "Run `packages` checks"
if: always() if: "steps.prepare.conclusion == 'success'"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml run --rm packages-ci inv ci docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
run --rm \
packages-ci inv ci
- name: "Run `extension` checks" - name: "Run `extension` checks"
if: always() if: "steps.prepare.conclusion == 'success'"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml run --rm extension-ci inv ci docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
run --rm \
extension-ci inv ci
- name: "Run `apple` checks" - name: "Run `apple` checks"
if: always() if: "steps.prepare.conclusion == 'success'"
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml run --rm apple-ci inv ci docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
run --rm \
apple-ci inv ci
- name: "Clean up" - name: "Clean up"
if: always() if: always()
env:
COMPOSE_PROJECT: "${{ steps.get-run-info.outputs.COMPOSE_PROJECT }}"
run: | run: |
set -x set -x
docker compose -f docker-compose.yaml -f docker-compose-ci.yaml down --volumes docker compose \
-p "${COMPOSE_PROJECT}" \
-f "docker-compose.yaml" \
-f "docker-compose-ci.yaml" \
-f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" \
down --volumes --rmi all || true
rm -f "docker-compose-ci-${COMPOSE_PROJECT}.yaml" || true

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
.envrc* .envrc*
.ipythonhome/ .ipythonhome/
/docker-compose-ci-*.yaml

View File

@ -86,3 +86,5 @@ Licensed under terms of the MIT License
Pepper Hot Solid icon Pepper Hot Solid icon
Copyright (c) Icons8 Copyright (c) Icons8
Licensed under terms of the MIT License Licensed under terms of the MIT License
Spinner Loader CSS from https://css-loaders.com/

View File

@ -1,14 +1,14 @@
services: services:
postgres: postgres:
ports: [] ports: !override []
keycloak: keycloak:
command: "echo 'NOOP'" command: "echo 'NOOP'"
ports: [] ports: !override []
restart: "no" restart: "no"
rabbitmq: rabbitmq:
ports: [] ports: !override []
include: include:
- path: "./services/backend/docker-compose-ci.yaml" - path: "./services/backend/docker-compose-ci.yaml"

View File

@ -164,7 +164,7 @@ const doHandleAuthFlow = (authTab) => {
); );
const expectedSessionTabQuery = `?authSessionToken=${authSessionToken}`; const expectedSessionTabQuery = `?authSessionToken=${authSessionToken}`;
if (tabId !== currentAuthTabId && changedURL.includes(expectedSessionTabQuery)) { if (tabId !== currentAuthTabId && changedURL && changedURL.includes(expectedSessionTabQuery)) {
// When redirecting from the preauth page to the HotPocket instance, // When redirecting from the preauth page to the HotPocket instance,
// Safari "replaces" the auth tab with a new one. This nasty hack will // Safari "replaces" the auth tab with a new one. This nasty hack will
// allow the extension to keep track of it. // allow the extension to keep track of it.
@ -268,7 +268,7 @@ const doSetupRPC = async () => {
}; };
const doSendTabMessage = (tab, message) => { const doSendTabMessage = (tab, message) => {
HotPocketExtension.api.tabs.sendMessage(tab.id, message). return HotPocketExtension.api.tabs.sendMessage(tab.id, message).
then((result) => { then((result) => {
HotPocketExtension.LOGGER.debug( HotPocketExtension.LOGGER.debug(
'HotPocketExtension.background.doSendTabMessage(): message sent', 'HotPocketExtension.background.doSendTabMessage(): message sent',
@ -327,6 +327,10 @@ const onBrowserActionClicked = async (tab) => {
let result = false; let result = false;
let error = null; let error = null;
await doSendTabMessage(tab, {
type: 'HotPocket:Extension:browserActionClicked',
});
try { try {
let accessToken = await doSetupRPC(); let accessToken = await doSetupRPC();
@ -348,7 +352,7 @@ const onBrowserActionClicked = async (tab) => {
error: error, error: error,
}; };
doSendTabMessage(tab, message); return await doSendTabMessage(tab, message);
}; };
const onMessage = (message, sender, sendResponse) => { const onMessage = (message, sender, sendResponse) => {

View File

@ -1,6 +1,7 @@
import HotPocketExtension from '../common'; import HotPocketExtension from '../common';
import POPUP from './templates/popup.html'; import POPUP from './templates/popup.html';
import POPUP_CONTENT_SAVING from './templates/popup_content_saving.html';
import POPUP_CONTENT_SUCCESS from './templates/popup_content_success.html'; import POPUP_CONTENT_SUCCESS from './templates/popup_content_success.html';
import POPUP_CONTENT_ERROR from './templates/popup_content_error.html'; import POPUP_CONTENT_ERROR from './templates/popup_content_error.html';
@ -18,6 +19,19 @@ class Popup {
this.timeout = null; this.timeout = null;
} }
}; };
setContent = (content) => {
const shadow = this.container.shadowRoot;
const body = shadow.querySelector('.hotpocket-extension-popup-body');
body.innerHTML = content;
const i18nElements = shadow.querySelectorAll('[data-message]');
for (let i18nElement of i18nElements) {
i18nElement.innerHTML = HotPocketExtension.api.i18n.getMessage(
i18nElement.dataset.message,
);
}
};
close = () => { close = () => {
this.clearCloseTimeout(); this.clearCloseTimeout();
@ -36,15 +50,7 @@ class Popup {
const shadow = this.container.attachShadow({mode: 'open'}); const shadow = this.container.attachShadow({mode: 'open'});
shadow.innerHTML = POPUP; shadow.innerHTML = POPUP;
const body = shadow.querySelector('.hotpocket-extension-popup-body'); this.setContent(content);
body.innerHTML = content;
const i18nElements = shadow.querySelectorAll('[data-message]');
for (let i18nElement of i18nElements) {
i18nElement.innerHTML = HotPocketExtension.api.i18n.getMessage(
i18nElement.dataset.message,
);
}
const closeElements = shadow.querySelectorAll('.hotpocket-extension-popup-close'); const closeElements = shadow.querySelectorAll('.hotpocket-extension-popup-close');
for (const closeElement of closeElements) { for (const closeElement of closeElements) {
@ -53,6 +59,9 @@ class Popup {
document.body.appendChild(this.container); document.body.appendChild(this.container);
}; };
update = (content) => {
this.setContent(content);
};
onCloseClick = (event) => { onCloseClick = (event) => {
this.close(); this.close();
}; };
@ -60,15 +69,27 @@ class Popup {
let currentPopup = null; let currentPopup = null;
const doHandleSaveMessage = (message) => { const doHandleBrowserActionClickedMessage = (message) => {
if (currentPopup !== null) { if (currentPopup !== null) {
currentPopup.close(); currentPopup.close();
} }
currentPopup = new Popup(); currentPopup = new Popup();
currentPopup.show( currentPopup.show(POPUP_CONTENT_SAVING);
(message.result === true) ? POPUP_CONTENT_SUCCESS : POPUP_CONTENT_ERROR, };
);
const doHandleSaveMessage = (message) => {
let content = POPUP_CONTENT_ERROR;
if (message.result === true) {
content = POPUP_CONTENT_SUCCESS;
}
if (currentPopup === null) {
currentPopup = new Popup();
currentPopup.show(content);
} else {
currentPopup.update(content);
}
}; };
const doSendMessage = (message) => { const doSendMessage = (message) => {
@ -93,7 +114,9 @@ export default ({...configuration}) => {
let response = {ok: true}; let response = {ok: true};
try { try {
if (message.type === 'HotPocket:Extension:save') { if (message.type === 'HotPocket:Extension:browserActionClicked') {
doHandleBrowserActionClickedMessage(message);
} else if (message.type === 'HotPocket:Extension:save') {
doHandleSaveMessage(message); doHandleSaveMessage(message);
} }
} catch (exception) { } catch (exception) {

View File

@ -58,6 +58,9 @@
.hotpocket-extension-popup .hotpocket-extension-popup-body > * { .hotpocket-extension-popup .hotpocket-extension-popup-body > * {
margin: 0px; margin: 0px;
} }
.hotpocket-extension-popup .hotpocket-extension-popup-body > .hotpocket-extension-popup-loader {
margin: 0px auto;
}
.hotpocket-extension-popup .hotpocket-extension-popup-body strong { .hotpocket-extension-popup .hotpocket-extension-popup-body strong {
font-weight: 600; font-weight: 600;
} }
@ -67,6 +70,33 @@
.hotpocket-extension-popup .hotpocket-extension-popup-message-error { .hotpocket-extension-popup .hotpocket-extension-popup-message-error {
color: #EE6476; color: #EE6476;
} }
.hotpocket-extension-popup-loader {
animation: hotpocket-extension-popup-loader-animation 1s infinite steps(12);
aspect-ratio: 1;
background:
linear-gradient(0deg ,rgb(240 240 240/50%) 30%,#0000 0 70%,rgb(240 240 240/100%) 0) 50%/8% 100%,
linear-gradient(90deg,rgb(240 240 240/25%) 30%,#0000 0 70%,rgb(240 240 240/75% ) 0) 50%/100% 8%;
background-repeat: no-repeat;
border-radius: 50%;
display: grid;
width: 32px;
}
.hotpocket-extension-popup-loader::before,
.hotpocket-extension-popup-loader::after {
background: inherit;
border-radius: 50%;
content: "";
grid-area: 1/1;
opacity: 0.915;
transform: rotate(30deg);
}
.hotpocket-extension-popup-loader::after {
opacity: 0.83;
transform: rotate(60deg);
}
@keyframes hotpocket-extension-popup-loader-animation {
100% {transform: rotate(1turn)}
}
</style> </style>
<div class="hotpocket-extension-popup"> <div class="hotpocket-extension-popup">
<div class="hotpocket-extension-popup-header"> <div class="hotpocket-extension-popup-header">

View File

@ -0,0 +1 @@
<div class="hotpocket-extension-popup-loader"></div>