deploy: split desktop releases and fix downloads navigation
This commit is contained in:
parent
8611415452
commit
927d9b298f
@ -13,3 +13,6 @@ WEB_ORIGIN=https://fieldlens.example.invalid
|
|||||||
FIELDLENS_PRINT_RELEASE_TAG=fieldlens-print-v0.1.0
|
FIELDLENS_PRINT_RELEASE_TAG=fieldlens-print-v0.1.0
|
||||||
FIELDLENS_PRINT_RELEASE_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download
|
FIELDLENS_PRINT_RELEASE_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download
|
||||||
FIELDLENS_PRINT_DOWNLOAD_DIR=downloads
|
FIELDLENS_PRINT_DOWNLOAD_DIR=downloads
|
||||||
|
FIELDLENS_DESKTOP_RELEASE_BASE_URL=https://git.arrowstack.dev/Admin/fieldlens-desktop/releases/download
|
||||||
|
FIELDLENS_DESKTOP_RELEASE_TAG=v0.1.0
|
||||||
|
FIELDLENS_DESKTOP_DOWNLOAD_BASE_URL=https://git.arrowstack.dev/Admin/fieldlens-desktop/raw/branch/main/downloads
|
||||||
|
|||||||
@ -1,202 +1,10 @@
|
|||||||
name: Desktop Release
|
name: Desktop Release (moved)
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
tags:
|
|
||||||
- "fieldlens-print-v*"
|
|
||||||
paths:
|
|
||||||
- "apps/print/**"
|
|
||||||
- ".gitea/workflows/desktop-release.yml"
|
|
||||||
pull_request:
|
|
||||||
paths:
|
|
||||||
- "apps/print/**"
|
|
||||||
- ".gitea/workflows/desktop-release.yml"
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
permissions:
|
|
||||||
code: read
|
|
||||||
releases: read
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
windows-nsis:
|
moved:
|
||||||
name: Windows x64 NSIS
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- run: echo "Desktop builds now run from Admin/fieldlens-desktop"
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 24
|
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
|
||||||
with:
|
|
||||||
targets: x86_64-pc-windows-msvc
|
|
||||||
|
|
||||||
- name: Install Windows cross-build tools
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y nsis lld llvm clang
|
|
||||||
cargo install --locked cargo-xwin
|
|
||||||
|
|
||||||
- name: Install Node dependencies
|
|
||||||
run: npm install
|
|
||||||
|
|
||||||
- name: Build Windows NSIS installer
|
|
||||||
run: npm run bundle:windows --workspace @fieldlens/print
|
|
||||||
env:
|
|
||||||
XWIN_CACHE_DIR: ${{ runner.temp }}/xwin-cache
|
|
||||||
|
|
||||||
- name: Stage Windows installer
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
source_file="$(find apps/print/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis -maxdepth 1 -type f -name '*-setup.exe' -print -quit)"
|
|
||||||
test -n "$source_file"
|
|
||||||
mkdir -p dist/desktop
|
|
||||||
cp "$source_file" dist/desktop/fieldlens-print-windows-x64-setup.exe
|
|
||||||
|
|
||||||
- name: Upload Windows installer
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: fieldlens-print-windows-x64
|
|
||||||
path: dist/desktop/fieldlens-print-windows-x64-setup.exe
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 14
|
|
||||||
|
|
||||||
linux-deb:
|
|
||||||
name: Linux x64 DEB
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: 24
|
|
||||||
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
|
||||||
|
|
||||||
- name: Install Tauri Linux prerequisites
|
|
||||||
run: |
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install -y \
|
|
||||||
build-essential \
|
|
||||||
curl \
|
|
||||||
wget \
|
|
||||||
file \
|
|
||||||
libwebkit2gtk-4.1-dev \
|
|
||||||
libxdo-dev \
|
|
||||||
libssl-dev \
|
|
||||||
libayatana-appindicator3-dev \
|
|
||||||
librsvg2-dev \
|
|
||||||
patchelf
|
|
||||||
|
|
||||||
- name: Install Node dependencies
|
|
||||||
run: npm install
|
|
||||||
|
|
||||||
- name: Build Debian package
|
|
||||||
run: npm run bundle:deb --workspace @fieldlens/print
|
|
||||||
|
|
||||||
- name: Stage Debian package
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
source_file="$(find apps/print/src-tauri/target/release/bundle/deb -maxdepth 1 -type f -name '*.deb' -print -quit)"
|
|
||||||
test -n "$source_file"
|
|
||||||
mkdir -p dist/desktop
|
|
||||||
cp "$source_file" dist/desktop/fieldlens-print-linux-x64.deb
|
|
||||||
|
|
||||||
- name: Upload Debian package
|
|
||||||
uses: actions/upload-artifact@v3
|
|
||||||
with:
|
|
||||||
name: fieldlens-print-linux-x64-deb
|
|
||||||
path: dist/desktop/fieldlens-print-linux-x64.deb
|
|
||||||
if-no-files-found: error
|
|
||||||
retention-days: 14
|
|
||||||
|
|
||||||
publish-release:
|
|
||||||
name: Publish tagged desktop release
|
|
||||||
if: gitea.ref == 'refs/heads/main' || startsWith(gitea.ref, 'refs/tags/fieldlens-print-v')
|
|
||||||
needs: [windows-nsis, linux-deb]
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
permissions:
|
|
||||||
code: read
|
|
||||||
releases: write
|
|
||||||
steps:
|
|
||||||
- name: Download Windows installer
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: fieldlens-print-windows-x64
|
|
||||||
path: release-assets
|
|
||||||
|
|
||||||
- name: Download Debian package
|
|
||||||
uses: actions/download-artifact@v3
|
|
||||||
with:
|
|
||||||
name: fieldlens-print-linux-x64-deb
|
|
||||||
path: release-assets
|
|
||||||
|
|
||||||
- name: Publish Gitea release assets
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
|
||||||
GITEA_SERVER_URL: ${{ gitea.server_url }}
|
|
||||||
GITEA_REPOSITORY: ${{ gitea.repository }}
|
|
||||||
GITEA_REF: ${{ gitea.ref }}
|
|
||||||
GITEA_REF_NAME: ${{ gitea.ref_name }}
|
|
||||||
GITEA_SHA: ${{ gitea.sha }}
|
|
||||||
run: |
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
api="${GITEA_SERVER_URL%/}/api/v1/repos/${GITEA_REPOSITORY}"
|
|
||||||
auth_header="Authorization: token ${GITEA_TOKEN}"
|
|
||||||
release_tag="$GITEA_REF_NAME"
|
|
||||||
if [ "$GITEA_REF" = "refs/heads/main" ]; then
|
|
||||||
release_tag="fieldlens-print-v0.1.0"
|
|
||||||
fi
|
|
||||||
|
|
||||||
status="$(curl -sS -o /tmp/release.json -w '%{http_code}' \
|
|
||||||
-H "$auth_header" \
|
|
||||||
"$api/releases/tags/$release_tag")"
|
|
||||||
|
|
||||||
if [ "$status" = "404" ]; then
|
|
||||||
jq -n \
|
|
||||||
--arg tag "$release_tag" \
|
|
||||||
--arg sha "$GITEA_SHA" \
|
|
||||||
'{
|
|
||||||
tag_name: $tag,
|
|
||||||
target_commitish: $sha,
|
|
||||||
name: ("FieldLens Print " + ($tag | sub("^fieldlens-print-"; ""))),
|
|
||||||
body: "FieldLens Print desktop installers.",
|
|
||||||
draft: false,
|
|
||||||
prerelease: false
|
|
||||||
}' > /tmp/create-release.json
|
|
||||||
|
|
||||||
curl -fsS \
|
|
||||||
-X POST \
|
|
||||||
-H "$auth_header" \
|
|
||||||
-H "Content-Type: application/json" \
|
|
||||||
--data-binary @/tmp/create-release.json \
|
|
||||||
"$api/releases" > /tmp/release.json
|
|
||||||
elif [ "$status" != "200" ]; then
|
|
||||||
cat /tmp/release.json
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
release_id="$(jq -r '.id' /tmp/release.json)"
|
|
||||||
test "$release_id" != "null"
|
|
||||||
|
|
||||||
curl -fsS -H "$auth_header" "$api/releases/$release_id/assets" > /tmp/assets.json
|
|
||||||
|
|
||||||
for file in release-assets/fieldlens-print-windows-x64-setup.exe release-assets/fieldlens-print-linux-x64.deb; do
|
|
||||||
name="$(basename "$file")"
|
|
||||||
existing_id="$(jq -r --arg name "$name" '.[] | select(.name == $name) | .id' /tmp/assets.json | head -n 1)"
|
|
||||||
if [ -n "$existing_id" ] && [ "$existing_id" != "null" ]; then
|
|
||||||
curl -fsS -X DELETE -H "$auth_header" "$api/releases/$release_id/assets/$existing_id"
|
|
||||||
fi
|
|
||||||
|
|
||||||
curl -fsS \
|
|
||||||
-X POST \
|
|
||||||
-H "$auth_header" \
|
|
||||||
-F "attachment=@$file" \
|
|
||||||
"$api/releases/$release_id/assets?name=$name"
|
|
||||||
done
|
|
||||||
|
|||||||
52
Dockerfile
52
Dockerfile
@ -1,53 +1,3 @@
|
|||||||
FROM node:24-bookworm AS desktop-build
|
|
||||||
WORKDIR /workspace
|
|
||||||
|
|
||||||
RUN apt-get update \
|
|
||||||
&& apt-get install -y --no-install-recommends \
|
|
||||||
build-essential \
|
|
||||||
clang \
|
|
||||||
curl \
|
|
||||||
file \
|
|
||||||
libayatana-appindicator3-dev \
|
|
||||||
libssl-dev \
|
|
||||||
libwebkit2gtk-4.1-dev \
|
|
||||||
libxdo-dev \
|
|
||||||
lld \
|
|
||||||
llvm \
|
|
||||||
nsis \
|
|
||||||
patchelf \
|
|
||||||
pkg-config \
|
|
||||||
librsvg2-dev \
|
|
||||||
wget \
|
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
|
|
||||||
| sh -s -- -y --profile minimal
|
|
||||||
ENV PATH="/root/.cargo/bin:$PATH"
|
|
||||||
|
|
||||||
RUN rustup target add x86_64-pc-windows-msvc \
|
|
||||||
&& cargo install --locked cargo-xwin
|
|
||||||
|
|
||||||
COPY package.json ./
|
|
||||||
COPY apps/api/package.json apps/api/package.json
|
|
||||||
COPY apps/web/package.json apps/web/package.json
|
|
||||||
COPY apps/print/package.json apps/print/package.json
|
|
||||||
COPY packages/contracts/package.json packages/contracts/package.json
|
|
||||||
COPY packages/fieldlens-core/package.json packages/fieldlens-core/package.json
|
|
||||||
|
|
||||||
RUN npm install
|
|
||||||
|
|
||||||
COPY apps/print apps/print
|
|
||||||
|
|
||||||
RUN npm run bundle:windows --workspace @fieldlens/print \
|
|
||||||
&& npm run bundle:deb --workspace @fieldlens/print \
|
|
||||||
&& mkdir -p /desktop-artifacts \
|
|
||||||
&& windows_file="$(find apps/print/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis -maxdepth 1 -type f -name '*-setup.exe' -print -quit)" \
|
|
||||||
&& linux_file="$(find apps/print/src-tauri/target/release/bundle/deb -maxdepth 1 -type f -name '*.deb' -print -quit)" \
|
|
||||||
&& test -n "$windows_file" \
|
|
||||||
&& test -n "$linux_file" \
|
|
||||||
&& cp "$windows_file" /desktop-artifacts/fieldlens-print-windows-x64-setup.exe \
|
|
||||||
&& cp "$linux_file" /desktop-artifacts/fieldlens-print-linux-x64.deb
|
|
||||||
|
|
||||||
FROM node:24-alpine AS build
|
FROM node:24-alpine AS build
|
||||||
WORKDIR /workspace
|
WORKDIR /workspace
|
||||||
|
|
||||||
@ -81,7 +31,6 @@ WORKDIR /app
|
|||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV PORT=3000
|
ENV PORT=3000
|
||||||
ENV STATIC_DIR=/app/web
|
ENV STATIC_DIR=/app/web
|
||||||
ENV FIELDLENS_PRINT_DOWNLOAD_DIR=/app/downloads
|
|
||||||
|
|
||||||
COPY --from=build /workspace/package.json /app/package.json
|
COPY --from=build /workspace/package.json /app/package.json
|
||||||
COPY --from=build /workspace/node_modules /app/node_modules
|
COPY --from=build /workspace/node_modules /app/node_modules
|
||||||
@ -91,7 +40,6 @@ COPY --from=build /workspace/migrations /app/migrations
|
|||||||
COPY --from=build /workspace/packages/contracts/package.json /app/packages/contracts/package.json
|
COPY --from=build /workspace/packages/contracts/package.json /app/packages/contracts/package.json
|
||||||
COPY --from=build /workspace/packages/contracts/dist /app/packages/contracts/dist
|
COPY --from=build /workspace/packages/contracts/dist /app/packages/contracts/dist
|
||||||
COPY --from=build /workspace/apps/web/dist /app/web
|
COPY --from=build /workspace/apps/web/dist /app/web
|
||||||
COPY --from=desktop-build /desktop-artifacts /app/downloads
|
|
||||||
|
|
||||||
USER node
|
USER node
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|||||||
20
README.md
20
README.md
@ -62,20 +62,18 @@ The Windows NSIS bundle is configured for `currentUser` installation, so install
|
|||||||
|
|
||||||
### Desktop release artifacts
|
### Desktop release artifacts
|
||||||
|
|
||||||
The `.gitea/workflows/desktop-release.yml` workflow packages FieldLens Print on changes to the desktop app, on `main`, and on `fieldlens-print-v*` tags.
|
Desktop packaging is intentionally separate from the PWA/API deployment.
|
||||||
|
|
||||||
Artifacts:
|
- Private source of truth: `Admin/fieldlens/apps/print`
|
||||||
|
- Public desktop build/release mirror: `Admin/fieldlens-desktop`
|
||||||
|
- PWA/API deploy mirror: `Admin/fieldlens-deploy`
|
||||||
|
|
||||||
- **Windows x64:** NSIS current-user installer from `apps/print/src-tauri/target/x86_64-pc-windows-msvc/release/bundle/nsis/*-setup.exe`
|
The desktop mirror builds Windows and Linux installers with Gitea Actions and publishes public release assets. Normal FieldLens web/API deployments do not install Rust, Tauri, cargo-xwin, NSIS, or Linux desktop packaging dependencies.
|
||||||
- **Linux x64:** Debian package from `apps/print/src-tauri/target/release/bundle/deb/*.deb`
|
|
||||||
|
|
||||||
After a successful Gitea Actions run, download the corresponding `fieldlens-print-windows-x64` or `fieldlens-print-linux-x64-deb` artifact from the workflow run.
|
Current public release assets:
|
||||||
|
|
||||||
Local packaging commands:
|
- `fieldlens-print-windows-x64-setup.exe`
|
||||||
|
- `fieldlens-print-linux-x64.deb`
|
||||||
|
|
||||||
```bash
|
The FieldLens `/downloads` page keeps stable same-site links; the API redirects those links to the configured desktop release tag.
|
||||||
npm run bundle:windows --workspace @fieldlens/print
|
|
||||||
npm run bundle:deb --workspace @fieldlens/print
|
|
||||||
```
|
|
||||||
|
|
||||||
The Windows package is cross-compiled with `cargo-xwin` and bundled with NSIS. MSI packaging is intentionally not part of the Linux runner workflow because WiX/MSI bundling requires Windows.
|
|
||||||
|
|||||||
@ -14,3 +14,6 @@ STATIC_DIR=
|
|||||||
FIELDLENS_PRINT_RELEASE_TAG=fieldlens-print-v0.1.0
|
FIELDLENS_PRINT_RELEASE_TAG=fieldlens-print-v0.1.0
|
||||||
FIELDLENS_PRINT_RELEASE_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download
|
FIELDLENS_PRINT_RELEASE_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download
|
||||||
FIELDLENS_PRINT_DOWNLOAD_DIR=downloads
|
FIELDLENS_PRINT_DOWNLOAD_DIR=downloads
|
||||||
|
FIELDLENS_DESKTOP_RELEASE_BASE_URL=https://git.arrowstack.dev/Admin/fieldlens-desktop/releases/download
|
||||||
|
FIELDLENS_DESKTOP_RELEASE_TAG=v0.1.0
|
||||||
|
FIELDLENS_DESKTOP_DOWNLOAD_BASE_URL=https://git.arrowstack.dev/Admin/fieldlens-desktop/raw/branch/main/downloads
|
||||||
|
|||||||
@ -18,5 +18,5 @@ export const config = {
|
|||||||
logLevel: process.env.LOG_LEVEL?.trim() || 'info',
|
logLevel: process.env.LOG_LEVEL?.trim() || 'info',
|
||||||
webOrigin: process.env.WEB_ORIGIN?.trim() || 'http://localhost:5173',
|
webOrigin: process.env.WEB_ORIGIN?.trim() || 'http://localhost:5173',
|
||||||
staticDir: process.env.STATIC_DIR?.trim() || '',
|
staticDir: process.env.STATIC_DIR?.trim() || '',
|
||||||
printDownloadDir: process.env.FIELDLENS_PRINT_DOWNLOAD_DIR?.trim() || 'downloads',
|
desktopDownloadBaseUrl: process.env.FIELDLENS_DESKTOP_DOWNLOAD_BASE_URL?.trim() || 'https://git.arrowstack.dev/Admin/fieldlens-desktop/raw/branch/main/downloads',
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import path from 'node:path'
|
import path from 'node:path'
|
||||||
import cors from 'cors'
|
import cors from 'cors'
|
||||||
import express, { type Response } from 'express'
|
import express from 'express'
|
||||||
import helmet from 'helmet'
|
import helmet from 'helmet'
|
||||||
import { FIELDLENS_SCHEMA_VERSION, isSyncRequest, type DeviceRegistration } from '@fieldlens/contracts'
|
import { FIELDLENS_SCHEMA_VERSION, isSyncRequest, type DeviceRegistration } from '@fieldlens/contracts'
|
||||||
import { getAccessRequest, listAccessRequests, requestAccess, reviewAccessRequest } from './access.js'
|
import { getAccessRequest, listAccessRequests, requestAccess, reviewAccessRequest } from './access.js'
|
||||||
@ -61,33 +61,27 @@ app.get('/api/health/live', (_req, res) => {
|
|||||||
res.json({ status: 'live', version: process.env.FIELDLENS_VERSION ?? 'dev' })
|
res.json({ status: 'live', version: process.env.FIELDLENS_VERSION ?? 'dev' })
|
||||||
})
|
})
|
||||||
|
|
||||||
const desktopDownloadDir = path.resolve(config.printDownloadDir)
|
function redirectDesktopDownload(res: express.Response, filename: string) {
|
||||||
|
const base = config.desktopDownloadBaseUrl.replace(/\/$/, '')
|
||||||
function sendDesktopDownload(res: Response, filename: string) {
|
res.setHeader('Cache-Control', 'no-store')
|
||||||
const file = path.join(desktopDownloadDir, filename)
|
res.redirect(302, `${base}/${filename}`)
|
||||||
res.setHeader('Cache-Control', 'public, max-age=300')
|
|
||||||
res.download(file, filename, (error) => {
|
|
||||||
if (!error || res.headersSent) return
|
|
||||||
log('error', 'desktop_download_missing', { filename, file })
|
|
||||||
res.status(404).json({ error: 'desktop_download_unavailable' })
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
app.get('/downloads/fieldlens-print-windows-x64-setup.exe', (_req, res) => {
|
app.get('/downloads/fieldlens-print-windows-x64-setup.exe', (_req, res) => {
|
||||||
sendDesktopDownload(res, 'fieldlens-print-windows-x64-setup.exe')
|
redirectDesktopDownload(res, 'fieldlens-print-windows-x64-setup.exe')
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/downloads/fieldlens-print-linux-x64.deb', (_req, res) => {
|
app.get('/downloads/fieldlens-print-linux-x64.deb', (_req, res) => {
|
||||||
sendDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
|
redirectDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
|
||||||
})
|
})
|
||||||
|
|
||||||
// Backward-compatible aliases used by the first downloads page.
|
// Backward-compatible aliases used by the first downloads page.
|
||||||
app.get('/downloads/fieldlens-print/windows-x64', (_req, res) => {
|
app.get('/downloads/fieldlens-print/windows-x64', (_req, res) => {
|
||||||
sendDesktopDownload(res, 'fieldlens-print-windows-x64-setup.exe')
|
redirectDesktopDownload(res, 'fieldlens-print-windows-x64-setup.exe')
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/downloads/fieldlens-print/linux-x64.deb', (_req, res) => {
|
app.get('/downloads/fieldlens-print/linux-x64.deb', (_req, res) => {
|
||||||
sendDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
|
redirectDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
|
||||||
})
|
})
|
||||||
|
|
||||||
app.get('/api/health/ready', async (_req, res) => {
|
app.get('/api/health/ready', async (_req, res) => {
|
||||||
|
|||||||
@ -15,7 +15,7 @@ const downloads = [
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
export function DesktopDownloadsLink() {
|
export function DesktopDownloadsLink({ onOpen }: { onOpen: () => void }) {
|
||||||
return (
|
return (
|
||||||
<section className="settings-section desktop-downloads-link">
|
<section className="settings-section desktop-downloads-link">
|
||||||
<header>
|
<header>
|
||||||
@ -23,22 +23,22 @@ export function DesktopDownloadsLink() {
|
|||||||
<div><h2>Desktop app</h2><p>Capture printed barcodes without a physical scan</p></div>
|
<div><h2>Desktop app</h2><p>Capture printed barcodes without a physical scan</p></div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<a className="desktop-download-settings-link" href="/downloads">
|
<button className="desktop-download-settings-link" type="button" onClick={onOpen}>
|
||||||
<span>
|
<span>
|
||||||
<strong>Get the desktop app</strong>
|
<strong>Get the desktop app</strong>
|
||||||
<small>Windows and Linux installers</small>
|
<small>Windows and Linux installers</small>
|
||||||
</span>
|
</span>
|
||||||
<span className="desktop-download-action"><Download size={13} /> Downloads</span>
|
<span className="desktop-download-action"><Download size={13} /> Downloads</span>
|
||||||
</a>
|
</button>
|
||||||
</section>
|
</section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function DesktopDownloadsPage() {
|
export function DesktopDownloadsPage({ onBack }: { onBack: () => void }) {
|
||||||
return (
|
return (
|
||||||
<main className="downloads-page">
|
<main className="downloads-page">
|
||||||
<div className="downloads-shell">
|
<div className="downloads-shell">
|
||||||
<a className="downloads-back" href="/"><ArrowLeft size={16} /> Back to FieldLens</a>
|
<button className="downloads-back" type="button" onClick={onBack}><ArrowLeft size={16} /> Back to FieldLens</button>
|
||||||
|
|
||||||
<header className="downloads-hero">
|
<header className="downloads-hero">
|
||||||
<span className="downloads-mark">FL</span>
|
<span className="downloads-mark">FL</span>
|
||||||
|
|||||||
@ -235,3 +235,23 @@
|
|||||||
.downloads-panel-heading { padding: 18px 17px 15px; }
|
.downloads-panel-heading { padding: 18px 17px 15px; }
|
||||||
.downloads-notes { padding: 13px 16px 16px; }
|
.downloads-notes { padding: 13px 16px 16px; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.host-app-behind-page {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.desktop-download-settings-link {
|
||||||
|
width: calc(100% - 36px);
|
||||||
|
cursor: pointer;
|
||||||
|
text-align: left;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.downloads-back {
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
|
background: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { StrictMode } from 'react'
|
import { StrictMode, useEffect, useState } from 'react'
|
||||||
import { createRoot } from 'react-dom/client'
|
import { createRoot } from 'react-dom/client'
|
||||||
|
|
||||||
import '../../../packages/fieldlens-core/src/index.css'
|
import '../../../packages/fieldlens-core/src/index.css'
|
||||||
@ -19,15 +19,62 @@ startDeviceHoldTracking()
|
|||||||
registerArrowstackReferenceProvider()
|
registerArrowstackReferenceProvider()
|
||||||
installSyncTriggers()
|
installSyncTriggers()
|
||||||
|
|
||||||
const isDownloadsPage = window.location.pathname === '/downloads'
|
function HostApp() {
|
||||||
const root = createRoot(document.getElementById('root')!)
|
const initialDownloadsPage = window.location.pathname === '/downloads'
|
||||||
|
const [downloadsOpen, setDownloadsOpen] = useState(initialDownloadsPage)
|
||||||
|
const [appMounted, setAppMounted] = useState(!initialDownloadsPage)
|
||||||
|
|
||||||
root.render(
|
useEffect(() => {
|
||||||
<StrictMode>
|
const onPopState = () => {
|
||||||
{isDownloadsPage ? (
|
const open = window.location.pathname === '/downloads'
|
||||||
<DesktopDownloadsPage />
|
setDownloadsOpen(open)
|
||||||
) : (
|
if (!open) setAppMounted(true)
|
||||||
<CoreApp settingsAddon={<><AccountSettings /><DesktopDownloadsLink /></>} />
|
}
|
||||||
|
|
||||||
|
window.addEventListener('popstate', onPopState)
|
||||||
|
return () => window.removeEventListener('popstate', onPopState)
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const openDownloads = () => {
|
||||||
|
setAppMounted(true)
|
||||||
|
if (window.location.pathname !== '/downloads') {
|
||||||
|
window.history.pushState({ fieldlensPage: 'downloads' }, '', '/downloads')
|
||||||
|
}
|
||||||
|
setDownloadsOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeDownloads = () => {
|
||||||
|
if (window.location.pathname === '/downloads' && window.history.length > 1) {
|
||||||
|
window.history.back()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
window.history.replaceState({}, '', '/')
|
||||||
|
setDownloadsOpen(false)
|
||||||
|
setAppMounted(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{appMounted && (
|
||||||
|
<div className={downloadsOpen ? 'host-app-behind-page' : undefined} aria-hidden={downloadsOpen || undefined}>
|
||||||
|
<CoreApp
|
||||||
|
settingsAddon={(
|
||||||
|
<>
|
||||||
|
<AccountSettings />
|
||||||
|
<DesktopDownloadsLink onOpen={openDownloads} />
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
{downloadsOpen && <DesktopDownloadsPage onBack={closeDownloads} />}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
createRoot(document.getElementById('root')!).render(
|
||||||
|
<StrictMode>
|
||||||
|
<HostApp />
|
||||||
</StrictMode>,
|
</StrictMode>,
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user