Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use raw.githubusercontent.com as the secondary URL for fetching the list of maps. Improve the loading indicator shown when the application is launching. #579

92 changes: 63 additions & 29 deletions src/app/map/leaflet-map/raster-tile-base-layer-configs-version-1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,47 +45,81 @@ export const rasterTileBaseLayerConfigsVersion1Fallback: RasterTileBaseLayerConf

// In the production environment, the content of main branch in photo-location-map-resources repo is used.
// In the development environment, the content of the feature branch can be used as needed.
const configsFileUrl
= 'https://cdn.jsdelivr.net/gh/TomoyukiAota/photo-location-map-resources@main/map-configs/raster-tile-base-layer-configs-version-1.jsonc';
const configsFileFetchArguments: Array<{ url: string, options: RequestInit }> = [
{
url: 'https://cdn.jsdelivr.net/gh/TomoyukiAota/photo-location-map-resources@main/map-configs/raster-tile-base-layer-configs-version-1.jsonc',
options: {
cache: 'no-store',
signal: AbortSignal.timeout(10000 /* milliseconds */),
},
},
{
url: 'https://raw.githubusercontent.com/TomoyukiAota/photo-location-map-resources/refs/heads/main/map-configs/raster-tile-base-layer-configs-version-1.jsonc',
options: {
cache: 'no-store',
// No timeout for the last attempt.
},
},
];

async function fetchRasterTileBaseLayerConfigsVersion1(): Promise<RasterTileBaseLayerConfigsVersion1> {
const response = await fetch(configsFileUrl);
async function fetchRasterTileBaseLayerConfigsVersion1(url: string, options: RequestInit): Promise<{configs: RasterTileBaseLayerConfigsVersion1, responseText: string}> {
const response = await fetch(url, options);
if (!response.ok) {
throw new Error(`response.status: ${response.status}, response.statusText: ${response.statusText}`);
}
const jsonc = await response.text();
const json = parseJsonc(jsonc);
return json as RasterTileBaseLayerConfigsVersion1;
const responseText = await response.text();
const configs = parseJsonc(responseText) as RasterTileBaseLayerConfigsVersion1;
return {configs, responseText};
}

function recordErrorAndGetFallback(message: string): RasterTileBaseLayerConfigsVersion1 {
function recordFetchingConfigs(url: string): void {
const message = `Fetching ${url}`;
logger.info(message);
Analytics.trackEvent('Leaflet Map', `[Leaflet Map] Fetching BaseLayerConfigs`, message);
}

function recordInvalidConfigsObjectError(url: string, configs: any, responseText: string): void {
const message = `Invalid configs object is fetched from ${url}.\n----------\nconfigs:\n${toLoggableString(configs)}\n----------\nresponseText:\n${responseText}`;
logger.error(message);
Analytics.trackEvent('Leaflet Map', `[Leaflet Map] Fallback BaseLayerConfigs`, message);
return rasterTileBaseLayerConfigsVersion1Fallback;
Analytics.trackEvent('Leaflet Map', `[Leaflet Map] Invalid BaseLayerConfigs`, message);
}

async function fetchRasterTileBaseLayerConfigsVersion1WithFallback(): Promise<RasterTileBaseLayerConfigsVersion1> {
const fetchingMessage = `Fetching ${configsFileUrl}`;
logger.info(fetchingMessage);
Analytics.trackEvent('Leaflet Map', `[Leaflet Map] Fetching BaseLayerConfigs`, fetchingMessage);
function recordFetchSuccess(url: string, configs: RasterTileBaseLayerConfigsVersion1, responseText: string): void {
const message = `Fetched ${url}\n----------\nconfigs:\n${toLoggableString(configs)}\n----------\nresponseText:\n${responseText}`;
logger.info(message);
Analytics.trackEvent('Leaflet Map', `[Leaflet Map] Fetched BaseLayerConfigs`, message);
}

let configs: RasterTileBaseLayerConfigsVersion1;
try {
configs = await fetchRasterTileBaseLayerConfigsVersion1();
} catch (error) {
const message = `Failed to fetch ${configsFileUrl}. Using the fallback configs. ${error.message}`;
return recordErrorAndGetFallback(message);
}
function recordFetchFailed(url: string, error: Error): void {
const message = `Failed to fetch ${url}. error.message: "${error.message}"`;
logger.error(message);
Analytics.trackEvent('Leaflet Map', `[Leaflet Map] Failed to fetch configs`, message);
}

function recordFetchFailedForAllUrls(): void {
const message = `Failed to fetch the configs from all the possible URLs. Using the fallback configs.`;
logger.error(message);
Analytics.trackEvent('Leaflet Map', `[Leaflet Map] Fallback BaseLayerConfigs`, message);
}

if (!configs?.rasterTileBaseLayerConfigs?.length) {
const message = `Invalid configs object is fetched from ${configsFileUrl}. Using the fallback configs.\n${toLoggableString(configs)}`;
return recordErrorAndGetFallback(message);
async function fetchRasterTileBaseLayerConfigsVersion1WithRetry(): Promise<RasterTileBaseLayerConfigsVersion1> {
for (const {url, options} of configsFileFetchArguments) {
try {
recordFetchingConfigs(url);
const {configs, responseText} = await fetchRasterTileBaseLayerConfigsVersion1(url, options);
if (!configs?.rasterTileBaseLayerConfigs?.length) {
recordInvalidConfigsObjectError(url, configs, responseText);
continue;
}
recordFetchSuccess(url, configs, responseText);
return configs;
} catch (error) {
recordFetchFailed(url, error);
}
}

const fetchedMessage = `Fetched ${configsFileUrl}\n${toLoggableString(configs)}`;
logger.info(fetchedMessage);
Analytics.trackEvent('Leaflet Map', `[Leaflet Map] Fetched BaseLayerConfigs`, fetchedMessage);
return configs;
recordFetchFailedForAllUrls();
return rasterTileBaseLayerConfigsVersion1Fallback;
}

export const rasterTileBaseLayerConfigsVersion1 = await fetchRasterTileBaseLayerConfigsVersion1WithFallback();
export const rasterTileBaseLayerConfigsVersion1 = await fetchRasterTileBaseLayerConfigsVersion1WithRetry();
7 changes: 6 additions & 1 deletion src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@

</head>
<body>
<app-root>Loading...</app-root>
<app-root>
<div class="app-loading-indicator-container">
<span class="app-loading-indicator"></span>
<span>Loading...</span>
</div>
</app-root>
</body>
</html>
40 changes: 40 additions & 0 deletions src/styles/_app-loading-indicator.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Reference: https://cssloaders.github.io/

$loading-indicator-color: #888888;

.app-loading-indicator-container {
height: 100%;
display: grid;
gap: 10px;
place-content: center;
place-items: center;
color: $loading-indicator-color;

opacity: 0;
animation: fadeInAppLoadingIndicator 2s ease 0.5s forwards;
}

@keyframes fadeInAppLoadingIndicator {
from { opacity: 0; }
to { opacity: 1; }
}

.app-loading-indicator {
width: 48px;
height: 48px;
border: 5px solid $loading-indicator-color;
border-bottom-color: transparent;
border-radius: 50%;
display: inline-block;
box-sizing: border-box;
animation: rotateAppLoadingIndicator 1s linear infinite;
}

@keyframes rotateAppLoadingIndicator {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
1 change: 1 addition & 0 deletions src/styles/global-styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ $max-z-index: 2147483647;
display: none;
}

@import "app-loading-indicator";
@import "photo-cluster-viewer/photo-cluster-viewer";
@import "photo-info-viewer/photo-info-viewer";
@import "plm-leaflet-map/plm-leaflet-map";
Loading