Compare commits

...

7 Commits

8 changed files with 133 additions and 50 deletions

View File

@ -64,7 +64,7 @@
top: 5px;
right: 0;
display: flex;
gap: 0px
gap: 0px;
}
.content {

View File

@ -15,8 +15,8 @@
<Card config={upstreamConfig}>
<form action={config.formAction}>
<input type="text" name={config.fieldName} placeholder={config.placeholder} maxlength={config.maxLen} autocomplete="off">
<br>
<input type="text" name={config.fieldName} placeholder={config.placeholder} maxlength={config.maxLen} autocomplete="off" />
<br />
<button type="submit">Submit</button>
</form>
</Card>
@ -50,11 +50,10 @@
input:hover {
box-shadow: var(--box-shadow-dark);
}
button:hover {
background-color: rgb(45, 45, 45);
box-shadow: var(--box-shadow-dark);
}
</style>
</style>

View File

@ -6,50 +6,80 @@
import type { NearestStationResponse } from "@owlboard/ts-types";
import { uuid } from "$lib/stores/uuid";
import { location } from "$lib/stores/location";
import InLineLoading from "$lib/navigation/InLineLoading.svelte";
import { apiGet } from "$lib/scripts/apiFetch";
import { onMount } from "svelte";
let stations: NearestStationResponse[];
let errorMessage: string;
let stations: NearestStationResponse[] = [];
let config: CardConfig = {
title: "Near to Me",
showHelp: true,
showRefresh: true,
helpText: "Your location may not be accurate, particularly on desktop and laptop devices.",
onRefresh: refresh
}
helpText: "Your location may not be accurate on desktop and laptop devices.",
onRefresh: refresh,
};
function turnOnLocation() {
location.set(true)
location.set(true);
getCurrentLocation();
toast.success("Done\nTo disable location, go to settings")
toast.success("Done\nTo disable location, go to settings");
}
function refresh() {getNearestStations()}
function refresh() {
stations = [];
getNearestStations();
}
async function getNearestStations() {
// Get location, then fetch nearest stations and push to `stations` variable
const currentLocation = await getCurrentLocation();
const apiPath: string = `/api/v2/live/station/nearest/${currentLocation.latitude}/${currentLocation.longitude}`;
try {
const apiResponse = (await apiGet(apiPath)) as NearestStationResponse[];
stations = apiResponse;
} catch (err) {
errorMessage = err as string;
}
}
onMount(() => {
if ($location) {
if ($uuid && $uuid != "null") {
getNearestStations();
} else {
errorMessage = "Register to use this feature";
}
}
});
</script>
<Card {config}>
{#if !$uuid || $uuid === "null"}
<p><a href="/more/reg">Register to use this feature</a></p>
{:else}
{#if $location}
{#if !stations}
<p>Fetching locations...</p>
{:else if $location}
{#if !stations.length}
{#if errorMessage}
<p>{errorMessage}</p>
{:else}
{#each stations as station}
<a href="/ldb?station={station["3ALPHA"]}">{station.NLCDESC} - {station.miles}mi</a>
{/each}
<InLineLoading />
{/if}
{:else}
<p><button on:click={turnOnLocation}>Turn on Location</button></p>
<div id="buttons">
{#each stations as station}
<a class="link" href="/ldb?station={station['3ALPHA']}">{station.NLCDESC} - {station.miles}mi</a>
{/each}
</div>
{/if}
{:else}
<p><button on:click={turnOnLocation}>Turn on Location</button></p>
{/if}
</Card>
<style>
a, button {
a,
button {
color: aliceblue;
text-decoration: none;
margin: auto;
@ -61,8 +91,30 @@
transition: all 0.3s ease;
}
a:hover, button:hover {
a:hover,
button:hover {
background-color: rgb(45, 45, 45);
box-shadow: var(--box-shadow-dark);
}
</style>
#buttons {
display: flex;
flex-wrap: wrap;
justify-content: center;
align-items: center;
width: 95%;
margin: auto;
padding-top: 5px;
}
.link {
display: inline-flex;
margin: 5px;
border: none;
border-radius: 20px;
padding: 5px 10px;
font-family: urwgothic, "Lucida Sans", "Lucida Sans Regular", "Lucida Grande", "Lucida Sans Unicode", Geneva, Verdana, sans-serif;
font-size: 16px;
font-weight: 400;
white-space: nowrap;
}
</style>

View File

@ -0,0 +1,28 @@
<script lang="ts">
export let size: string = "1em";
export let color: string = "aliceblue";
</script>
<div class="spinner" style="--spinner-size: {size}; --spinner-color: {color};" />
<style>
.spinner {
display: inline-block;
width: var(--spinner-size);
height: var(--spinner-size);
border: 2px solid transparent;
border-top-color: var(--spinner-color);
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 10px;
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
</style>

View File

@ -1,32 +1,32 @@
<script>
import { getApiUrl } from "$lib/scripts/upstream";
import { uuid } from "$lib/stores/uuid";
<script lang="ts">
import { apiGet } from "$lib/scripts/apiFetch";
import { ReasonCode } from "@owlboard/ts-types";
export let code = "";
export let type = "";
export let code: string;
export let type: string;
async function getDelay(code = "") {
console.log(`Fetching delay reason ${code}`);
async function getDelay(code: string): Promise<string | undefined> {
const data = await getReason(code);
return data[0].lateReason || "This train has been delayed";
if (data) {
return data[0].lateReason || "This train has been delayed";
}
}
async function getCancel(code = "") {
console.log(`Fetching cancel reason ${code}`);
async function getCancel(code: string): Promise<string | undefined> {
const data = await getReason(code);
return data[0].cancReason || "This train has been cancelled";
if (data) {
return data[0].cancReason || "This train has been cancelled";
}
}
async function getReason(code = "") {
const url = `${getApiUrl()}/api/v2/ref/reasonCode/${code}`;
const options = {
method: "GET",
headers: {
uuid: $uuid,
},
};
const res = await fetch(url, options);
return await res.json();
async function getReason(code: string): Promise<ReasonCode[] | undefined> {
const apiString = `/api/v2/ref/reasonCode/${code}`;
try {
const apiRes = (await apiGet(apiString)) as ReasonCode[];
return apiRes;
} catch (err) {
console.error("Unable to define reason code");
}
}
</script>

View File

@ -34,8 +34,12 @@ export async function apiGet(path: string): Promise<any> {
try {
const res = await fetch(getUrlString() + path, options);
if (res.status === 401) {
throw new Error("Registration not accepted. Register at `Menu > Registration`");
}
if (!res.ok) {
throw new Error("Network response not ok");
throw new Error(`Failed: ${res.status}: ${res.statusText}`);
}
const contentType = res.headers.get("content-type");
if (!contentType || !contentType.includes("application/json")) {

View File

@ -1,3 +1,3 @@
export const version: string = "2024.07.1";
export const version: string = "2024.07.2";
export const versionTag: string = "";
export const showWelcome: boolean = false;

View File

@ -18,7 +18,7 @@
placeholder: "Enter CRS/TIPLOC/STANOX",
maxLen: 7,
formAction: "/ldb/",
fieldName: "station"
fieldName: "station",
};
let TimetableConfig: LookupCardConfig = {
@ -27,8 +27,8 @@
placeholder: "Enter headcode",
maxLen: 4,
formAction: "/train/",
fieldName: "headcode"
}
fieldName: "headcode",
};
function onRefresh() {
console.log("Refresh");
@ -37,4 +37,4 @@
<LookupCard config={LookupConfig} />
<LookupCard config={TimetableConfig} />
<NearToMeCard />
<NearToMeCard />