Skip to content

Commit

Permalink
Fixes codec to work with Note and Integrate codec to Geopoint Vue Com…
Browse files Browse the repository at this point in the history
…ponent
  • Loading branch information
latin-panda committed Feb 14, 2025
1 parent 79a5432 commit c2dd023
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 47 deletions.
28 changes: 9 additions & 19 deletions packages/web-forms/src/components/controls/Input/InputGeopoint.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { GeopointInputNode } from '@getodk/xforms-engine';
import type { GeopointInputNode, InputValue } from '@getodk/xforms-engine';
import Button from 'primevue/button';
import PrimeProgressSpinner from 'primevue/progressspinner';
import { inject, computed, ref } from 'vue';
Expand All @@ -11,20 +11,8 @@ interface InputGeopointProps {
const props = defineProps<InputGeopointProps>();
const coords = ref<GeolocationCoordinates | null>(null);
interface Coordinates {
latitude: string;
longitude: string;
altitude: string;
accuracy: string;
}
const value = computed((): Coordinates | null => {
if (!props.node.currentState.value?.length) {
return null;
}
const [latitude, longitude, altitude, accuracy] = props.node.currentState.value.trim().split(' ');
return { latitude, longitude, altitude, accuracy };
const value = computed((): InputValue<'geopoint'> => {
return props.node.currentState.value;
});
/**
Expand Down Expand Up @@ -152,10 +140,12 @@ const save = () => {
return;
}
const { latitude, longitude, altitude, accuracy } = coords.value;
// ToDo: if one is missing, it still need to know the position of the others.
// ToDo: Change the value type to object? or default to negative / zero number?
props.node.setValue([latitude ?? 0, longitude ?? 0, altitude ?? 0, accuracy ?? 0].join(' '));
props.node.setValue({
latitude: coords.value.latitude,
longitude: coords.value.longitude,
altitude: coords.value.altitude,
accuracy: coords.value.accuracy,
});
};
const formatNumber = (num: number) => {
Expand Down
58 changes: 31 additions & 27 deletions packages/xforms-engine/src/lib/codecs/GeopointValueCodec.ts
Original file line number Diff line number Diff line change
@@ -1,61 +1,65 @@
import { type CodecDecoder, type CodecEncoder, ValueCodec } from './ValueCodec.ts';

export interface GeopointRuntimeValue {
export interface GeopointValue {
readonly latitude: number;
readonly longitude: number;
readonly altitude: number;
readonly accuracy: number;
readonly altitude: number | null;
readonly accuracy: number | null;
}

export type GeopointRuntimeValue = GeopointValue | null;
export type GeopointInputValue = GeopointRuntimeValue;

const DEGREES_MAX = {
latitude: 90,
longitude: 180,
} as const;

const isValidDegrees = (coordinate: keyof typeof DEGREES_MAX, degrees: number | undefined): boolean => {
if (degrees == null) {
return false;
}

return Math.abs(degrees) <= DEGREES_MAX[coordinate];
const isValidDegrees = (
coordinate: keyof typeof DEGREES_MAX,
degrees: number | undefined
): degrees is number => {
return (
typeof degrees === 'number' && !isNaN(degrees) && Math.abs(degrees) <= DEGREES_MAX[coordinate]
);
};

export class GeopointValueCodec extends ValueCodec<'geopoint', GeopointRuntimeValue, GeopointInputValue> {
export class GeopointValueCodec extends ValueCodec<
'geopoint',
GeopointRuntimeValue,
GeopointInputValue
> {
constructor() {
const encodeValue: CodecEncoder<GeopointInputValue> = (value): string => {
if (!isValidDegrees('latitude', value.latitude) || !isValidDegrees('longitude', value.longitude)) {
throw new Error('Not valid coordinates');
const encodeValue: CodecEncoder<GeopointInputValue> = (value) => {
if (
value == null ||
!isValidDegrees('latitude', value.latitude) ||
!isValidDegrees('longitude', value.longitude)
) {
return '';
}

return [value.latitude, value.longitude, value.altitude, value.accuracy].join(' ');
return [value.latitude, value.longitude, value.altitude ?? 0, value.accuracy ?? 0].join(' ');
};

const decodeValue: CodecDecoder<GeopointRuntimeValue | null> = (value: string) => {
const coordinates = value.split(/\s+/).map(item => Number(item));
const isGeopointRuntimeValue = (
const decodeValue: CodecDecoder<GeopointRuntimeValue> = (value: string) => {
const coordinates = value.split(/\s+/).map((item) => Number(item));

const isGeopointRuntimeValue =
coordinates.length >= 2 &&
coordinates.length <= 4 &&
coordinates.every((item) => item != null)
);

coordinates.every((item) => item != null);
if (!isGeopointRuntimeValue) {
return null;
}

const [latitude, longitude, altitude, accuracy] = coordinates;
const [latitude, longitude, altitude = 0, accuracy = 0] = coordinates;

if (!isValidDegrees('latitude', latitude) || !isValidDegrees('longitude', longitude)) {
return null;
}

return {
latitude,
longitude,
altitude: altitude ?? 0,
accuracy: accuracy ?? 0,
};
return { latitude, longitude, altitude, accuracy };
};

super('geopoint', encodeValue, decodeValue);
Expand Down
6 changes: 5 additions & 1 deletion packages/xforms-engine/src/lib/codecs/NoteCodec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,11 @@ export class NoteCodec<V extends ValueType> extends ValueCodec<
> {
constructor(baseCodec: SharedValueCodec<V>) {
const encodeValue = (value: NoteInputValue<V>): string => {
return baseCodec.encodeValue(value ?? '');
if (value == null) {
return '';
}

return baseCodec.encodeValue(value);
};

const decodeValue = (value: string): NoteRuntimeValue<V> => {
Expand Down

0 comments on commit c2dd023

Please sign in to comment.