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_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download
|
||||
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:
|
||||
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:
|
||||
|
||||
permissions:
|
||||
code: read
|
||||
releases: read
|
||||
|
||||
jobs:
|
||||
windows-nsis:
|
||||
name: Windows x64 NSIS
|
||||
moved:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- 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
|
||||
- run: echo "Desktop builds now run from Admin/fieldlens-desktop"
|
||||
|
||||
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
|
||||
WORKDIR /workspace
|
||||
|
||||
@ -81,7 +31,6 @@ WORKDIR /app
|
||||
ENV NODE_ENV=production
|
||||
ENV PORT=3000
|
||||
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/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/dist /app/packages/contracts/dist
|
||||
COPY --from=build /workspace/apps/web/dist /app/web
|
||||
COPY --from=desktop-build /desktop-artifacts /app/downloads
|
||||
|
||||
USER node
|
||||
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
|
||||
|
||||
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`
|
||||
- **Linux x64:** Debian package from `apps/print/src-tauri/target/release/bundle/deb/*.deb`
|
||||
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.
|
||||
|
||||
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
|
||||
npm run bundle:windows --workspace @fieldlens/print
|
||||
npm run bundle:deb --workspace @fieldlens/print
|
||||
```
|
||||
The FieldLens `/downloads` page keeps stable same-site links; the API redirects those links to the configured desktop release tag.
|
||||
|
||||
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_BASE_URL=https://git.example.invalid/owner/fieldlens/releases/download
|
||||
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',
|
||||
webOrigin: process.env.WEB_ORIGIN?.trim() || 'http://localhost:5173',
|
||||
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 cors from 'cors'
|
||||
import express, { type Response } from 'express'
|
||||
import express from 'express'
|
||||
import helmet from 'helmet'
|
||||
import { FIELDLENS_SCHEMA_VERSION, isSyncRequest, type DeviceRegistration } from '@fieldlens/contracts'
|
||||
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' })
|
||||
})
|
||||
|
||||
const desktopDownloadDir = path.resolve(config.printDownloadDir)
|
||||
|
||||
function sendDesktopDownload(res: Response, filename: string) {
|
||||
const file = path.join(desktopDownloadDir, 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' })
|
||||
})
|
||||
function redirectDesktopDownload(res: express.Response, filename: string) {
|
||||
const base = config.desktopDownloadBaseUrl.replace(/\/$/, '')
|
||||
res.setHeader('Cache-Control', 'no-store')
|
||||
res.redirect(302, `${base}/${filename}`)
|
||||
}
|
||||
|
||||
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) => {
|
||||
sendDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
|
||||
redirectDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
|
||||
})
|
||||
|
||||
// Backward-compatible aliases used by the first downloads page.
|
||||
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) => {
|
||||
sendDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
|
||||
redirectDesktopDownload(res, 'fieldlens-print-linux-x64.deb')
|
||||
})
|
||||
|
||||
app.get('/api/health/ready', async (_req, res) => {
|
||||
|
||||
@ -15,7 +15,7 @@ const downloads = [
|
||||
},
|
||||
]
|
||||
|
||||
export function DesktopDownloadsLink() {
|
||||
export function DesktopDownloadsLink({ onOpen }: { onOpen: () => void }) {
|
||||
return (
|
||||
<section className="settings-section desktop-downloads-link">
|
||||
<header>
|
||||
@ -23,22 +23,22 @@ export function DesktopDownloadsLink() {
|
||||
<div><h2>Desktop app</h2><p>Capture printed barcodes without a physical scan</p></div>
|
||||
</header>
|
||||
|
||||
<a className="desktop-download-settings-link" href="/downloads">
|
||||
<button className="desktop-download-settings-link" type="button" onClick={onOpen}>
|
||||
<span>
|
||||
<strong>Get the desktop app</strong>
|
||||
<small>Windows and Linux installers</small>
|
||||
</span>
|
||||
<span className="desktop-download-action"><Download size={13} /> Downloads</span>
|
||||
</a>
|
||||
</button>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export function DesktopDownloadsPage() {
|
||||
export function DesktopDownloadsPage({ onBack }: { onBack: () => void }) {
|
||||
return (
|
||||
<main className="downloads-page">
|
||||
<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">
|
||||
<span className="downloads-mark">FL</span>
|
||||
|
||||
@ -235,3 +235,23 @@
|
||||
.downloads-panel-heading { padding: 18px 17px 15px; }
|
||||
.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 '../../../packages/fieldlens-core/src/index.css'
|
||||
@ -19,15 +19,62 @@ startDeviceHoldTracking()
|
||||
registerArrowstackReferenceProvider()
|
||||
installSyncTriggers()
|
||||
|
||||
const isDownloadsPage = window.location.pathname === '/downloads'
|
||||
const root = createRoot(document.getElementById('root')!)
|
||||
function HostApp() {
|
||||
const initialDownloadsPage = window.location.pathname === '/downloads'
|
||||
const [downloadsOpen, setDownloadsOpen] = useState(initialDownloadsPage)
|
||||
const [appMounted, setAppMounted] = useState(!initialDownloadsPage)
|
||||
|
||||
root.render(
|
||||
useEffect(() => {
|
||||
const onPopState = () => {
|
||||
const open = window.location.pathname === '/downloads'
|
||||
setDownloadsOpen(open)
|
||||
if (!open) setAppMounted(true)
|
||||
}
|
||||
|
||||
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>
|
||||
{isDownloadsPage ? (
|
||||
<DesktopDownloadsPage />
|
||||
) : (
|
||||
<CoreApp settingsAddon={<><AccountSettings /><DesktopDownloadsLink /></>} />
|
||||
)}
|
||||
<HostApp />
|
||||
</StrictMode>,
|
||||
)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user