Skip to content

Commit

Permalink
refactor: isolate FFI
Browse files Browse the repository at this point in the history
  • Loading branch information
ghivert committed Jul 23, 2024
1 parent 6be385e commit e5538e7
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 26 deletions.
10 changes: 10 additions & 0 deletions apps/frontend/src/data/model.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub type Model {
loading: Bool,
view_cache: Dict(String, Element(Msg)),
route: router.Route,
is_mobile: Bool,
trendings: Option(List(Package)),
submitted_input: String,
keep_functions: Bool,
Expand All @@ -37,6 +38,9 @@ pub type Model {
)
}

@external(javascript, "../gloogle.ffi.mjs", "isMobile")
fn is_mobile() -> Bool

pub fn init() {
let search_results = search_result.Start
let index = compute_index(search_results)
Expand All @@ -47,6 +51,7 @@ pub fn init() {
loading: False,
view_cache: dict.new(),
route: router.Home,
is_mobile: is_mobile(),
trendings: option.None,
submitted_input: "",
keep_functions: False,
Expand All @@ -67,6 +72,10 @@ pub fn update_submitted_input(model: Model) {
Model(..model, submitted_input: model.input)
}

pub fn update_is_mobile(model: Model, is_mobile: Bool) {
Model(..model, is_mobile: is_mobile)
}

pub fn update_trendings(model: Model, trendings: List(Package)) {
model.trendings
|> option.unwrap([])
Expand Down Expand Up @@ -295,6 +304,7 @@ pub fn reset(model: Model) {
loading: False,
view_cache: model.view_cache,
route: router.Home,
is_mobile: is_mobile(),
trendings: model.trendings,
submitted_input: "",
keep_functions: False,
Expand Down
3 changes: 3 additions & 0 deletions apps/frontend/src/data/msg.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,15 @@ pub type Filter {

pub type Msg {
None
OnSearchFocus
SubmitSearch
UpdateIsMobile(is_mobile: Bool)
SearchResults(input: String, result: Result(SearchResults, http.HttpError))
Trendings(result: Result(List(package.Package), http.HttpError))
UpdateInput(String)
Reset
ScrollTo(String)
OnEscape
OnRouteChange(router.Route)
OnCheckFilter(Filter, Bool)
}
36 changes: 29 additions & 7 deletions apps/frontend/src/frontend.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import data/package
import data/search_result
import frontend/config
import frontend/errors
import frontend/ffi
import frontend/router
import frontend/view
import frontend/view/body/search_result as sr
Expand All @@ -26,15 +27,29 @@ import sketch/lustre as sketch
import sketch/options as sketch_options
import toast/error as toast_error

@external(javascript, "./config.ffi.mjs", "scrollTo")
fn scroll_to_element(id: String) -> fn(dispatch) -> Nil
fn focus(on id: String) {
use _ <- effect.from()
ffi.focus(on: id)
}

@external(javascript, "./config.ffi.mjs", "subscribeFocus")
fn do_subscribe_focus() -> Nil
fn unfocus() {
use _ <- effect.from()
ffi.unfocus()
}

fn subscribe_focus() {
use _ <- effect.from()
do_subscribe_focus()
use dispatch <- effect.from()
use key <- ffi.subscribe_focus()
case key {
"Escape" -> dispatch(msg.OnEscape)
_ -> dispatch(msg.OnSearchFocus)
}
}

fn subscribe_is_mobile() {
use dispatch <- effect.from()
use is_mobile <- ffi.suscribe_is_mobile()
dispatch(msg.UpdateIsMobile(is_mobile))
}

pub fn main() {
Expand Down Expand Up @@ -63,6 +78,7 @@ fn init(_) {
|> update.add_effect(modem.init(on_url_change))
|> update.add_effect(router.update_page_title({ initial.0 }.route))
|> update.add_effect(subscribe_focus())
|> update.add_effect(subscribe_is_mobile())
|> update.add_effect(
http.expect_json(dynamic.list(package.decoder), msg.Trendings)
|> http.get(config.api_endpoint() <> "/trendings", _),
Expand All @@ -83,6 +99,12 @@ fn update(model: Model, msg: Msg) {
msg.ScrollTo(id) -> scroll_to(model, id)
msg.OnRouteChange(route) -> handle_route_change(model, route)
msg.Trendings(trendings) -> handle_trendings(model, trendings)
msg.OnSearchFocus -> update.effect(model, focus(on: "search-input"))
msg.OnEscape -> update.effect(model, unfocus())
msg.UpdateIsMobile(is_mobile) ->
model
|> model.update_is_mobile(is_mobile)
|> update.none
msg.SearchResults(input, search_results) ->
handle_search_results(model, input, search_results)
msg.OnCheckFilter(filter, value) ->
Expand Down Expand Up @@ -141,7 +163,7 @@ fn submit_search(model: Model) {
}

fn scroll_to(model: Model, id: String) {
scroll_to_element(id)
ffi.scroll_to(element: id)
|> effect.from
|> update.effect(model, _)
}
Expand Down
2 changes: 1 addition & 1 deletion apps/frontend/src/frontend/errors.gleam
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@external(javascript, "../config.ffi.mjs", "captureMessage")
@external(javascript, "../gloogle.ffi.mjs", "captureMessage")
pub fn capture_message(content: String) -> String
20 changes: 20 additions & 0 deletions apps/frontend/src/frontend/ffi.gleam
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
@external(javascript, "../gloogle.ffi.mjs", "scrollTo")
pub fn scroll_to(element id: String) -> fn(dispatch) -> Nil

@external(javascript, "../gloogle.ffi.mjs", "subscribeIsMobile")
pub fn suscribe_is_mobile(callback: fn(Bool) -> Nil) -> Nil

@external(javascript, "../gloogle.ffi.mjs", "subscribeFocus")
pub fn subscribe_focus(callback: fn(String) -> Nil) -> Nil

@external(javascript, "../gloogle.ffi.mjs", "focus")
pub fn focus(on id: String) -> Nil

@external(javascript, "../gloogle.ffi.mjs", "unfocus")
pub fn unfocus() -> Nil

@external(javascript, "../gloogle.ffi.mjs", "isMac")
pub fn is_mac() -> Bool

@external(javascript, "../gloogle.ffi.mjs", "updateTitle")
pub fn update_title(title: String) -> Nil
10 changes: 4 additions & 6 deletions apps/frontend/src/frontend/router.gleam
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
import frontend/ffi
import gleam/list
import gleam/option
import gleam/result
import gleam/uri.{type Uri}
import lustre/effect

@external(javascript, "../config.ffi.mjs", "updateTitle")
fn update_title(title: String) -> Nil

pub type Route {
Home
Search(query: String)
Expand Down Expand Up @@ -37,8 +35,8 @@ fn handle_search_path(uri: Uri) {
pub fn update_page_title(route: Route) {
use _ <- effect.from()
case route {
Home -> update_title("Gloogle")
Search(q) -> update_title("Gloogle — Search " <> q)
Trending -> update_title("Gloogle — Trending")
Home -> ffi.update_title("Gloogle")
Search(q) -> ffi.update_title("Gloogle — Search " <> q)
Trending -> ffi.update_title("Gloogle — Trending")
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import data/msg
import frontend/ffi
import frontend/view/search_input/styles as s
import lustre/attribute as a
import lustre/element as el
import lustre/event as e

@external(javascript, "../../../config.ffi.mjs", "isMac")
fn is_mac() -> Bool

pub fn view(loading loading: Bool, input input: String, small small: Bool) {
let modifier = case is_mac() {
let modifier = case ffi.is_mac() {
True -> "Cmd"
False -> "Ctrl"
}
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/src/frontend/view/search_input/styles.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -81,5 +81,6 @@ pub fn shortcut_hint(attrs, children) {
s.padding_("3px 6px"),
s.border_radius(px(6)),
s.opacity(0.4),
s.media(media.max_width(px(700)), [s.display("none")]),
])
}
2 changes: 1 addition & 1 deletion apps/frontend/src/gleam/coerce.gleam
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@external(javascript, "../config.ffi.mjs", "coerce")
@external(javascript, "../gloogle.ffi.mjs", "coerce")
pub fn coerce(value: a) -> b
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,45 @@ export function coerceEvent(a) {
return a.detail
}

export function subscribeFocus() {
export function subscribeFocus(callback) {
document.addEventListener('keydown', event => {
if (event.key === 'Escape') return callback(event.key)
if ((!event.metaKey && !event.ctrlKey) || event.key !== 'k') return
const element = document.getElementById('search-input')
if (element) {
element.focus()
element.select()
}
callback(event.key)
})
}

export function focus(id) {
const element = document.getElementById(id)
if (element) {
element.focus()
element.select()
}
}

export function unfocus() {
const element = document.activeElement
if (element) {
element.blur()
}
}

export function isMac() {
return (
navigator.platform.indexOf('Mac') === 0 || navigator.platform === 'iPhone'
)
}

export function isMobile() {
return window.matchMedia('(max-width: 700px)').matches
}

export function subscribeIsMobile(callback) {
window.matchMedia('(max-width: 700px)').addEventListener('change', event => {
if (event.matches) {
callback(true)
} else {
callback(false)
}
})
}
2 changes: 1 addition & 1 deletion apps/frontend/src/lustre/lazy.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import lustre/effect
import lustre/element.{type Element}
import lustre/event

@external(javascript, "../config.ffi.mjs", "coerceEvent")
@external(javascript, "../gloogle.ffi.mjs", "coerceEvent")
fn coerce_event(a: a) -> b

const tag_name = "lazy-node"
Expand Down

0 comments on commit e5538e7

Please sign in to comment.