diff --git a/src/main/frontend/components/avatar.cljs b/src/main/frontend/components/avatar.cljs index 34235fd346..bc04ded5af 100644 --- a/src/main/frontend/components/avatar.cljs +++ b/src/main/frontend/components/avatar.cljs @@ -4,6 +4,29 @@ [logseq.shui.util :as shui-util] [rum.core :as rum])) +(defonce ^:private latin-or-number-re + (js/RegExp. "^[\\p{Script=Latin}\\p{Number}]" "u")) + +(defonce ^:private grapheme-segmenter + (when (exists? js/Intl.Segmenter) + (js/Intl.Segmenter. js/undefined #js {:granularity "grapheme"}))) + +(defn- graphemes + [s] + (if grapheme-segmenter + (map #(.-segment %) (array-seq (js/Array.from (.segment grapheme-segmenter s)))) + (array-seq (js/Array.from s)))) + +(defn- latin-or-number? + [s] + (.test latin-or-number-re s)) + +(defn- fallback-grapheme-count + [letters n] + (if (some-> letters first latin-or-number?) + n + 1)) + (defn initials ([name] (initials name 2)) @@ -11,8 +34,10 @@ (when (some? name) (let [name (string/trim (str name))] (when-not (string/blank? name) - (-> (subs name 0 (min n (count name))) - string/upper-case)))))) + (let [letters (graphemes name)] + (-> (string/join (take (fallback-grapheme-count letters n) + letters)) + string/upper-case))))))) (rum/defc user-avatar [{:keys [name title uuid avatar-src class style fallback fallback-length fallback-props image-props] diff --git a/src/test/frontend/components/avatar_test.cljs b/src/test/frontend/components/avatar_test.cljs new file mode 100644 index 0000000000..19b0f166c8 --- /dev/null +++ b/src/test/frontend/components/avatar_test.cljs @@ -0,0 +1,13 @@ +(ns frontend.components.avatar-test + (:require [cljs.test :refer [deftest is testing]] + [frontend.components.avatar :as avatar])) + +(deftest initials-uses-two-letters-for-latin-names-without-spaces + (is (= "AL" (avatar/initials "alice")))) + +(deftest initials-uses-one-grapheme-for-non-latin-names-without-spaces + (testing "CJK names fit avatar fallback" + (is (= "会" (avatar/initials "会爬树的猫")))) + (testing "other non-Latin names do not use arbitrary first two characters" + (is (= "А" (avatar/initials "Алексей"))))) +