Class: Ruflet::Rails::NativeApp

Inherits:
Object
  • Object
show all
Defined in:
lib/ruflet/rails/native_app.rb

Overview

Hotwire Native-style driver for ruflet_rails.

Your existing web app is the body, rendered in a WebView. Navigation works out of the box: a tiny JS bridge injected into each page intercepts link clicks and proposes them to native, so every visit becomes a NATIVE screen pushed onto the stack (with an automatic back button) — no per-link code. The native AppBar title tracks each page’s <title>, and you declare special paths as data.

Ruflet.run do |page|
  Ruflet::Rails.native_app(
    page,
    start_url: "https://myapp.com",
    title: "My App",                              # auto-updates from <title>
    actions: -> { [icon_button("search", on_click: ->(_e) { ... })] },
    navigation_bar: navigation_bar(destinations: [...]),

    # web content shown in a bottom sheet (auth, quick forms):
    modal: ["/sign_in", "/sign_up", %r{/new\z}],

    # optional: override a path with a fully native screen:
    native: { %r{\A/products/(\d+)\z} => ->(ctx) { product_screen(ctx.match[1]) } }
  )
end

Normal links just push a native webview screen (back returns). Paths listed in ‘modal:` open as a bottom sheet. Paths in `native:` render your own UI.

The bridge talks to native over the webview’s console channel (on_console_message), which works on iOS/Android/macOS. On platforms without a native webview the body degrades to a plain frame.

Defined Under Namespace

Classes: Context, Screen

Constant Summary collapse

BRIDGE_JS =

Injected into every page: report the title, and turn same-origin link clicks into native visit proposals (so they don’t load in place).

<<~JS
  (function () {
    function report(kind, value) { console.log("ruflet:" + kind + ":" + value); }
    report("title", document.title || "");
    if (window.__rufletBridgeBound) return;
    window.__rufletBridgeBound = true;
    document.addEventListener("click", function (e) {
      var a = e.target && e.target.closest ? e.target.closest("a[href]") : null;
      if (!a || a.target === "_blank") return;
      if (a.origin && a.origin !== location.origin) return; // external: leave it
      e.preventDefault();
      report("visit", a.href);
    }, true);
  })();
JS
VISIT_PREFIX =
"ruflet:visit:"
TITLE_PREFIX =
"ruflet:title:"

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(page, start_url:, title: nil, actions: nil, navigation_bar: nil, bottom_appbar: nil, modal: [], native: {}) ⇒ NativeApp

Returns a new instance of NativeApp.



63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/ruflet/rails/native_app.rb', line 63

def initialize(page, start_url:, title: nil, actions: nil, navigation_bar: nil,
               bottom_appbar: nil, modal: [], native: {})
  @page = page
  @start_url = start_url.to_s
  @title = title.to_s
  @actions = actions
  @navigation_bar = navigation_bar
  @bottom_appbar = bottom_appbar
  @modal_patterns = Array(modal).map { |p| compile_pattern(p) }
  @native_rules = native.map { |pattern, builder| [compile_pattern(pattern), builder] }
  @screens = []
  @modal_sheet = nil
end

Class Method Details

.bridge_jsObject



77
# File 'lib/ruflet/rails/native_app.rb', line 77

def self.bridge_js = BRIDGE_JS

Instance Method Details

#startObject



79
80
81
82
83
# File 'lib/ruflet/rails/native_app.rb', line 79

def start
  @page.on_view_pop = ->(_event) { pop }
  push_webview(@start_url, root: true)
  self
end