diff --git a/ios/App/App/Utils.swift b/ios/App/App/Utils.swift index 3aad44a0dc..1950cf337c 100644 --- a/ios/App/App/Utils.swift +++ b/ios/App/App/Utils.swift @@ -12,6 +12,38 @@ import UIKit @objc(Utils) public class Utils: CAPPlugin { private var currentInterfaceStyle: UIUserInterfaceStyle = .unspecified + private var contentSizeObserver: NSObjectProtocol? + + public override func load() { + super.load() + + contentSizeObserver = NotificationCenter.default.addObserver( + forName: UIContentSizeCategory.didChangeNotification, + object: nil, + queue: .main + ) { [weak self] _ in + guard let self = self else { return } + self.notifyListeners("contentSizeCategoryChanged", data: self.contentSizePayload()) + } + } + + deinit { + if let observer = contentSizeObserver { + NotificationCenter.default.removeObserver(observer) + } + } + + private func contentSizePayload() -> [String: Any] { + let category = UIApplication.shared.preferredContentSizeCategory + let base: CGFloat = 17.0 + let scaled = UIFontMetrics(forTextStyle: .body).scaledValue(for: base) + let scale = scaled / base + + return [ + "category": category.rawValue, + "scale": scale + ] + } @objc func isZoomed(_ call: CAPPluginCall) { @@ -22,6 +54,10 @@ public class Utils: CAPPlugin { call.resolve(["isZoomed": isZoomed]) } + @objc func getContentSize(_ call: CAPPluginCall) { + call.resolve(contentSizePayload()) + } + @objc func getDocumentRoot(_ call: CAPPluginCall) { let doc = FileManager.default.urls( for: .documentDirectory, diff --git a/src/main/frontend/components/block.css b/src/main/frontend/components/block.css index 5f5f100352..3c166aed28 100644 --- a/src/main/frontend/components/block.css +++ b/src/main/frontend/components/block.css @@ -58,10 +58,13 @@ @apply inline; .icon-emoji-wrap { - @apply relative top-[2px] pl-[1px]; + position: relative; + top: 0.08em; + padding-left: 1px; &.as-emoji { - @apply top-0 pr-[1px]; + top: 0.02em; + padding-right: 1px; } } @@ -69,6 +72,11 @@ @apply inline-flex items-center pr-0.5; } + .icon-emoji-wrap em-emoji, + .icon-cp-container .ui__icon { + vertical-align: middle; + } + .block-title-wrap.as-heading { @apply inline text-[length:inherit]; } @@ -208,7 +216,8 @@ &.bullet-closed { .ls-icon-color-wrap em-emoji { - @apply relative -top-[1px]; + position: relative; + top: -0.08em; } } } @@ -747,23 +756,32 @@ } .bullet-container { - display: flex; - height: 16px; - width: 16px; + display: inline-flex; + height: 1em; + width: 1em; + min-width: 1em; + flex-shrink: 0; + align-self: center; border-radius: 50%; justify-content: center; align-items: center; + line-height: 1; + vertical-align: middle; .bullet-heading { background-color: var(--ls-block-bullet-color, #8fbc8f); } &.as-order-list { - @apply w-[22px] whitespace-nowrap justify-center pl-[3px]; + width: 1.4em; + min-width: 1.4em; + @apply whitespace-nowrap justify-center pl-[3px]; } .bullet { - @apply rounded-full w-[6px] h-[6px] text-[15px] opacity-80; + @apply rounded-full opacity-80; + width: 0.4em; + height: 0.4em; > * { @apply cursor-pointer; @@ -792,6 +810,9 @@ } .bullet-link-wrap { + @apply inline-flex items-center; + line-height: 1; + vertical-align: middle; color: var(--ls-primary-text-color); .ui__icon { diff --git a/src/main/frontend/mobile/util.cljs b/src/main/frontend/mobile/util.cljs index 2752e35865..10640100e6 100644 --- a/src/main/frontend/mobile/util.cljs +++ b/src/main/frontend/mobile/util.cljs @@ -36,6 +36,31 @@ (set! native-selection-action-bar (registerPlugin "NativeSelectionActionBarPlugin")) (set! ios-utils (registerPlugin "Utils"))) +(defonce ios-content-size-listener nil) + +(defn- set-ios-font-scale! + [scale] + (let [^js style (.-style js/document.documentElement) + scale (or scale 1)] + (.setProperty style "--ls-mobile-font-scale" (str scale)))) + +(defn sync-ios-content-size! + "Fetch the current iOS Dynamic Type scale and sync it to CSS variables. + Also attaches a listener to keep it in sync when the user changes the setting." + [] + (when (native-ios?) + (let [apply-scale! (fn [payload] + (let [payload (js->clj payload :keywordize-keys true)] + (set-ios-font-scale! (:scale payload))))] + (p/let [payload (p/chain (.getContentSize ^js ios-utils) + #(js->clj % :keywordize-keys true))] + (set-ios-font-scale! (:scale payload))) + (when (nil? ios-content-size-listener) + (set! ios-content-size-listener + (.addListener ^js ios-utils "contentSizeCategoryChanged" + (fn [^js payload] + (apply-scale! payload)))))))) + (defn hide-splash [] (.hide SplashScreen)) diff --git a/src/main/mobile/bottom_tabs.cljs b/src/main/mobile/bottom_tabs.cljs index a596de5e21..517e29f8c4 100644 --- a/src/main/mobile/bottom_tabs.cljs +++ b/src/main/mobile/bottom_tabs.cljs @@ -39,13 +39,14 @@ `f` receives the tab id string. Returns the Capacitor listener handle; call `(.remove handle)` to unsubscribe." [f] - (.addListener - liquid-tabs - "tabSelected" - (fn [data] + (when (and (util/capacitor?) liquid-tabs) + (.addListener + liquid-tabs + "tabSelected" + (fn [data] ;; data is like { id: string } - (when-let [id (.-id data)] - (f id))))) + (when-let [id (.-id data)] + (f id)))))) (defn add-search-listener! "Listen to native search query changes from the SwiftUI search tab. diff --git a/src/main/mobile/components/app.cljs b/src/main/mobile/components/app.cljs index 7d76efc883..9eea8741ea 100644 --- a/src/main/mobile/components/app.cljs +++ b/src/main/mobile/components/app.cljs @@ -28,7 +28,7 @@ (rum/defc journals [] (ui-component/classic-app-container-wrap - [:div.pt-3 + [:div.pt-6 (journal/all-journals)])) (rum/defc home-inner < rum/static diff --git a/src/main/mobile/components/app.css b/src/main/mobile/components/app.css index de1f5b0fc9..690c032a61 100644 --- a/src/main/mobile/components/app.css +++ b/src/main/mobile/components/app.css @@ -4,9 +4,11 @@ } :root { - --ls-page-title-size: 26px; + --ls-mobile-font-scale: 1; + --ls-page-title-size: calc(24px * var(--ls-mobile-font-scale, 1)); --safe-area-inset-top: 40px; --safe-area-inset-bottom: 16px; + --ls-mobile-font-size: 16px; } html.is-native-ios { @@ -14,6 +16,29 @@ html.is-native-ios { --safe-area-inset-bottom: 24px; } +html.is-native-ios, +html.is-ios { + font-size: calc(var(--ls-mobile-font-size) * var(--ls-mobile-font-scale, 1)); +} + +html.is-native-ios body, +html.is-native-ios textarea, +html.is-native-ios input, +html.is-native-ios select, +html.is-native-ios .block-content, +html.is-ios body, +html.is-ios textarea, +html.is-ios input, +html.is-ios select, +html.is-ios .block-content { + font-size: inherit; +} + +html.is-native-ios .block-content, +html.is-ios .block-content { + line-height: 1.6; +} + html.has-mobile-keyboard { body { @apply overflow-hidden @@ -331,3 +356,8 @@ div[data-radix-menu-content] { position:fixed; top:-9999px; } + +.bullet { + width: 0.45em; + height: 0.45em; +} diff --git a/src/main/mobile/init.cljs b/src/main/mobile/init.cljs index a7c29f41df..4592028485 100644 --- a/src/main/mobile/init.cljs +++ b/src/main/mobile/init.cljs @@ -25,7 +25,8 @@ (defn- ios-init! "Initialize iOS-specified event listeners" [] - (mobile-util/check-ios-zoomed-display)) + (mobile-util/check-ios-zoomed-display) + (mobile-util/sync-ios-content-size!)) (defn- android-init! "Initialize Android-specified event listeners" diff --git a/src/main/mobile/routes.cljs b/src/main/mobile/routes.cljs index 3944472136..196b32302f 100644 --- a/src/main/mobile/routes.cljs +++ b/src/main/mobile/routes.cljs @@ -10,7 +10,7 @@ ["/page/:name" {:name :page :view (fn [route-match] - [:div.ls-mobile-page.mt-6 + [:div.ls-mobile-page.pt-10 (page/page-cp (assoc route-match :mobile-page? true))])}] ["/import" {:name :import