mirror of
https://github.com/xweiba/location-spoofer.git
synced 2026-09-21 22:30:46 +08:00
fix: improve coordinate selection feedback
This commit is contained in:
@@ -431,8 +431,8 @@ struct MapHomeView: View {
|
|||||||
HStack {
|
HStack {
|
||||||
VStack(alignment: .leading, spacing: 3) {
|
VStack(alignment: .leading, spacing: 3) {
|
||||||
Text(mapState.displayName ?? "当前选点").font(.subheadline.weight(.semibold)).lineLimit(1)
|
Text(mapState.displayName ?? "当前选点").font(.subheadline.weight(.semibold)).lineLimit(1)
|
||||||
coordinateRow(label: "国内标准 GCJ-02", system: .gcj02)
|
coordinateRow(label: "GCJ-02(国内)", system: .gcj02)
|
||||||
coordinateRow(label: "国际标准 WGS-84", system: .wgs84)
|
coordinateRow(label: "WGS-84(国际)", system: .wgs84)
|
||||||
}
|
}
|
||||||
Spacer()
|
Spacer()
|
||||||
// 帮助说明按钮
|
// 帮助说明按钮
|
||||||
@@ -768,11 +768,15 @@ struct MapHomeView: View {
|
|||||||
Text(label)
|
Text(label)
|
||||||
.font(.caption2.weight(.medium))
|
.font(.caption2.weight(.medium))
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.frame(width: 112, alignment: .leading)
|
.lineLimit(1)
|
||||||
|
.fixedSize(horizontal: true, vertical: false)
|
||||||
Text(text)
|
Text(text)
|
||||||
.font(.caption.monospaced())
|
.font(.caption.monospaced())
|
||||||
.foregroundStyle(copiedCoordinateSystem == system ? .green : .secondary)
|
.foregroundStyle(copiedCoordinateSystem == system ? .green : .secondary)
|
||||||
.lineLimit(1)
|
.lineLimit(1)
|
||||||
|
.minimumScaleFactor(0.72)
|
||||||
|
.allowsTightening(true)
|
||||||
|
.layoutPriority(1)
|
||||||
}
|
}
|
||||||
.contentShape(Rectangle())
|
.contentShape(Rectangle())
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
@@ -1296,7 +1300,7 @@ struct MapHomeView: View {
|
|||||||
mapState.focusSelection(distanceMeters: currentViewport)
|
mapState.focusSelection(distanceMeters: currentViewport)
|
||||||
// Persist both forms once from the explicitly typed input boundary.
|
// Persist both forms once from the explicitly typed input boundary.
|
||||||
LastCoordinateStore.save(coordinatePair: pair, zoomMeters: currentViewport)
|
LastCoordinateStore.save(coordinatePair: pair, zoomMeters: currentViewport)
|
||||||
favorites.select(nil)
|
favorites.selectMatching(coordinatePair: pair)
|
||||||
scheduleGeocode(pair: pair, revision: mapState.selection.revision)
|
scheduleGeocode(pair: pair, revision: mapState.selection.revision)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -160,6 +160,18 @@ final class FavoriteLocationStore: ObservableObject {
|
|||||||
defaults.set(id?.uuidString, forKey: Keys.selectedID)
|
defaults.set(id?.uuidString, forKey: Keys.selectedID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@discardableResult
|
||||||
|
func selectMatching(coordinatePair: CoordinatePair) -> FavoriteLocation? {
|
||||||
|
let matchingFavorite = favorites.first {
|
||||||
|
$0.coordinatePair.matchesWGS84(
|
||||||
|
latitude: coordinatePair.wgs84.latitude,
|
||||||
|
longitude: coordinatePair.wgs84.longitude
|
||||||
|
)
|
||||||
|
}
|
||||||
|
select(matchingFavorite?.id)
|
||||||
|
return matchingFavorite
|
||||||
|
}
|
||||||
|
|
||||||
func rename(_ id: UUID, to name: String) {
|
func rename(_ id: UUID, to name: String) {
|
||||||
guard let idx = favorites.firstIndex(where: { $0.id == id }) else { return }
|
guard let idx = favorites.firstIndex(where: { $0.id == id }) else { return }
|
||||||
favorites[idx].name = name
|
favorites[idx].name = name
|
||||||
|
|||||||
@@ -19,6 +19,39 @@ final class FavoriteLocationStoreTests: XCTestCase {
|
|||||||
XCTAssertEqual(FavoriteLocationStore(defaults: defaults).selectedFavorite?.name, "深圳湾")
|
XCTAssertEqual(FavoriteLocationStore(defaults: defaults).selectedFavorite?.name, "深圳湾")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testSelectingMatchingCoordinatePairRestoresFavoriteSelection() {
|
||||||
|
let suite = "FavoriteLocationStoreTests.\(UUID().uuidString)"
|
||||||
|
let defaults = UserDefaults(suiteName: suite)!
|
||||||
|
defer { defaults.removePersistentDomain(forName: suite) }
|
||||||
|
let store = FavoriteLocationStore(defaults: defaults)
|
||||||
|
let favorite = store.save(
|
||||||
|
name: "深圳湾",
|
||||||
|
coordinatePair: CoordinateConverter.coordinatePair(
|
||||||
|
lat: 22.491_438,
|
||||||
|
lon: 113.945_702,
|
||||||
|
mapCoordinateSystem: .wgs84
|
||||||
|
),
|
||||||
|
accuracy: 20
|
||||||
|
)
|
||||||
|
store.select(nil)
|
||||||
|
|
||||||
|
let nearbyRealtimePair = CoordinateConverter.coordinatePair(
|
||||||
|
lat: 22.491_488,
|
||||||
|
lon: 113.945_752,
|
||||||
|
mapCoordinateSystem: .wgs84
|
||||||
|
)
|
||||||
|
XCTAssertEqual(store.selectMatching(coordinatePair: nearbyRealtimePair)?.id, favorite.id)
|
||||||
|
XCTAssertEqual(store.selectedFavoriteID, favorite.id)
|
||||||
|
|
||||||
|
let unrelatedPair = CoordinateConverter.coordinatePair(
|
||||||
|
lat: 31.2304,
|
||||||
|
lon: 121.4737,
|
||||||
|
mapCoordinateSystem: .wgs84
|
||||||
|
)
|
||||||
|
XCTAssertNil(store.selectMatching(coordinatePair: unrelatedPair))
|
||||||
|
XCTAssertNil(store.selectedFavoriteID)
|
||||||
|
}
|
||||||
|
|
||||||
func testFavoriteStoresBothFormsAndSelectsMatchingPairWithoutReadConversion() {
|
func testFavoriteStoresBothFormsAndSelectsMatchingPairWithoutReadConversion() {
|
||||||
let suite = "FavoriteLocationStoreTests.\(UUID().uuidString)"
|
let suite = "FavoriteLocationStoreTests.\(UUID().uuidString)"
|
||||||
let defaults = UserDefaults(suiteName: suite)!
|
let defaults = UserDefaults(suiteName: suite)!
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ grep -q 'refreshRuntimeMapCoordinateSystem(reason:' "$CONVERTER" || fail "fixed-
|
|||||||
grep -q 'scheduleBluePointMapCoordinateSystemRefresh()' "$MAP_HOME" || fail "native blue-point samples must trigger runtime map-type refresh while spoofing"
|
grep -q 'scheduleBluePointMapCoordinateSystemRefresh()' "$MAP_HOME" || fail "native blue-point samples must trigger runtime map-type refresh while spoofing"
|
||||||
! grep -A3 'private func scheduleBluePointMapCoordinateSystemRefresh' "$MAP_HOME" | grep -q 'spoofState == .active' || fail "blue-point map-type refresh must also detect the return to physical location"
|
! grep -A3 'private func scheduleBluePointMapCoordinateSystemRefresh' "$MAP_HOME" | grep -q 'spoofState == .active' || fail "blue-point map-type refresh must also detect the return to physical location"
|
||||||
grep -q 'awaitCoordinatedMapCoordinateSystemRefresh(reason: "点击实时定位")' "$MAP_HOME" || fail "realtime button must await the coordinated map-type refresh"
|
grep -q 'awaitCoordinatedMapCoordinateSystemRefresh(reason: "点击实时定位")' "$MAP_HOME" || fail "realtime button must await the coordinated map-type refresh"
|
||||||
|
grep -q 'favorites.selectMatching(coordinatePair: pair)' "$MAP_HOME" || fail "realtime selection must restore a matching favorite selection"
|
||||||
grep -q 'awaitCoordinatedMapCoordinateSystemRefresh(reason: "保存收藏")' "$MAP_HOME" || fail "favorite save must await the coordinated map-type refresh"
|
grep -q 'awaitCoordinatedMapCoordinateSystemRefresh(reason: "保存收藏")' "$MAP_HOME" || fail "favorite save must await the coordinated map-type refresh"
|
||||||
grep -q 'awaitCoordinatedMapCoordinateSystemRefresh(reason: "App回到前台")' "$MAP_HOME" || fail "foreground recovery must reuse the coordinated map-type refresh"
|
grep -q 'awaitCoordinatedMapCoordinateSystemRefresh(reason: "App回到前台")' "$MAP_HOME" || fail "foreground recovery must reuse the coordinated map-type refresh"
|
||||||
grep -q '地图坐标标准运行期检测结果已过期,取消写入' "$CONVERTER" || fail "cancelled runtime probes must not mutate the global map type"
|
grep -q '地图坐标标准运行期检测结果已过期,取消写入' "$CONVERTER" || fail "cancelled runtime probes must not mutate the global map type"
|
||||||
@@ -76,8 +77,10 @@ grep -q '地图坐标标准运行期检测结果已过期,取消写入' "$CONV
|
|||||||
grep -q 'clearRealtimeLocationForMapCoordinateSystemChange' "$MAP_HOME" || fail "map-type changes must discard superseded blue-point samples"
|
grep -q 'clearRealtimeLocationForMapCoordinateSystemChange' "$MAP_HOME" || fail "map-type changes must discard superseded blue-point samples"
|
||||||
grep -q '地图坐标类型已变化' "$MAP_HOME" || fail "map-type changes must emit an explicit searchable business log"
|
grep -q '地图坐标类型已变化' "$MAP_HOME" || fail "map-type changes must emit an explicit searchable business log"
|
||||||
grep -q '图钉已按新类型重设' "$MAP_HOME" || fail "map-type change log must report pin reprojection"
|
grep -q '图钉已按新类型重设' "$MAP_HOME" || fail "map-type change log must report pin reprojection"
|
||||||
grep -q '国内标准 GCJ-02' "$MAP_HOME" || fail "current selection panel must show GCJ-02"
|
grep -q 'coordinateRow(label: "GCJ-02(国内)", system: .gcj02)' "$MAP_HOME" || fail "current selection panel must label the domestic coordinate as GCJ-02"
|
||||||
grep -q '国际标准 WGS-84' "$MAP_HOME" || fail "current selection panel must show WGS-84"
|
grep -q 'coordinateRow(label: "WGS-84(国际)", system: .wgs84)' "$MAP_HOME" || fail "current selection panel must label the international coordinate as WGS-84"
|
||||||
|
grep -q 'fixedSize(horizontal: true, vertical: false)' "$MAP_HOME" || fail "coordinate labels must keep their natural single-line width"
|
||||||
|
grep -q 'minimumScaleFactor(0.72)' "$MAP_HOME" || fail "coordinate values must shrink to remain on one line"
|
||||||
grep -q 'phase = .map' "$CONTENT" || fail "ContentView must explicitly gate MapHomeView construction"
|
grep -q 'phase = .map' "$CONTENT" || fail "ContentView must explicitly gate MapHomeView construction"
|
||||||
! grep -q 'startTileProbe' "$MAP_HOME" || fail "MapHomeView must not start a second fixed-anchor coordinate-system probe"
|
! grep -q 'startTileProbe' "$MAP_HOME" || fail "MapHomeView must not start a second fixed-anchor coordinate-system probe"
|
||||||
! grep -q 'initializeMap()' "$MAP_HOME" || fail "MapHomeView must not replay a second map initialization from onAppear"
|
! grep -q 'initializeMap()' "$MAP_HOME" || fail "MapHomeView must not replay a second map initialization from onAppear"
|
||||||
|
|||||||
Reference in New Issue
Block a user