Enhance map functionality and API documentation

- Updated API documentation for the `rebuildZooms` endpoint to clarify its long execution time and response behavior.
- Modified MapView component to manage tile cache invalidation after rebuilding zoom levels, ensuring fresh tile display.
- Introduced a new composable for handling tile cache invalidation state after admin actions.
- Enhanced character icon creation to reflect ownership status with distinct colors.
- Improved loading state handling in various components for better user experience during data fetching.
This commit is contained in:
2026-03-01 19:09:46 +03:00
parent 8331473808
commit 225aaa36e7
16 changed files with 236 additions and 52 deletions

View File

@@ -334,7 +334,10 @@ func (h *Handlers) APIAdminRebuildZooms(rw http.ResponseWriter, req *http.Reques
if h.requireAdmin(rw, req) == nil {
return
}
h.Admin.RebuildZooms(req.Context())
if err := h.Admin.RebuildZooms(req.Context()); err != nil {
HandleServiceError(rw, err)
return
}
rw.WriteHeader(http.StatusOK)
}

View File

@@ -349,6 +349,6 @@ func (s *AdminService) HideMarker(ctx context.Context, markerID string) error {
}
// RebuildZooms delegates to MapService.
func (s *AdminService) RebuildZooms(ctx context.Context) {
s.mapSvc.RebuildZooms(ctx)
func (s *AdminService) RebuildZooms(ctx context.Context) error {
return s.mapSvc.RebuildZooms(ctx)
}

View File

@@ -207,8 +207,7 @@ func (s *ExportService) Merge(ctx context.Context, zr *zip.Reader) error {
for _, op := range ops {
s.mapSvc.SaveTile(ctx, op.MapID, app.Coord{X: op.X, Y: op.Y}, 0, op.File, time.Now().UnixNano())
}
s.mapSvc.RebuildZooms(ctx)
return nil
return s.mapSvc.RebuildZooms(ctx)
}
func (s *ExportService) processMergeJSON(

View File

@@ -226,21 +226,33 @@ func (s *MapService) UpdateZoomLevel(ctx context.Context, mapid int, c app.Coord
slog.Error("failed to create zoom dir", "error", err)
return
}
f, err := os.Create(fmt.Sprintf("%s/%d/%d/%s.png", s.gridStorage, mapid, z, c.Name()))
s.SaveTile(ctx, mapid, c, z, fmt.Sprintf("%d/%d/%s.png", mapid, z, c.Name()), time.Now().UnixNano())
path := fmt.Sprintf("%s/%d/%d/%s.png", s.gridStorage, mapid, z, c.Name())
relPath := fmt.Sprintf("%d/%d/%s.png", mapid, z, c.Name())
f, err := os.Create(path)
if err != nil {
slog.Error("failed to create tile file", "path", path, "error", err)
return
}
defer f.Close()
png.Encode(f, img)
if err := png.Encode(f, img); err != nil {
f.Close()
os.Remove(path)
slog.Error("failed to encode tile PNG", "path", path, "error", err)
return
}
if err := f.Close(); err != nil {
slog.Error("failed to close tile file", "path", path, "error", err)
return
}
s.SaveTile(ctx, mapid, c, z, relPath, time.Now().UnixNano())
}
// RebuildZooms rebuilds all zoom levels from base tiles.
func (s *MapService) RebuildZooms(ctx context.Context) {
// It can take a long time for many grids; the client should account for request timeouts.
func (s *MapService) RebuildZooms(ctx context.Context) error {
needProcess := map[zoomproc]struct{}{}
saveGrid := map[zoomproc]string{}
s.st.Update(ctx, func(tx *bbolt.Tx) error {
if err := s.st.Update(ctx, func(tx *bbolt.Tx) error {
b := tx.Bucket(store.BucketGrids)
if b == nil {
return nil
@@ -254,7 +266,10 @@ func (s *MapService) RebuildZooms(ctx context.Context) {
})
tx.DeleteBucket(store.BucketTiles)
return nil
})
}); err != nil {
slog.Error("RebuildZooms: failed to update store", "error", err)
return err
}
for g, id := range saveGrid {
f := fmt.Sprintf("%s/grids/%s.png", s.gridStorage, id)
@@ -271,6 +286,7 @@ func (s *MapService) RebuildZooms(ctx context.Context) {
needProcess[zoomproc{p.c.Parent(), p.m}] = struct{}{}
}
}
return nil
}
// ReportMerge sends a merge event.