Compare commits

..
Author SHA1 Message Date
FredApps 71adfd4701 artcraft app: mark fire-and-forget promises with void (Sonar S9383) (#2806)
## Summary

Resolves all 38 SonarCloud **typescript:S9383** findings ("Promises must
be awaited, end with a call to .catch, end with a call to .then with a
rejection handler or be explicitly marked as ignored with the `void`
operator") in `frontend/apps/artcraft/app`, across 19 files. They are:

- startup calls in `index.tsx`, such as `SyncStorytellerApiConfig()` and
`persistLogin()`
- polling in `useActiveJobs`, `useBackgroundLoadingMedia`,
`signals/activeJobs.ts` and `signals/authentication/fetchers.ts`
- listener cleanups (`unlisten*.then(f => f())`) in `TaskQueue` and
`useDesktopGenerationFeed`
- UI handlers that start uploads or renames, such as
`ImageTo3DExperience`, `UploadFilesMedia`, `SceneTitleInput`,
`ProfileDropdown` and `DesktopGenerationGallery`
- async IIFEs in `useDesktopUsername`, `TaskQueue` and
`BaseImageSelector`

This is part 2 of 3 for artcraft's 157 S9383 findings. Part 1 (Tauri
libraries) is #2805. Part 3 (`libs/components`) is #2807.

Several of these files are also changed by open PRs for unrelated
reasons, for example #2519, #2520, #2615 and #2064. Each change here
inserts one word at the start of a statement, so a rebase either way is
trivial.

## Approach

This ports the approach of storytold/artcraft-services#179, which fixes
the same rule in artcraft-services. **#179 is still open and unmerged.**
This PR follows its approach and does not depend on it.

- Each flagged expression is a fire-and-forget promise: a listener
`setup()`, an `unlisten.then(f => f())` cleanup, a loader called from an
effect, an upload kicked off from a handler, or an async IIFE. Prefixing
it with `void` marks the ignored promise explicitly, which is the fix
the rule documents.
- It deliberately does not add `.catch()` everywhere. These call sites
already handle failures inside the async function or surface them
through their own state, and empty handlers would hide errors.
- Async IIFEs become `void (async () => …)()`. `if (cond) call()`
becomes `if (cond) void call()`.
- There is no behaviour change. `void` only discards the promise value,
which was already being discarded.

Each insertion is placed at the exact `textRange` start offset of the
SonarCloud finding, with no other edits.

## Testing

- Each changed file parses with `esbuild <file>`, using the same check
as #179.
- I parsed each changed file with the TypeScript 5.9 compiler API
(`createSourceFile`). That found no parse errors, and every inserted
`void` is either a whole expression statement or an arrow-function body
applied to a call, never part of a larger expression.
- `frontend/.eslintrc.json` has no `no-void` rule.
- I did not run the full Nx type-check, lint or test targets, because
`frontend/node_modules` is not installed in my environment. Please rely
on CI.
- No tests are added. Discarding an already-discarded promise value is
not observable, so no test can fail before this change and pass after
it.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Chores**
* Updated background tasks across uploads, sign-in, polling, scene
editing, and other app workflows to handle asynchronous operations
consistently.
* Existing workflows and their success and error handling remain
unchanged; no user-facing behavior changes are expected.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-11-10 21:35:11 +01:00
FredApps c9da5cac18 tauri-events/tauri-api/tauri-utils: mark fire-and-forget promises with void (Sonar S9383) (#2805)
## Summary

Resolves all 53 SonarCloud **typescript:S9383** findings ("Promises must
be awaited, end with a call to .catch, end with a call to .then with a
rejection handler or be explicitly marked as ignored with the `void`
operator") in the Tauri frontend libraries:

| Library | Findings | What is marked |

|----------------------------|----------|------------------------------------------------------------------------|
| `frontend/libs/tauri-events` | 48 | `setup()` and the `unlisten.then(f
=> f())` cleanups in each `use*Event` hook |
| `frontend/libs/tauri-api` | 4 | Same pattern in
`CanvasBgRemovedEvent`, plus `setupListener()` in `useSoraLoginListener`
|
| `frontend/libs/tauri-utils` | 1 | The async IIFE in
`useTauriWindowControls` |

This is part 1 of 3 for artcraft's 157 S9383 findings. Part 2
(`apps/artcraft/app`) is #2806, and part 3 (`libs/components`) is #2807.

## Approach

This ports the approach of storytold/artcraft-services#179, which fixes
the same rule in artcraft-services. **#179 is still open and unmerged.**
This PR follows its approach and does not depend on it.

- Each flagged expression is a fire-and-forget promise: a listener
`setup()`, an `unlisten.then(f => f())` cleanup, a loader called from an
effect, an upload kicked off from a handler, or an async IIFE. Prefixing
it with `void` marks the ignored promise explicitly, which is the fix
the rule documents.
- It deliberately does not add `.catch()` everywhere. These call sites
already handle failures inside the async function or surface them
through their own state, and empty handlers would hide errors.
- Async IIFEs become `void (async () => …)()`. `if (cond) call()`
becomes `if (cond) void call()`.
- There is no behaviour change. `void` only discards the promise value,
which was already being discarded.

Each insertion is placed at the exact `textRange` start offset of the
SonarCloud finding, with no other edits.

## Testing

- Each changed file parses with `esbuild <file>`, using the same check
as #179.
- I parsed each changed file with the TypeScript 5.9 compiler API
(`createSourceFile`). That found no parse errors, and every inserted
`void` is either a whole expression statement or an arrow-function body
applied to a call, never part of a larger expression.
- `frontend/.eslintrc.json` has no `no-void` rule.
- I did not run the full Nx type-check, lint or test targets, because
`frontend/node_modules` is not installed in my environment. Please rely
on CI.
- No tests are added. Discarding an already-discarded promise value is
not observable, so no test can fail before this change and pass after
it.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Refactor**
* Clarified that asynchronous event listener setup, cleanup, and
window-control initialization run without blocking. Existing
subscription behavior and cleanup conditions remain unchanged; no
user-facing functionality changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-11-10 21:21:36 +01:00
FredApps 83aad5eb54 Exclude TypeScript and JavaScript from Sonar coverage (#2813)
## Summary

Adds `sonar.coverage.exclusions=**/*.ts,**/*.tsx,**/*.js,**/*.jsx` to
`sonar-project.properties`. There were no existing exclusion entries.

The shared SonarCloud workflow only publishes the Rust lcov report
(`sonar.rust.lcov.reportPaths=lcov.info`). Artcraft's workflow wrapper
does not pass a TypeScript or JavaScript coverage report, so those files
can never appear covered.

That is why [#2805](https://github.com/storytold/artcraft/pull/2805) and
[#2806](https://github.com/storytold/artcraft/pull/2806) fail the
quality gate on **new_coverage** (0.0% against the 80% target) after the
organisation was recreated. They only add `void` in front of existing
TypeScript calls:

- #2805: 53 new lines to cover, all uncovered
- #2806: 38 new lines to cover, all uncovered

[#2807](https://github.com/storytold/artcraft/pull/2807) also touches
TypeScript (`void` in `libs/components`) and is listed here for the same
exclusion, but its current gate failure is duplicated new code (10.6%),
which this PR does not address.

Same approach as
[anicraft#106](https://github.com/storytold/anicraft/pull/106), which
excluded JS, PowerShell and Python for the same reason. filmcraft and
lightcraft already exclude `**/tools/**,**/docs/**,**/examples/**`. This
PR uses extensions because the uncovered TypeScript and JavaScript live
under `frontend/`.

No product code changes. No open PR already edits
`sonar-project.properties`.

## Testing

Visual review of `sonar-project.properties` against anicraft, filmcraft
and lightcraft, and of the shared `sonarcloud.yml` coverage inputs. No
Rust or frontend tests were run (properties file only). The quality-gate
effect on #2805 and #2806 will be visible after this merges and those
branches are updated from main.

- [x] Clean room: no proprietary code, scripts or assets copied
(AGENTS.md)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

<!-- coderabbit-ai-generated:v1 -->

## Summary by CodeRabbit

* **Chores**
* Updated project configuration for coverage reporting. No user-facing
changes.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-11-10 21:07:25 +01:00
43 changed files with 117 additions and 162 deletions
-4
View File
@@ -14,8 +14,6 @@ updates:
- "*"
commit-message:
prefix: "chore(deps)"
cooldown:
default-days: 7
# Maintain GitHub Actions workflows
- package-ecosystem: "github-actions"
@@ -30,5 +28,3 @@ updates:
- "*"
commit-message:
prefix: "ci(deps)"
cooldown:
default-days: 7
+12 -42
View File
@@ -13,13 +13,7 @@ jobs:
runs-on: ubuntu-22.04
timeout-minutes: 10
steps:
- name: Harden runner
uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/checkout@v7
- uses: storytold/craft-common-actions/actions/install-apt-packages@main
with:
packages: rpm jq binutils zsync
@@ -40,13 +34,7 @@ jobs:
TAURI_FRONTEND_PATH: ${{ github.workspace }}/frontend
TAURI_APP_PATH: ${{ github.workspace }}/crates/desktop/artcraft
steps:
- name: Harden runner
uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/checkout@v7
- uses: storytold/craft-common-actions/actions/install-apt-packages@main
with:
packages: libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf libssl-dev libclang-dev libasound2-dev libxdo-dev libdbus-1-dev build-essential clang file jq perl cmake nasm rpm zsync gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-libav
@@ -75,7 +63,7 @@ jobs:
PKG_CONFIG_LIBDIR_riscv64gc_unknown_linux_gnu=/usr/lib/riscv64-linux-gnu/pkgconfig:/usr/share/pkgconfig
ENV
echo "CMAKE_TOOLCHAIN_FILE_riscv64gc_unknown_linux_gnu=$GITHUB_WORKSPACE/packaging/linux/riscv64.cmake" >> "$GITHUB_ENV"
- uses: actions/setup-node@0a44ba7841725637a54e2801669c06500c541b61 # v7.0.0
- uses: actions/setup-node@v7
with:
node-version: '22'
cache: npm
@@ -92,7 +80,7 @@ jobs:
working-directory: frontend
- run: ./node_modules/.bin/nx run artcraft:build
working-directory: frontend
- uses: tauri-apps/tauri-action@fce9c6108b31ea247710505d3aaaa893ee6768d4 # v0.5.18
- uses: tauri-apps/tauri-action@fce9c6108b31ea247710505d3aaaa893ee6768d4 # v0
with:
projectPath: crates/desktop/artcraft
args: --target ${{ matrix.target }} --bundles ${{ matrix.bundles }} --config ${{ github.workspace }}/packaging/linux/tauri.conf.json
@@ -101,7 +89,7 @@ jobs:
ARCH: ${{ matrix.arch }}
TARGET: ${{ matrix.target }}
run: packaging/linux/package.sh "$ARCH" "$TARGET"
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- uses: actions/upload-artifact@v7
with:
name: release-linux-${{ matrix.arch }}
path: dist/release/*
@@ -114,14 +102,8 @@ jobs:
matrix:
arch: [x86_64, aarch64]
steps:
- name: Harden runner
uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
name: release-linux-${{ matrix.arch }}
path: dist/linux
@@ -144,7 +126,7 @@ jobs:
for package in dist/release/*.pkg.tar.zst; do
tar --zstd -xOf "$package" .PKGINFO | grep -Fx 'depend = webkit2gtk-4.1'
done
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- uses: actions/upload-artifact@v7
with:
name: release-arch-${{ matrix.arch }}
path: dist/release/*.pkg.tar.zst
@@ -163,14 +145,8 @@ jobs:
- {arch: x86_64, runner: ubuntu-24.04}
- {arch: aarch64, runner: ubuntu-24.04-arm}
steps:
- name: Harden runner
uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
- uses: actions/checkout@v7
- uses: actions/download-artifact@v8
with:
name: release-linux-${{ matrix.arch }}
path: dist/linux
@@ -188,7 +164,7 @@ jobs:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
run: packaging/linux/flatpak.sh
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
- uses: actions/upload-artifact@v7
with:
name: release-flatpak-${{ matrix.arch }}
path: |
@@ -201,13 +177,7 @@ jobs:
permissions:
contents: write
steps:
- name: Harden runner
uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/checkout@v7
- name: Read version
id: version
run: echo "value=$(jq -r .version crates/desktop/artcraft/tauri.conf.json)" >> "$GITHUB_OUTPUT"
+4 -12
View File
@@ -38,24 +38,16 @@ jobs:
# Successful builds took 10-22 min (2026-04 to 2026-11).
timeout-minutes: 60
steps:
- name: Harden runner
uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/checkout@v7
- name: setup node
uses: actions/setup-node@0a44ba7841725637a54e2801669c06500c541b61 # v7.0.0
uses: actions/setup-node@v7
with:
node-version: lts/*
- name: install Rust stable
uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
targets: ${{ matrix.platform == 'macos-latest-xlarge' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- name: install dependencies (ubuntu only)
@@ -234,7 +226,7 @@ jobs:
- name: build and publish
uses: tauri-apps/tauri-action@fce9c6108b31ea247710505d3aaaa893ee6768d4 # v0.5.18
uses: tauri-apps/tauri-action@v0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_ID: ${{ secrets.APPLE_ID }}
+4 -13
View File
@@ -33,19 +33,10 @@ jobs:
# Successful builds took 17-36 min (2026-04 to 2026-11); one hung run used the full 6-hour default.
timeout-minutes: 90
steps:
- name: Harden runner
uses: step-security/harden-runner@1d88384fd173af6ecb012dfd0264a306b711241a # v2.22.4
with:
egress-policy: audit
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- uses: actions/checkout@v7
- name: install Rust stable
uses: dtolnay/rust-toolchain@683b588c487a69d01f6c6ddf40f036ff682cc2c8 # v1
with:
toolchain: stable
uses: dtolnay/rust-toolchain@stable
- name: install wreq dependencies
run: |
@@ -57,7 +48,7 @@ jobs:
shell: cmd
- name: setup node
uses: actions/setup-node@0a44ba7841725637a54e2801669c06500c541b61 # v7.0.0
uses: actions/setup-node@v7
with:
node-version: lts/*
@@ -110,7 +101,7 @@ jobs:
$config | ConvertTo-Json -Depth 10 | Set-Content tauri.conf.json
- name: build and publish
uses: tauri-apps/tauri-action@fce9c6108b31ea247710505d3aaaa893ee6768d4 # v0.5.18
uses: tauri-apps/tauri-action@v0
env:
# NB(bt): This is needed by wreq. Hopefully it doesn't kill Tauri
RUSTFLAGS: "-C target-feature=+crt-static"
@@ -120,7 +120,7 @@ export function GlobalFileDropHandler() {
}
};
setup();
void setup();
return () => { unlisteners.forEach((fn) => fn()); };
} else {
@@ -256,7 +256,7 @@ export const ImageTo3DExperience = ({
};
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
handleFiles(event.target.files);
void handleFiles(event.target.files);
event.target.value = "";
};
@@ -264,7 +264,7 @@ export const ImageTo3DExperience = ({
event.preventDefault();
event.stopPropagation();
setDragActive(false);
handleFiles(event.dataTransfer?.files);
void handleFiles(event.dataTransfer?.files);
};
const handlePickFromLibrary = () => {
@@ -716,7 +716,7 @@ export const ImageTo3DExperience = ({
e.stopPropagation();
setDragActive(false);
if (e.dataTransfer?.files)
handleWorldFiles(
void handleWorldFiles(
Array.from(e.dataTransfer.files).filter((f) =>
f.type.startsWith("image/"),
),
@@ -1177,7 +1177,7 @@ export const ImageTo3DExperience = ({
className="hidden"
onChange={(e) => {
if (e.target.files) {
handleWorldFiles(Array.from(e.target.files));
void handleWorldFiles(Array.from(e.target.files));
}
e.target.value = "";
}}
@@ -248,7 +248,7 @@ function ItemActions({
e.stopPropagation();
const url = item.fullImage || item.thumbnail;
if (!url) return;
applyMakeVideoFromImage(url, item.id);
void applyMakeVideoFromImage(url, item.id);
},
[item.fullImage, item.thumbnail, item.id],
);
@@ -295,8 +295,8 @@ export function useDesktopGenerationFeed(options: {
cancelled = true;
clearInterval(intervalId);
window.removeEventListener("task-queue-update", handleTaskUpdate);
unlistenComplete.then((f) => f());
unlistenFailed.then((f) => f());
void unlistenComplete.then((f) => f());
void unlistenFailed.then((f) => f());
};
}, [load]);
@@ -8,7 +8,7 @@ export function useDesktopUsername(): string | null {
useEffect(() => {
let cancelled = false;
(async () => {
void (async () => {
try {
const session = await new UsersApi().GetSession();
if (!cancelled && session.success && session.data?.user) {
@@ -48,7 +48,7 @@ export const UploadFilesMedia = ({
return;
}
uploadPlane({
void uploadPlane({
title: uploadTitle.value,
assetFile: assetFile.value,
progressCallback: onUploadProgress,
@@ -26,7 +26,7 @@ export default function ProfileDropdown() {
label: "Logout",
icon: LogOutIcon,
onClick: () => {
logout();
void logout();
},
},
];
@@ -67,7 +67,7 @@ export const SceneTitleInput = ({ pageName }: Props) => {
e.currentTarget.focus();
} else if (scene.value.token) {
setIsSaving(true);
renameScene(
void renameScene(
scene.value.title!, //guarunteed by input
scene.value.token,
);
@@ -164,7 +164,7 @@ const CopyPromptButton = ({ prompt }: { prompt: string }) => {
aria-label="Copy prompt"
onClick={(e) => {
e.stopPropagation();
navigator.clipboard.writeText(prompt);
void navigator.clipboard.writeText(prompt);
setCopied(true);
setTimeout(() => setCopied(false), 3000);
}}
@@ -869,7 +869,7 @@ export const TaskQueue = () => {
};
cleanupOldEntries();
load();
void load();
const id = setInterval(load, 5000);
// Listen for cover image uploads to refresh and show new thumbnails
@@ -883,17 +883,17 @@ export const TaskQueue = () => {
let unlistenComplete: Promise<UnlistenFn> | null = null;
let unlistenFailed: Promise<UnlistenFn> | null = null;
(async () => {
void (async () => {
// Update immediately when Tauri signals a generation completion
unlistenComplete = listen("generation-complete-event", () => {
if (!cancelled) {
load();
void load();
}
});
// Also update immediately when a generation fails
unlistenFailed = listen("generation-failed-event", () => {
if (!cancelled) {
load();
void load();
// Refunded credits may take a moment to settle in the database
setTimeout(() => {
useCreditsState.getState().fetchFromServer();
@@ -906,10 +906,10 @@ export const TaskQueue = () => {
clearInterval(id);
window.removeEventListener("cover-image-uploaded", handleCoverUploaded);
if (unlistenComplete) {
unlistenComplete.then((f) => f());
void unlistenComplete.then((f) => f());
}
if (unlistenFailed) {
unlistenFailed.then((f) => f());
void unlistenFailed.then((f) => f());
}
};
}, [
@@ -125,7 +125,7 @@ export function storeEnqueueMeta(meta: EnqueueMeta) {
// Then convert blob URLs to base64 in the background for persistence
if (meta.refImageUrls && meta.refImageUrls.length > 0) {
Promise.all(meta.refImageUrls.map(toBase64Thumb)).then((dataUrls) => {
void Promise.all(meta.refImageUrls.map(toBase64Thumb)).then((dataUrls) => {
const current = readEntries();
const found = current.find((e) => e.id === entryId);
if (found) {
@@ -12,7 +12,7 @@ export const withProtectionRoute = <P extends object>(
function ProtectionRoute(rest: P) {
useSignals();
useSignalEffect(() => {
persistLogin();
void persistLogin();
});
// const { status, userInfo } = authentication;
@@ -38,7 +38,7 @@ export const useActiveJobs = () => {
// then set new one with new interval
clearInterval(curr.timer);
const newTimer = setInterval(() => {
PollRecentJobs();
void PollRecentJobs();
}, newTimeInterval);
return {
timer: newTimer,
@@ -72,7 +72,7 @@ export const useActiveJobs = () => {
useSignalEffect(() => {
// if active jobs is not initiated, do do a first pull;
if (!activeJobs.value) {
PollRecentJobs();
void PollRecentJobs();
return;
}
// if there are no active jobs, stop making polls
@@ -115,7 +115,7 @@ export const useActiveJobs = () => {
//else make and set timer
const newTimer = setInterval(() => {
PollRecentJobs();
void PollRecentJobs();
}, curr.interval);
return {
...curr,
@@ -24,7 +24,7 @@ export const useBackgroundLoadingMedia = () => {
//CASE 1: first load
// if myMovies undefined, poll for the first time
if (!userMovies.value) {
PollUserGeneratedMovies();
void PollUserGeneratedMovies();
return;
}
@@ -45,7 +45,7 @@ export const useBackgroundLoadingMedia = () => {
//there are videos newly completed, set and poll
lastCompletedWorkflow.current = completedWorkflowJobs.value;
PollUserGeneratedMovies().then((ret: boolean) => {
void PollUserGeneratedMovies().then((ret: boolean) => {
if (ret) {
addToast(
ToastTypes.SUCCESS,
@@ -60,7 +60,7 @@ export const useBackgroundLoadingMedia = () => {
//CASE 1: first load
// if audioItems undefined, poll for the first time
if (!userAudioItems.value) {
PollUserAudioItems();
void PollUserAudioItems();
return;
}
@@ -82,7 +82,7 @@ export const useBackgroundLoadingMedia = () => {
//there are audio jobs newly completed, set and poll
lastCompletedAudioJobs.current = completedAudioJobs.value;
PollUserAudioItems().then((ret: boolean) => {
void PollUserAudioItems().then((ret: boolean) => {
if (ret) {
addToast(
ToastTypes.SUCCESS,
+2 -2
View File
@@ -26,12 +26,12 @@ const ENV = {
const GlobalSettingsManager = ({ env }: { env: Record<string, string> }) => {
console.log("GlobalSettingsManager()");
SyncStorytellerApiConfig();
void SyncStorytellerApiConfig();
useSignals();
useSignalEffect(() => {
persistLogin();
void persistLogin();
});
/// Initizations that depends on ENV vars ///
@@ -152,7 +152,7 @@ export const MainApp = ({ sceneToken }: Props) => {
// Session probe (runs once per shell mount).
useEffect(() => {
const usersApi = new UsersApi();
usersApi.GetSession().then((result) => {
void usersApi.GetSession().then((result) => {
console.log(
`User Info | Username: ${result.data?.user?.username}, Token: ${result.data?.user?.user_token}`,
);
@@ -84,7 +84,7 @@ export const BaseImageSelector = ({
progressCallback: (newState) => {
if (newState.status === UploaderStates.success && newState.data) {
const mediaToken = newState.data || "";
(async () => {
void (async () => {
let finalUrl = reader.result as string;
let thumbnailUrlTemplate = undefined;
try {
@@ -148,7 +148,7 @@ export const RemoveBackground = () => {
}
};
processExternalUrl();
void processExternalUrl();
}, [
pendingExternalUrl,
setPendingExternalUrl,
@@ -87,7 +87,7 @@ export const completedAudioJobs = computed(() => {
export const shouldPollActiveJobs = signal<boolean>(false);
export const startPollingActiveJobs = () => {
PollRecentJobs(); //poll once regardless of polling intervals
void PollRecentJobs(); //poll once regardless of polling intervals
shouldPollActiveJobs.value = true;
};
@@ -47,7 +47,7 @@ export const login = async ({
// technically user is login with the system now, HOWEVER,
// in storyteller studio, only having a sesison is not enough,
// we need session info and active subscription info as well
getUserInfoAndSubcriptions();
void getUserInfoAndSubcriptions();
window.location.href = "/"; // TODO(bt,2025-04-19): Once we have in-page routing, get rid of this.
};
@@ -86,7 +86,7 @@ export const signUp = async ({
// technically user is login with the system now, HOWEVER,
// in storyteller studio, only having a sesison is not enough,
// we need session info and active subscription info as well
getUserInfoAndSubcriptions();
void getUserInfoAndSubcriptions();
return "";
};
@@ -95,12 +95,12 @@ export const persistLogin = async () => {
if (authentication.status.value !== AUTH_STATUS.INIT) {
return;
}
getUserInfoAndSubcriptions();
void getUserInfoAndSubcriptions();
};
// NB: Only for SyncStorytellerApiConfig.
export const forceGetUserInfoAndSubcriptions = async () => {
getUserInfoAndSubcriptions();
void getUserInfoAndSubcriptions();
};
async function getUserInfoAndSubcriptions() {
@@ -22,15 +22,15 @@ export const useCanvasBgRemovedEvent = (asyncCallback: (event: CanvasBgRemovedEv
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -21,7 +21,7 @@ export function useSoraLoginListener(
unlisten = stop;
};
setupListener();
void setupListener();
return () => {
if (unlisten) unlisten();
@@ -23,15 +23,15 @@ export const useCreditsBalanceChangedEvent = (
);
if (isUnmounted) {
unlisten.then((f) => f()); // Unsubscribe if unmounted early.
void unlisten.then((f) => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then((f) => f());
void unlisten.then((f) => f());
};
}, []);
};
@@ -27,15 +27,15 @@ export const useImageEditCompleteEvent = (asyncCallback: (event: ImageEditComple
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -24,15 +24,15 @@ export const useMediaFileDeletedEvent = (
);
if (isUnmounted) {
unlisten.then((f) => f()); // Unsubscribe if unmounted early.
void unlisten.then((f) => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then((f) => f());
void unlisten.then((f) => f());
};
}, []);
};
@@ -32,15 +32,15 @@ export const useObjectGenerationCompleteEvent = (
);
if (isUnmounted) {
unlisten.then((f) => f()); // Unsubscribe if unmounted early.
void unlisten.then((f) => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then((f) => f());
void unlisten.then((f) => f());
};
}, []);
};
@@ -19,15 +19,15 @@ export const useRefreshAccountStateEvent = (asyncCallback: (event: RefreshAccoun
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -20,15 +20,15 @@ export const useShowProviderBillingModalEvent = (asyncCallback: (event: ShowProv
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -20,15 +20,15 @@ export const useShowProviderLoginModalEvent = (asyncCallback: (event: ShowProvid
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -23,15 +23,15 @@ export const useSubscriptionPlanChangedEvent = (
);
if (isUnmounted) {
unlisten.then((f) => f()); // Unsubscribe if unmounted early.
void unlisten.then((f) => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then((f) => f());
void unlisten.then((f) => f());
};
}, []);
};
@@ -27,15 +27,15 @@ export const useTextToImageGenerationCompleteEvent = (asyncCallback: (event: Tex
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -27,15 +27,15 @@ export const useVideoGenerationCompleteEvent = (asyncCallback: (event: VideoGene
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -25,15 +25,15 @@ export const useGenerationCompleteEvent = () => {
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -26,15 +26,15 @@ export const useGenerationEnqueueFailureEvent = () => {
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -25,15 +25,15 @@ export const useGenerationEnqueueSuccessEvent = () => {
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -26,15 +26,15 @@ export const useGenerationFailedEvent = () => {
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -28,15 +28,15 @@ export const useFlashFileDownloadErrorEvent = (asyncCallback: (event: FlashFileD
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -19,15 +19,15 @@ export const useFlashUserInputErrorEvent = (asyncCallback: (event: FlashUserInpu
});
if (isUnmounted) {
unlisten.then(f => f()); // Unsubscribe if unmounted early.
void unlisten.then(f => f()); // Unsubscribe if unmounted early.
}
};
setup();
void setup();
return () => {
isUnmounted = true;
unlisten.then(f => f());
void unlisten.then(f => f());
};
}, []);
@@ -16,7 +16,7 @@ export function useTauriWindowControls() {
let unlistenUnmax: (() => void) | undefined;
let unlistenResize: (() => void) | undefined;
(async () => {
void (async () => {
try {
const win = await getCurrentWindowSafe();
setIsMaximized(await win.isMaximized());
+6
View File
@@ -1,2 +1,8 @@
sonar.projectKey=storytold_artcraft
sonar.organization=storytold
# CI only publishes the Rust lcov report, so TypeScript and JavaScript never
# appear covered. Same idea as anicraft excluding JS/PowerShell/Python, and
# as filmcraft and lightcraft excluding tools/docs/examples.
sonar.coverage.exclusions=**/*.ts,**/*.tsx,**/*.js,**/*.jsx