Skip to content

Conversation

@xerial
Copy link
Member

@xerial xerial commented Feb 5, 2026

Summary

  • Add Geolocation object for reactive geolocation tracking using the browser's Geolocation API
  • Add GeoPosition case class for position data (latitude, longitude, altitude, accuracy, heading, speed)
  • Add GeoError enum for error handling (PermissionDenied, PositionUnavailable, Timeout, NotSupported)
  • Add GeoOptions for configuring requests (enableHighAccuracy, timeout, maximumAge)
  • Support one-shot position requests via getCurrentPosition()
  • Support continuous watching via watch() returning PositionWatcher with Rx[Option[GeoPosition]]

Test plan

  • GeoPosition case class tests
  • GeoError enum tests
  • GeoOptions defaults and customization tests
  • PositionWatcher reactive stream tests
  • All 19 tests passing

🤖 Generated with Claude Code

Provides reactive geolocation tracking using the browser's Geolocation API:
- GeoPosition case class for position data (lat, lng, altitude, accuracy, etc.)
- GeoError enum for error handling (PermissionDenied, PositionUnavailable, Timeout)
- GeoOptions for configuring requests (high accuracy, timeout, max age)
- Geolocation.getCurrentPosition() for one-shot position requests
- Geolocation.watch() returns PositionWatcher with Rx[Option[GeoPosition]]

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions github-actions bot added doc Improvements or additions to documentation feature New feature labels Feb 5, 2026
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @xerial, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances uni-dom by integrating the browser's Geolocation API, providing developers with a robust and reactive way to access user location data. The new API simplifies location tracking, offering structured data types for positions and errors, and supporting both single-shot and continuous monitoring. This addition empowers applications built with uni-dom to easily incorporate location-aware features, improving user experience and expanding functionality.

Highlights

  • New Geolocation API: Introduced a new Geolocation object to provide reactive tracking using the browser's native Geolocation API.
  • Structured Data Types: Added GeoPosition case class for detailed position data, GeoError enum for comprehensive error handling (PermissionDenied, PositionUnavailable, Timeout, NotSupported), and GeoOptions for configuring requests (e.g., high accuracy, timeout, maximum age).
  • One-shot and Continuous Tracking: The API supports both one-shot position requests via getCurrentPosition() and continuous watching through watch(), which returns a PositionWatcher.
  • Reactive Updates: The PositionWatcher provides reactive streams (Rx[Option[GeoPosition]] and Rx[Option[GeoError]]) for real-time updates on location and errors, integrating seamlessly with the existing reactive framework.
  • Comprehensive Testing: A new test suite (GeolocationTest.scala) has been added to ensure the correctness and robustness of all new Geolocation API components, covering data structures, options, and watcher functionality.
Changelog
  • plans/2026-02-05-geolocation.md
    • Added a new design document outlining the API, goals, core types (GeoPosition, GeoError, GeoOptions), Geolocation object methods, usage examples, and implementation notes for the new Geolocation API support.
  • uni-dom-test/src/test/scala/wvlet/uni/dom/GeolocationTest.scala
    • Added a new test file containing comprehensive unit tests for GeoPosition, GeoError, GeoOptions, Geolocation.isSupported, Geolocation.watch, PositionWatcher properties (position, error, lastPosition, lastError), cancellation logic, and callback handling for getCurrentPosition.
  • uni/.js/src/main/scala/wvlet/uni/dom/Geolocation.scala
    • Implemented the core Geolocation object, GeoPosition case class, GeoError enum, GeoOptions case class, and PositionWatcher class.
    • Provided methods for checking isSupported, getCurrentPosition for one-shot requests, and watch for continuous tracking.
    • Integrated with org.scalajs.dom.window.navigator.geolocation to bridge Scala.js with the browser's native API.
    • Included utility functions for converting dom.Position and dom.PositionError to GeoPosition and GeoError respectively, and handling optional/undefined values.
  • uni/.js/src/main/scala/wvlet/uni/dom/all.scala
    • Exported the newly introduced Geolocation object, GeoPosition, GeoError, and GeoOptions to make them easily accessible through wvlet.uni.dom.all.
Activity
  • The pull request introduces a new feature, adding Geolocation API support to uni-dom.
  • New files were added for the API design, implementation, and comprehensive testing.
  • The all.scala file was modified to export the new Geolocation-related types and objects.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

The pull request introduces a valuable Geolocation API integration to uni-dom, providing reactive tracking and one-shot position requests. The API design in the plans document is clear, and the implementation in Geolocation.scala correctly handles the browser's Geolocation API. The test suite covers the basic functionality of the new types and the Geolocation object. However, there are opportunities to enhance clarity in the GeoOptions design, improve error handling specificity, and expand test coverage for the reactive streams to ensure continuous updates are correctly emitted.

// Options for position requests
case class GeoOptions(
enableHighAccuracy: Boolean = false,
timeout: Long = Long.MaxValue,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The default value Long.MaxValue for timeout in GeoOptions is used to signify 'no timeout' by the implementation. While functional, this might be less intuitive for users compared to using Option[Long] or aligning with the browser API's convention (e.g., 0 for maximumAge or omitting the property for timeout to use the browser's default of Infinity). Consider clarifying this in the documentation or adjusting the type to Option[Long] for better Scala idiomatic representation of an optional timeout.

// One-shot position request
def getCurrentPosition(
onSuccess: GeoPosition => Unit,
onError: GeoError => Unit = _ => (),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Providing a default no-op function (_ => ()) for onError can lead to silent failures if the caller doesn't explicitly provide an error handler. While convenient for simple cases, it might mask important issues. For robust applications, it's often better to encourage explicit error handling.

def watch(options: GeoOptions = GeoOptions()): PositionWatcher

class PositionWatcher extends Cancelable:
def position: Rx[Option[GeoPosition]] // None until first position
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The comment // None until first position is helpful. To be even more precise for reactive stream users, it could explicitly state that the Rx stream will emit None as its initial value before any actual position data is acquired.


class PositionWatcher extends Cancelable:
def position: Rx[Option[GeoPosition]] // None until first position
def error: Rx[Option[GeoError]] // None if no error
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the position stream, clarifying that the error stream will emit None as its initial value when there is no error would enhance clarity for reactive stream consumers.

Comment on lines +145 to +148
test("Geolocation.getCurrentPosition accepts callbacks"):
var called = false
Geolocation.getCurrentPosition(onSuccess = _ => called = true, onError = _ => called = true)
// In jsdom environment without geolocation, this should call onError
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This test currently only verifies that getCurrentPosition doesn't throw an exception. In a jsdom environment without geolocation support, it should specifically call the onError callback. A more robust test would mock the geolocation object to explicitly trigger success or error scenarios and assert that the correct callback (onSuccess or onError) is invoked.

Comment on lines +173 to +183
val watcher = Geolocation.watch()
var emittedValue = false
val cancel = watcher
.error
.run { _ =>
emittedValue = true
}
// Initial value should be emitted
emittedValue shouldBe true
cancel.cancel
watcher.cancel
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Similar to the position stream test, this test only confirms the initial emission of the error stream. It does not verify that actual GeoError instances are emitted when an error occurs during continuous watching. A comprehensive test would mock geolocation.watchPosition to trigger error callbacks and assert the emission of specific GeoError types.

*/
case class GeoOptions(
enableHighAccuracy: Boolean = false,
timeout: Long = Long.MaxValue,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using Long.MaxValue as a sentinel for 'no timeout' is a valid approach, but it might be more idiomatic in Scala to represent an optional timeout using Option[Long]. This would explicitly convey that the timeout parameter is optional and align with common Scala patterns for handling absence of a value. If Option[Long] is used, the toPositionOptions method would need to be updated accordingly.

    timeout: Option[Long] = None,

*/
def getCurrentPosition(
onSuccess: GeoPosition => Unit,
onError: GeoError => Unit = _ => (),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Providing a default no-op function (_ => ()) for onError can lead to silent failures if the caller doesn't explicitly provide an error handler. While convenient for simple cases, it might mask important issues. For robust applications, it's often better to encourage explicit error handling.

Comment on lines 231 to 232
case _ =>
GeoError.PositionUnavailable
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Mapping all unknown dom.PositionError codes to GeoError.PositionUnavailable might obscure the true nature of an unexpected error. It would be more informative to introduce a GeoError.UnknownError(code: Short) or GeoError.Other(message: String) to capture and report these specific, unexpected error codes, which can aid in debugging and providing more accurate user feedback.

      case _ =>
        GeoError.PositionUnavailable // Or GeoError.UnknownError(err.code)

Comment on lines +236 to +237
opts.enableHighAccuracy = options.enableHighAccuracy
if options.timeout != Long.MaxValue then
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

If GeoOptions.timeout were changed to Option[Long] as suggested, this if condition would be replaced by a foreach call, making the code more concise and idiomatic Scala.

    options.timeout.foreach { t =>
      opts.timeout = t.toDouble
    }

- Add safer isSupported check for non-browser environments (Node.js)
- Improve Rx stream documentation to clarify initial None values
- Add comments documenting error code mapping
- Make toOption more robust for undefined/NaN handling

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@xerial xerial merged commit 2ce8c35 into main Feb 5, 2026
13 checks passed
@xerial xerial deleted the feature/geolocation branch February 5, 2026 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

doc Improvements or additions to documentation feature New feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant