Detect failed logins; fix map camera recenter, marker contrast, dark map tiles
- Client: server returns 200 + Status:"Error" on bad credentials; loginWith* now throw LoginError with the server message instead of faking a signed-in state - Map: uncontrolled camera positioned once on load + explicit actions only, so Search/marker-tap/re-renders no longer snap back to the user's location - Markers: always-dark high-contrast bubble + zone-colored dot (some zones report a white BackgroundColor -> was white-on-white) - Dark mode now switches map tiles to CARTO dark-matter Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
2f9f0f604a
commit
fb3d1cdf3a
4 changed files with 96 additions and 26 deletions
|
|
@ -51,6 +51,14 @@ export interface ParkSmarterClientOptions {
|
|||
logSink?: (line: string) => void;
|
||||
}
|
||||
|
||||
/** Thrown when credentials are rejected (the server signals this via a 200 + Status:"Error"). */
|
||||
export class LoginError extends Error {
|
||||
constructor(message: string) {
|
||||
super(message);
|
||||
this.name = 'LoginError';
|
||||
}
|
||||
}
|
||||
|
||||
function resolveEnvironment(
|
||||
env: EnvironmentName | Environment | undefined,
|
||||
): Environment {
|
||||
|
|
@ -115,6 +123,21 @@ export class ParkSmarterClient {
|
|||
/* Auth */
|
||||
/* ============================================================== */
|
||||
|
||||
/**
|
||||
* The server returns HTTP 200 even for a FAILED login, signalling the failure
|
||||
* only via `Status: "Error"` + a null `Auth_Token`. Detect that here so callers
|
||||
* get a real error instead of a phantom "signed-in" state.
|
||||
*/
|
||||
private finishLogin(data: T.AuthResponse): Promise<T.AuthResponse> {
|
||||
const token = typeof data?.Auth_Token === 'string' ? data.Auth_Token : '';
|
||||
if (!token || data?.Status === 'Error') {
|
||||
throw new LoginError(data?.Message || 'Invalid login or password.');
|
||||
}
|
||||
return this.tokens.setAuthToken
|
||||
? Promise.resolve(this.tokens.setAuthToken(token)).then(() => data)
|
||||
: Promise.resolve(data);
|
||||
}
|
||||
|
||||
/** POST /api/Auth — phone + password login. Persists Auth_Token & SessionId. */
|
||||
async loginWithPhone(params: T.LoginWithPhoneParams): Promise<T.AuthResponse> {
|
||||
const res = await this.http.request<T.AuthResponse>({
|
||||
|
|
@ -123,8 +146,7 @@ export class ParkSmarterClient {
|
|||
body: { UserName: params.phoneNumber, Password: params.password },
|
||||
includeAuthToken: false,
|
||||
});
|
||||
if (res.data?.Auth_Token) await this.tokens.setAuthToken(res.data.Auth_Token);
|
||||
return res.data;
|
||||
return this.finishLogin(res.data);
|
||||
}
|
||||
|
||||
/** POST /api/Auth — Sign in with Apple. Persists Auth_Token & SessionId. */
|
||||
|
|
@ -140,8 +162,7 @@ export class ParkSmarterClient {
|
|||
},
|
||||
includeAuthToken: false,
|
||||
});
|
||||
if (res.data?.Auth_Token) await this.tokens.setAuthToken(res.data.Auth_Token);
|
||||
return res.data;
|
||||
return this.finishLogin(res.data);
|
||||
}
|
||||
|
||||
/** Re-authenticate using a cached Auth_Token (sets it, then callers can bootstrap). */
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export { ParkSmarterClient } from './client.js';
|
||||
export { ParkSmarterClient, LoginError } from './client.js';
|
||||
export type { ParkSmarterClientOptions } from './client.js';
|
||||
export {
|
||||
ENVIRONMENTS,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue