Browse Source

refactor(types): improve type safety and eliminate type assertions

- Replace type assertions with proper type guards in ProfileService
- Add isAxiosError type guard and improve error handling
- Clean up formatting and improve type safety in deepLinks service
- Remove type assertions in AccountViewView Vue component
- Improve code formatting and consistency across services
fix-deep-link
Matthew Raymer 3 days ago
parent
commit
9384f0083a
  1. 43
      src/services/ProfileService.ts
  2. 8
      src/services/deepLinks.ts
  3. 30
      src/views/AccountViewView.vue

43
src/services/ProfileService.ts

@ -140,18 +140,20 @@ export class ProfileService {
logger.error("Unexpected response status when deleting profile:", {
status: response.status,
statusText: response.statusText,
data: response.data
data: response.data,
});
throw new Error(`Profile not deleted - HTTP ${response.status}: ${response.statusText}`);
throw new Error(
`Profile not deleted - HTTP ${response.status}: ${response.statusText}`,
);
}
} catch (error) {
if (this.isApiError(error) && error.response) {
const response = error.response as any; // Type assertion for error response
const response = error.response;
logger.error("API error deleting profile:", {
status: response.status,
statusText: response.statusText,
data: response.data,
url: (error as any).config?.url
url: this.getErrorUrl(error),
});
// Handle specific HTTP status codes
@ -163,7 +165,11 @@ export class ProfileService {
return true; // Consider this a success if profile doesn't exist
} else if (response.status === 400) {
logger.error("Bad request when deleting profile:", response.data);
throw new Error(`Profile deletion failed: ${response.data?.message || 'Bad request'}`);
const errorMessage =
typeof response.data === "string"
? response.data
: response.data?.message || "Bad request";
throw new Error(`Profile deletion failed: ${errorMessage}`);
} else if (response.status === 401) {
logger.error("Unauthorized to delete profile");
throw new Error("You are not authorized to delete this profile");
@ -244,11 +250,32 @@ export class ProfileService {
/**
* Type guard for API errors
*/
private isApiError(
error: unknown,
): error is { response?: { status?: number } } {
private isApiError(error: unknown): error is {
response?: {
status?: number;
statusText?: string;
data?: { message?: string } | string;
};
} {
return typeof error === "object" && error !== null && "response" in error;
}
/**
* Extract URL from AxiosError without type casting
*/
private getErrorUrl(error: unknown): string | undefined {
if (this.isAxiosError(error)) {
return error.config?.url;
}
return undefined;
}
/**
* Type guard for AxiosError
*/
private isAxiosError(error: unknown): error is AxiosError {
return error instanceof AxiosError;
}
}
/**

8
src/services/deepLinks.ts

@ -199,8 +199,10 @@ export class DeepLinkHandler {
}
// Continue with parameter validation as before...
const pathSchema = deepLinkPathSchemas[path as keyof typeof deepLinkPathSchemas];
const querySchema = deepLinkQuerySchemas[path as keyof typeof deepLinkQuerySchemas];
const pathSchema =
deepLinkPathSchemas[path as keyof typeof deepLinkPathSchemas];
const querySchema =
deepLinkQuerySchemas[path as keyof typeof deepLinkQuerySchemas];
let validatedPathParams: Record<string, string> = {};
let validatedQueryParams: Record<string, string> = {};
@ -235,7 +237,7 @@ export class DeepLinkHandler {
await this.router.replace({
name: routeName,
params: validatedPathParams,
query: validatedQueryParams
query: validatedQueryParams,
});
} catch (error) {
logger.error(

30
src/views/AccountViewView.vue

@ -182,7 +182,9 @@
@change="onLocationCheckboxChange"
/>
<label for="includeUserProfileLocation">Include Location</label>
<span class="text-xs text-slate-400 ml-2">(Debug: {{ isMapReady ? 'Map Ready' : 'Map Loading' }})</span>
<span class="text-xs text-slate-400 ml-2"
>(Debug: {{ isMapReady ? "Map Ready" : "Map Loading" }})</span
>
</div>
<div v-if="includeUserProfileLocation" class="mb-4 aspect-video">
<p class="text-sm mb-2 text-slate-500">
@ -922,11 +924,14 @@ export default class AccountViewView extends Vue {
// Fix Leaflet icon issues in modern bundlers
// This prevents the "Cannot read properties of undefined (reading 'Default')" error
if (L.Icon.Default) {
delete (L.Icon.Default.prototype as any)._getIconUrl;
delete (L.Icon.Default.prototype as { _getIconUrl?: unknown })
._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon-2x.png',
iconUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png',
shadowUrl: 'https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png',
iconRetinaUrl:
"https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon-2x.png",
iconUrl: "https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png",
shadowUrl:
"https://unpkg.com/leaflet@1.7.1/dist/images/marker-shadow.png",
});
}
}
@ -1543,12 +1548,18 @@ export default class AccountViewView extends Vue {
try {
logger.debug("Map ready event fired, map object:", map);
// doing this here instead of on the l-map element avoids a recentering after a drag then zoom at startup
const zoom = this.userProfileLatitude && this.userProfileLongitude ? 12 : 2;
const zoom =
this.userProfileLatitude && this.userProfileLongitude ? 12 : 2;
const lat = this.userProfileLatitude || 0;
const lng = this.userProfileLongitude || 0;
map.setView([lat, lng], zoom);
this.isMapReady = true;
logger.debug("Map ready state set to true, coordinates:", [lat, lng], "zoom:", zoom);
logger.debug(
"Map ready state set to true, coordinates:",
[lat, lng],
"zoom:",
zoom,
);
} catch (error) {
logger.error("Error in onMapReady:", error);
this.isMapReady = true; // Set to true even on error to prevent infinite loading
@ -1710,7 +1721,10 @@ export default class AccountViewView extends Vue {
onLocationCheckboxChange(): void {
try {
logger.debug("Location checkbox changed, new value:", this.includeUserProfileLocation);
logger.debug(
"Location checkbox changed, new value:",
this.includeUserProfileLocation,
);
if (!this.includeUserProfileLocation) {
// Location checkbox was unchecked, clean up map state
this.isMapReady = false;

Loading…
Cancel
Save