Refactor/home page background - #522
Open
whes1015 wants to merge 64 commits into
Open
Conversation
whes1015
force-pushed
the
refactor/home-page-background
branch
from
August 9, 2026 15:25
551706a to
0481412
Compare
whes1015
force-pushed
the
refactor/home-page-background
branch
from
August 9, 2026 15:32
0481412 to
25b7bd4
Compare
whes1015
force-pushed
the
refactor/home-page-background
branch
from
August 10, 2026 14:09
92f3d06 to
42589dc
Compare
Wire the home sheet's 近 1 小時降雨趨勢 card to
GET /api/weather/rainforecast/{code} (exptech.dingbot.tw), decoding the
start/60×mm envelope into RainHourTrend with loading/error states.
Keep the blur/dim layer's tree shape constant (ImageFiltered.enabled + an always-present transparent scrim) so the sky's element is never re-parented when the sheet starts climbing or content scrolls — which used to reset WeatherSkyBackground's shader cache and flash the flat fallback colour.
CWB's ones digit refines the glyph: showers, fog banks, hail and lightning each get their own icon (with 大雷雨 filled, the heaviest rung), while the accent colour keeps following the resolved mode so the family tint stays consistent.
Featured earthquake card on primaryContainer plus a two-column ranking grid with accent-coloured icons and badges.
RainHourTrend gains a structured summary (grade, sustained, stop time) that drives a one-line reading under the card title; the chart's axis now labels minutes-from-now ticks instead of Taipei wall clock times.
Pre-render one PNG per speed bucket with a black outline baked in (MapLibre's icon-halo only works on true SDF images), pick the image via a step expression, and expose readingIcon so sheets can reuse the arrow.
The speed curve is drawn in per-bucket runs (the same white→pink ramp as the map arrows) instead of one flat line, and the X-axis ticks use speed-coloured WindArrowIcon glyphs; the station hero gains the same rotating arrow icon.
fl_chart requires one tooltip item per touched spot; a touch at a segment boundary hits two bars at the same x, and deduping them shrank the list — drawTouchTooltip threw "tooltipItems and touchedSpots size should be same". windTooltipItems returns every item; the identical duplicates overlap and read as one card.
Bump the native memory cache to 24M and warm outward from the selected frame (0 ±4, then ±12 and beyond while space remains): warm() gains a fillUntil target so injection stops at the mirror's cap instead of churning LRU, and the timeline re-centres its warm band when scrubbing past it.
Extract the radar layer's scan-range + county + township overlay into a ScanRangeOverlayChrome mixin and reuse it for the QPESUMS layer, so the next-1-hour forecast layer draws the same boundaries as the radar.
After a hot restart the engine can still own the previous isolate's view id, so UiKitView creation fails with recreating_view and onMapCreated never fires. Remount the map with a fresh key when it isn't ready within 4s (bounded retries), and treat the benign platform exception as neither a log entry nor a fatal crash.
Replace the native MapLibre compass (rendered inside the platform view, so any Flutter overlay — typhoon forecast tooltips, an expanded sheet — paints over it) with a Flutter MapCompass at the top of the map stack, driven by the camera bearing and tapping to reset north.
未來1小時 → 未來 1 小時 for the four Chinese variants.
Legacy handled tap → navigation inside notify.dart; the rewrite had it as a private method on the app service host. Move it to notification_routes.dart (which already owns the channel → route table) as routeNotificationTap — the app host just registers it on NotificationTaps — with an injectable navigate for tests.
whes1015
force-pushed
the
refactor/home-page-background
branch
from
August 10, 2026 16:00
8306f93 to
7e3c228
Compare
An empty series in the rainforecast envelope (or an all-zero one) is the API's way of saying no rain this hour, so decode returns a dry trend instead of failing, the home trend card hides itself, and the chart paints minutes outside the window as one continuous grey band rather than full-height bars. A no-data pane labels the missing span.
When the next-hour forecast is dry there is no rain chart to show, so the hero slot swaps the trend card for the 24-hour forecast card, which starts as a one-glance summary and expands into the full card as the sheet is pulled up — one widget, not two cards.
所在地 is a place, not a region: frame the camera on the live GPS fix (a small span around it) instead of the whole township, while saved townships keep their bounds-centred framing. Falls back to the township box, or the whole island, when no fix is available.
math.pow(x, 2) routes through the generic power path and needs an `as double` cast; x*x is a plain multiply on the already-double operands. Equivalent up to floating-point rounding.
nearestSample ran a linear scan for every axis tick and tooltip — a 7 d wind series is ~170 points, and the x-values are monotonic, so a binary search finds the same index in O(log n). Ties resolve to the earlier index, matching the old strict-< scan.
The 8 offset neighbours that bake the black outline were declared twice — once in the map PNG renderer, once in WindArrowIcon — with identical values. Hoist one windOutlineDirs constant both use.
The frame ids were sorted as strings, then immediately re-sorted as ints, wasting a pass and repeatedly int-parsing inside the comparator. Keep the int sort only.
Clamp the chunk end with min() and always pass the same sublist form instead of branching on whether the last chunk is short.
The draggable sheet re-rendered its whole backdrop every scroll tick: the sky shader repainted each frame even under the blur, the scroll blur re-computed every tick with nothing cached behind it, and card rain kept running on cards already scrolling off-screen. - Freeze the sky (active=false) the instant the list scrolls — the blur about to cover it hides the hold, and holding the last frame lets the blur layer cache; the state clock stops with the ticker, so resuming at the top restarts where it left off. - Quantise the scroll blur/dim to a 4-step ladder, so the full-screen blur re-renders only on level changes instead of every tick. - Rebuild HomeContent's ListView shell only when the sky re-bakes; the scroll-driven reveal/expansion lives on one inner builder, merging the hero's bottom-gap and dry-hour forecast builders. - Stop RainOnGlass and the card-water LiquidFun worlds the moment the position gate closes (cut the water outright instead of draining), and resume on the next rebuild once the gate reopens.
The sky screen pass was a full-screen shader whose output is a pure vertical gradient of a fixed LUT column, so it is now CPU-baked once per re-bake (4×1024) and stretched per frame. The cloud shader's LUT probes share a constant u, so the horizontal filtering moved to a CPU-extracted 1×141 column at bake time and each probe is a vertical 2-tap. The galaxy layer and starmap, never loaded in production, are gone. Base/haze sky colours arrive as baked uniforms, and dead uniforms (iFrustumA/C, iSunElevParam) are dropped. A parity test pins the baked gradient to the reference shader within its own dither. The rain particle field no longer allocates a Color or boxes a clamp num per drop each frame (up to 1792 drops at storm grade); the packed ARGB32 and branch clamps are bit-identical. Forcing an experimental weather mode now drops the live rain/snow/humidity overrides, so a forced clear sky no longer keeps raining. The resolution is a pure function with a regression test.
The picker's header gains a reorder button that opens a fixed-height editor over the sheet (two stacked drag sheets felt broken). The saved order is a list of layer ids resolved against each surface's layers via orderedLayers(), so a layer added after the order was saved appends at the bottom, a stale id is ignored, and duplicates count once — future layers can never throw. Every drop persists; a reset button restores the declared order.
The bottom-nav More tab's default-layer picker was missing the QPESUMS layer. Add it alongside radar, with its own nav label and icon, so users can default the Map tab to the 1-hour precipitation forecast.
Replace the broad three-way split (reference / weather / hazard) with the six categories the app's data actually falls into — earthquake, typhoon, weather observations, satellite, radar, and daily life. The layer picker and the default-layer settings page both group under these headers, and `categoryOf` falls back deterministically so a layer added later still resolves without crashing.
The picker's order sheet previously only reordered layers within a category. Persist the category order alongside the layer order (same tolerant merge, same reset) and make a category header draggable — it moves its whole block, while a layer drag still clamps to its category and a drop always snaps to a group boundary so a block can't land inside another group.
Reordering a flat list of categories and layers mixed together got confusing once categories were sortable — a header drag moved a whole block but a stray drop could still land mid-list. Make the editor two-level instead: level 1 reorders the categories (draggable, persisted to the category order), and tapping a category drills into level 2 to reorder just its layers. Each level is a homogeneous list, so no block banding or boundary snapping is needed, and a single-layer category isn't drill-in-able at all.
Set the default category order to earthquake → radar → typhoon → weather → satellite → life, moving the radar group up past typhoon. This is the declared order `orderedCategories` falls back to when the user hasn't saved a custom category order.
Advance the pinned maplibre-gl ref from d3deabf to 7c2e249 for the newer native tile-bridge changes.
Drop highp float to mediump in the weather sky, card-water, rain-on-glass, and sun-flare fragments — mediump is sufficient for their colour work and is cheaper on tile-based GPUs.
Radar, satellite, and QPESUMS each had a near-verbatim API class and repository impl differing only in the URL path segment. Collapse them into a parameterised `FrameTileApi` (path + shared delta-frame decode) and a single `FrameTileRepositoryImpl` that implements all three empty domain interfaces; the provider layer still injects each overlay under its own interface.
Centralise the `* pi / 180` conversion (11 sites) into `core/geo/geo_math.dart`, share the value-ramp interpolation `rampColor` between the rain and weather-station layers, and let `LatLng` reuse degToRad instead of a private alias.
Sheet layers (weather stations, rain, lightning, RTS, typhoon, DPM) each hand-wrote the same ten-plus no-op bodies for the MapLayer members they don't use. A `MapLayerDefaults` mixin provides them once; layers override only what they actually do. Also folds the shared 4/maxZoom floor into the defaults.
- Extract the code→station map and delta-seconds history decodes shared by the meteor weather/rain/lightning repositories. - Move boundsFromPoints into camera_fit and reuse it from the report detail page instead of a second min/max loop. - Use AppTime.utc for the rain-trend dry/placeholder "now" instead of DateTime.now, per the calibrated-time convention.
Generalize WeatherStationLayer over the snapshot/trend/observation types so the rain overlay (which only differs in its interval-scrubbing source) is a thin subclass instead of a parallel copy.
The weather / rain / lightning datasets share the station, latest, list, at, and trend surface; fold the three near-identical clients and impls into one parameterized MeteorSnapshotApi / MeteorSnapshotRepositoryImpl.
Three models each decoded {updated, cyclones} payloads and blank/coord
helpers by hand; pull the common decodes into typhoon_decode and lat_lng
pair helpers.
Replace the ad-hoc +8h DateTime arithmetic with one calibrated facade so every wall-clock conversion reads the same way.
Add LegendDot, InlineLoading, trimTrailingZero, and an EventType icon key + resolver so the dozen hand-rolled spinners, status dots, axis labels, and event icons render from one place.
Centralize extent listening, the at-top flag, the grip, the frosted surface, and the full draggable-sheet chrome (ExtentSheetChrome) that four sheets used to copy verbatim.
…ndpoint Put the wire paths shared by the ETag cache and the API clients in one ApiPaths registry so they can't drift, reuse MeteorDecode in the frame list, and delete the unused /api/v1/eq/report client.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.