dotswan / filament-map-picker

Map Picker is a Filament custom field designed to simplify the process of choosing a location on a map and obtaining its geo-coordinates.
MIT License
61 stars 14 forks source link

AfterStateUpdated always called after $set('location', ['lat' => $latitude, 'lng' => $longitude]) #29

Closed Jamerze closed 1 month ago

Jamerze commented 2 months ago

Hello,

While experimenting further with the maps plugin, I encountered the following issue:

The code below is how my location section is rendered, I have an address field, latitude and longitude field and the map itself.

The functions are almost working well, however, when I set the location with $set('location', ['lat' => $latitude, 'lng' => $longitude]), the afterstateupdated function of the map is called every time.

In this case, this means that when I changed the address, the address field calls the updatelatlng function. This function will then update the latitude, longitude and location, and when location is set it is calling the updateaddress function (because it is defined in the afterstateupdated function of the map that is being called) which leads to the map continuously moving from one place to another. Removing the afterstateupdated is also not an option, because then it won't save the changes anymore when I move the marker on the map itself.

Is this a bug and should afterstateupdated indeed only be called when you move something on the map, and not when the location is set externally (in this case my functions), or is this done by design?

Tabs\Tab::make(__('lang.events.tabs.tab_location')) ->icon('fluentui-location-12-o') ->schema([ Components\Section::make() ->schema([ Components\TextInput::make('address') ->label(__('lang.events.fields.address')) ->live(onBlur: true) ->afterStateUpdated(fn ($state, callable $get, callable $set, callable $livewire) => static::updateLatLng($get, $set, $livewire)) ->columnSpan(2), Components\Toggle::make('show_coordinates') ->label(__('lang.events.fields.show_coordinates')) ->inline(false) ->live(onBlur: true) ->dehydrated(false) ->columnSpan(1), Components\TextInput::make('latitude') ->label(__('lang.events.fields.latitude')) ->hidden(fn (Forms\Get $get) => !$get('show_coordinates')) ->live(onBlur: true) ->afterStateUpdated(fn ($state, callable $get, callable $set, callable $livewire) => static::updateAddress($get, $set, $livewire)) ->columnSpan(2), Components\TextInput::make('longitude') ->label(__('lang.events.fields.longitude')) ->hidden(fn (Forms\Get $get) => !$get('show_coordinates')) ->live(onBlur: true) ->afterStateUpdated(fn ($state, callable $get, callable $set, callable $livewire) => static::updateAddress($get, $set, $livewire)) ->columnSpan(2), Map::make('location') ->hiddenLabel() ->columnSpanFull() ->afterStateUpdated(function (Forms\Set $set, ?array $state, callable $get, callable $livewire): void { $set('latitude', $state['lat']); $set('longitude', $state['lng']); static::updateAddress($get, $set, $livewire); }) ->afterStateHydrated(function ($state, Forms\Get $get, Forms\Set $set): void { $set('location', ['lat' => $get('latitude') ?? 0, 'lng' => $get('longitude') ?? 0]); }) ->extraStyles([ 'border-radius: 50px', 'min-height: 30vh', 'z-index: 0 !important' ]) ->liveLocation() ->showMarker() ->markerColor("#22c55eff") ->showFullscreenControl() ->showZoomControl() ->draggable() ->tilesUrl("https://tile.openstreetmap.de/{z}/{x}/{y}.png") ->zoom(15) ->detectRetina() ->showMyLocationButton() ->extraTileControl([]) ->extraControl([ 'zoomDelta' => 1, 'zoomSnap' => 2, ]) ->visible(fn ($state) => !empty($state['lat']) && !empty($state['lng'])) ->columnSpanFull(), ]) ->columns(4) ])

The functions are as follows:

`protected static function updateLatLng(callable $get, callable $set, callable $livewire) { $address = $get('address');

    if ($address) {
        $coordinates = static::getLatLngFromAddress($address);
        if ($coordinates) {
            $set('latitude', $coordinates['lat']);
            $set('longitude', $coordinates['lng']);
            $set('location', ['lat' => $coordinates['lat'], 'lng' => $coordinates['lng']]);
        }
    } else {
        $set('location', ['lat' => 0, 'lng' => 0]);
    }

    $livewire->dispatch('refreshMap');
}

protected static function updateAddress(callable $get, callable $set, callable $livewire)
{
    $latitude = $get('latitude');
    $longitude = $get('longitude');

    if ($latitude && $longitude) {
        $address = static::getAddressFromLatLng($latitude, $longitude);
        $set('address', $address);
        $set('location', ['lat' => $latitude, 'lng' => $longitude]);
    } else {
        $set('location', ['lat' => 0, 'lng' => 0]);
    }

    $livewire->dispatch('refreshMap');
}

protected static function getLatLngFromAddress($address)
{
    $response = Http::get('https://photon.komoot.io/api/', [
        'q' => $address,
        'limit' => 1,
    ]);

    if ($response->successful() && !empty($response->json()['features'])) {
        $data = $response->json()['features'][0]['geometry']['coordinates'];
        return [
            'lat' => $data[1],
            'lng' => $data[0],
        ];
    }

    return [
        'lat' => 0,
        'lng' => 0,
    ];
}

protected static function getAddressFromLatLng($latitude, $longitude)
{
    $response = Http::get('https://photon.komoot.io/reverse', [
        'lat' => $latitude,
        'lon' => $longitude,
    ]);

    if ($response->successful() && !empty($response->json()['features'])) {
        $data = $response->json()['features'][0]['properties'];
        return implode(', ', array_filter([
            $data['name'] ?? '',
            $data['street'] ?? '',
            $data['city'] ?? '',
            $data['state'] ?? '',
            $data['country'] ?? ''
        ]));
    }

    return [
        'lat' => 0,
        'lng' => 0,
    ];
}`
mohaphez commented 1 month ago

Hi @Jamerze ,

First of all, I recommend sending long code snippets as screenshots or using appropriate code blocks to improve readability and make it easier to understand what's going on.

After reviewing your code, I don't believe the issue is related to the package itself or a bug within it. The problem seems to stem from how your code is designed. The recursive behavior you're experiencing happens because the logic in your functions triggers continuous updates between the address and coordinates, which makes the map keep refreshing.

I can't suggest a direct fix for your code structure, but this doesn't seem to be an issue with the package.

If you have any specific questions or problems with the package itself, feel free to reach out. We're happy to help!

Thanks