ThreadCount Community edition
Uniform stock management for healthcare linen rooms: the coordinator app, the phone counter and the staff app, for your own server. Built from e2d6d42 on 2026-09-13. Licensed under the Functional Source License (FSL-1.1-ALv2).
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import type { CapacitorConfig } from "@capacitor/cli";
|
||||
|
||||
/* Two Android apps, one config.
|
||||
*
|
||||
* npx cap sync android → the counter app (tech.threadcount.app → /m)
|
||||
* TC_APP=staff npx cap sync android → the staff app (tech.threadcount.staff → /my)
|
||||
*
|
||||
* The Capacitor 6 CLI has no --config flag and always reads this file from the working directory,
|
||||
* so the alternative to branching here is swapping files around before every build — which is the
|
||||
* kind of step that eventually ships the wrong app id to Play. One file, one env var, two
|
||||
* `android.path`s, and neither project can overwrite the other.
|
||||
*
|
||||
* Both are the live site in a native shell, so the apps update when the site does and there is one
|
||||
* copy of every rule rather than two. Each bundles only its own first-run screens, which are the
|
||||
* only ones that must work before someone has signed in or has signal.
|
||||
*/
|
||||
const staff = process.env.TC_APP === "staff";
|
||||
|
||||
const counter: CapacitorConfig = {
|
||||
appId: "tech.threadcount.app",
|
||||
appName: "ThreadCount",
|
||||
// Splash, welcome (with the video) and the offline screen.
|
||||
webDir: "androidshell",
|
||||
server: {
|
||||
androidScheme: "https",
|
||||
cleartext: false,
|
||||
// The shell opens on its own bundled splash and welcome, then navigates to the site — the
|
||||
// hosted one, or the self-hosted server the welcome screen saved (Community edition rooms run
|
||||
// their own). Any host, because the saved one cannot be known at build time; the welcome
|
||||
// screen is the only page that chooses it, and MainActivity honours the choice only from there.
|
||||
allowNavigation: ["*"],
|
||||
// A page that fails to load falls back to the bundled offline screen, not a browser error.
|
||||
errorPath: "error.html",
|
||||
},
|
||||
android: {
|
||||
// A stock count is read at arm's length under ward lighting; let the OS text size apply.
|
||||
allowMixedContent: false,
|
||||
// captureInput is deliberately absent. It swaps the WebView's input connection for a bare
|
||||
// BaseInputConnection that never fills in the EditorInfo, so the keyboard sees no inputType on
|
||||
// any field: the numeric two-factor box gets a QWERTY keyboard, one-time-code autofill is
|
||||
// never offered, and a password manager loses the hints the assetlinks file goes out of its
|
||||
// way to enable. It only buys anything for a hardware keyboard-wedge scanner, and neither app
|
||||
// has one — every barcode here is read through the camera.
|
||||
webContentsDebuggingEnabled: false,
|
||||
},
|
||||
plugins: {
|
||||
SplashScreen: {
|
||||
// Just long enough to cover the WebView starting. The bundled splash draws the mark from
|
||||
// here, so this screen is plain ink — showing the mark twice, once still and once drawing,
|
||||
// reads as a stutter.
|
||||
launchShowDuration: 350,
|
||||
backgroundColor: "#201e1dff",
|
||||
androidSplashResourceName: "splash",
|
||||
androidScaleType: "CENTER_CROP",
|
||||
showSpinner: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* The staff app.
|
||||
*
|
||||
* A separate app rather than a role switch inside the counter app, because the audiences share
|
||||
* nothing. The counter app goes on a handful of ward phones belonging to the people who run the
|
||||
* linen room; this one goes on every nurse's phone. One Play listing cannot describe both, and a
|
||||
* wearer opening an app whose home screen is a stocktake counter would reasonably conclude they
|
||||
* had installed the wrong thing.
|
||||
*
|
||||
* It carries no barcode scanner and asks for no camera permission: nothing a wearer does involves
|
||||
* scanning, and an app that every clinical staff member installs should ask for as little as it
|
||||
* possibly can.
|
||||
*/
|
||||
const staffApp: CapacitorConfig = {
|
||||
...counter,
|
||||
appId: "tech.threadcount.staff",
|
||||
appName: "ThreadCount Staff",
|
||||
webDir: "staffshell",
|
||||
android: {
|
||||
...counter.android,
|
||||
// Its own native project, beside the counter app's rather than replacing it.
|
||||
path: "android-staff",
|
||||
},
|
||||
};
|
||||
|
||||
export default staff ? staffApp : counter;
|
||||
Reference in New Issue
Block a user