Tienson Qin 3 месяцев назад
Родитель
Сommit
7e8ed5333a

+ 36 - 0
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,

+ 29 - 8
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 {

+ 25 - 0
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))
 

+ 7 - 6
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.

+ 1 - 1
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

+ 31 - 1
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;
+}

+ 2 - 1
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"

+ 1 - 1
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