Repository navigation
Conversation
… question types - Answer time and date-time questions; date questions pick and keep a calendar day without a time zone shift. - Display logic compares number and rating answers as numbers. - A question type the app doesn't know shows a notice and never blocks the form. - Translations for the new placeholders and notice.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
The legacy splash config shrank the whole full-screen image into the Android 12+ splash icon, leaving the logo about 40dp wide. Use the expo-splash-screen plugin with a logo-only image: 190dp on Android (the widest that stays inside the splash icon's visible circle) and 280pt on iOS.
… preview Deleting pushed the form's submissions screen on top of the delete preview, so back went to the preview of the deleted submission. Go back to the submissions screen it was opened from instead.
…e delete button Long forms pushed the answers and the Delete button below the screen with no way to scroll, so observers couldn't confirm the deletion.
Saving updated the polling station information card on the home screen while Android was still re-attaching it during the back transition, which crashed with "addViewAt: failed to insert view". The home screen now shows changes to that card only once its transition has ended.
They are expected on observers' phones and buried the real errors. One check, isExpectedApiError, drops them in the API error reporter and in Sentry's beforeSend, and the polling station and citizen form loaders no longer send their own messages for them. The [API FAILED] console log, which Sentry kept as a breadcrumb with the full stack, now prints only in development.
Add a "Time and date settings" page, reached from the More screen for observers and citizens, where the user picks a 24 or 12-hour clock and a date format (YYYY-MM-DD, DD.MM.YYYY, DD/MM/YYYY or MM/DD/YYYY). The choice is stored on the device and used by every date and time shown in the app and by the time pickers. Translations added for all app languages.
A picker quality below 1 makes expo-image-picker on Android decode the whole photo into a bitmap before re-encoding it, which runs out of memory on 50-200MP cameras. Keep quality at 1 and let react-native-compressor do the downsampled resize.
Each 10MB part was read as a base64 string, turned into a Buffer and re-encoded by the network layer, holding ~40MB of JS memory per part. Upload parts natively with FileSystem.uploadAsync: single-part files go straight from disk, larger files copy each byte range to a temp file first.
…t stick on Android On Android, tamagui's Button kept its internal pressed state after the press ended (e.g. a submit that only shows validation errors), so the button stayed drawn faded with a grey border, or grey like a disabled one, until the screen was left. Build the Button on a plain XStack and drive the pressed and disabled looks from our own state and props; the pressed look is also cleared in onPress, as onPressOut is not always delivered.
…drawer Only started election rounds are listed, so the "Started - " prefix said nothing. Show the English title under the title when they differ.
Incident reporting forms no longer show in the observation forms list. "Report new issue" on the report tab now offers the quick report first, then each incident reporting form (asking for the form language like the observation forms). An incident report asks for the location like a quick report, then walks through the form questions, with notes and media per question, and a review before sending. Reports are sent to the incident-reports endpoints. The report is created first, as its attachments and notes reference it: attachments are uploaded once it exists, and notes share the report's mutation scope so they are sent after it, also when queued offline. The report tab lists quick and incident reports together (newest first) under a "Reports" header, with a read-only details screen for incident reports. New strings are translated in all app languages.
Only queries whose last fetch succeeded were persisted, so a refetch that failed while offline (status "error", data still there) dropped that data from the persisted cache: starting the app offline a second time found no election rounds or visits and showed the generic error screen. Also persist queries in error that still have data.
…in one tap An incident report is now saved as it is filled in, so the organization gets whatever the observer managed to send even if they can not finish: - the draft is created once the location is chosen and saved again with all the answers on every next / previous question - notes are sent when saved, edited or deleted, and media files are uploaded (and can be deleted) as soon as they are picked - "Send report" is available on every question and sends the report as it is, completed, in one tap (no review step) Drafts show "In progress" on the report list and can be continued; completed reports show "Done", open read-only and show when they were sent. Sending never waits for the server: the saves share the report's mutation scope, are queued while offline (also across app restarts) and requests that get no response are retried until they go through. The question form is typed instead of useForm<any> (the answer mapper now also accepts the Date of date and time questions), and the controls are labelled "Previous question" / "Next question" in all languages.
The full-width "Send report" button sat right under "Next question", so it was easy to hit by mistake, which locks the report. Sending is still a single tap with no confirmation (an observer may be in a hurry or in danger), but it is now kept away from the navigation: - a "Send" action (send icon + label) in the top right of the header, on every question; the form title wraps / shrinks next to it - the footer uses the same Previous / Next controls as the other forms; on the last question "Next" becomes "Send report" - a toast confirms the report was sent, or that it will be sent once back online Header gets an optional rightAction slot for a labelled action and WizzardControls an optional label for its back button. Also fixes the success toast background colour (whsl -> hsl), which left its white text unreadable.
…ports The incident report now behaves like the other forms: - the header has the form menu (⋮) with "Change language" for forms that have translations; the questions and the title switch right away, the answers are kept and the choice is saved as the form's language - "Send report" (one tap) sits under the progress bar instead of the header, so the header's rightAction slot is removed again A sent report can no longer be edited: - while its send is still pending (queued offline or behind earlier saves) a refetch of the report list keeps it completed, with the answers that were sent, instead of turning it back into a draft - once "Send report" is pressed the screen saves nothing more (e.g. a second tap on Next)
| `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T00:00:00Z`; | ||
|
|
||
| /** A date answer back to a local Date on that day (only the first 10 characters count). */ | ||
| export const fromDayIso = (value: string) => { |
There was a problem hiding this comment.
Datele salvate înainte de PR au ora completă în UTC (pickerul vechi era datetime). Dacă tai la 10 caractere, unele apar cu o zi mai devreme, de ex. 2 mai 01:30 → 2024-05-01T22:30Z → 1 mai.
| export const fromDayIso = (value: string) => { | |
| export const fromDayIso = (value: string) => { | |
| // old answers are full UTC instants: take the local day | |
| if (value.length > 10 && !/T00:00:00(\.0+)?Z$/.test(value)) { | |
| const d = new Date(value); | |
| return new Date(d.getFullYear(), d.getMonth(), d.getDate()); | |
| } |
La fel în formatDayIso.
| that got no response is retried (with a growing delay, paused while offline) until it goes through. | ||
| Errors the server answered (e.g. a locked report) are not retried. | ||
| */ | ||
| const retryWhileNoConnection = (_failureCount: number, error: unknown) => isNetworkError(error); |
There was a problem hiding this comment.
Aici failureCount e ignorat, deci la timeout sau server căzut face retry la infinit. Notele din același scope rămân blocate în spate. Offline e deja acoperit de pauză, așa că o limită ajunge:
| const retryWhileNoConnection = (_failureCount: number, error: unknown) => isNetworkError(error); | |
| const retryWhileNoConnection = (failureCount: number, error: unknown) => | |
| isNetworkError(error) && failureCount < 10; |
| // otherwise starting the app offline a second time finds nothing (no election rounds, visits, ...) | ||
| return ( | ||
| defaultShouldDehydrateQuery(query) || | ||
| (query.state.status === "error" && query.state.data !== undefined) |
There was a problem hiding this comment.
Așa se persistă și state.error (un AxiosError cu config/request) prin SuperJSON, iar query-ul revine după restart cu status: "error". Poți verifica că nu crapă serializarea și că ecranele cu isError nu arată eroare deși au date?
| tracesSampleRate: process.env.EXPO_PUBLIC_ENVIRONMENT === "production" ? 0.2 : 0, | ||
| integrations: [Sentry.reactNativeTracingIntegration()], | ||
| // 404s, timeouts and lost connectivity are expected (see isExpectedApiError) | ||
| beforeSend: (event, hint) => (isExpectedApiError(hint.originalException) ? null : event), |
There was a problem hiding this comment.
Asta scoate din Sentry toate 404-urile și erorile de rețea, inclusiv captureException de la upload. Dacă se strică un endpoint după un deploy, nu mai vedem nimic. Poate limităm filtrul de 404 doar la endpoint-urile unde e normal?
| @@ -59,6 +83,7 @@ export const DateInput: React.FC<DateInputProps> = ({ | |||
| onChange(selectedDate); | |||
There was a problem hiding this comment.
Pe Android rămân secundele din momentul alegerii, deci aceeași dată și oră aleasă din nou e văzută ca o modificare și se sincronizează iar. Pe linia de mai sus:
selectedDate.setMinutes(eventTime.getMinutes(), 0, 0);| const handle = source.open(); | ||
| try { | ||
| handle.offset = start; | ||
| const bytes = handle.readBytes(Math.min(partSize, fileSize - start)); |
There was a problem hiding this comment.
Nit: la multipart, fiecare parte tot ajunge în memoria JS aici. E mult mai bine ca base64, dar de ținut cont de mărimea părții pe telefoanele slabe.
| @@ -150,7 +155,19 @@ const PollingStationQuestionnaire = () => { | |||
| return { | |||
There was a problem hiding this comment.
Nit: switch-ul ăsta dublează mapFormSubmissionDataToAPIFormSubmissionAnswer / setFormDefaultValues, iar cazurile de dată/oră sunt acum în două locuri.
| setIs24Hour, | ||
| dateFormat, | ||
| setDateFormat, | ||
| formatTime: (value: Date) => formatTime(value, is24Hour), |
There was a problem hiding this comment.
Nit: un useMemo pe [is24Hour, dateFormat] pentru value, ca să nu se re-randeze toți consumatorii la fiecare render al root-ului.
Review comments: - date answers saved before the day picker are full UTC instants: show the local day of that instant instead of its first 10 characters (2 May 01:30 in UTC+3, stored as 2024-05-01T22:30Z, showed 1 May) - incident report saves stop retrying after 10 network failures while online (offline still pauses them), so notes queued behind are not blocked forever; a send that fails for good shows a toast - the persisted cache no longer stores request errors: a query whose refetch failed is restored with its data as a success, and the AxiosError (with the auth header in its config) is not written - Sentry only ignores 404s from the endpoints where it means there is nothing to fetch, so a broken endpoint is reported again - the Android date-time picker drops the seconds - the polling station questionnaire uses the shared answer mapping - memoized date/time format context value; CodeQL useless conditionals Date, time and date-time answers (common/utils/date-answers.ts): - date-time: the picked moment as a UTC instant, without seconds - date: the day shown, time: the time shown (no time zone shift) Tests: npm test runs tests/ with node --test (Node 22.18+): date answers in several time zones, Sentry error filter, persisted cache and incident report retries.
"Send report" moves from under the progress bar to the form menu (⋮), which is now shown on every question; "Change language" stays there for forms with translations. Sending still takes the answer on screen and locks the report in one tap; on the last question "Next" still becomes "Send report". The send icon, no longer used, is removed.
Summary
New question types
HH:mm:ss.Display logic
AnyOf/Allconditions, matched againstoptionIds.valuekeep working.10 > 9is now true; before, they were compared as text.Unknown question types
Answer timestamps
createdAt/lastUpdatedAt. These stay the same when the answer is unchanged.Translations
Splash screen
expo-splash-screenplugin with a logo-only image (assets/icons/splash-logo.png), 190dp wide on Android and 280pt on iOS. 190dp is the widest the logo can be while staying inside the circle Android shows.Deleting a submission
Checks
tsc --noEmitand ESLint pass.