0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-24 07:29:08 -05:00

🐛 Fix many corner cases on custom font management.

This commit is contained in:
Andrey Antukh 2021-05-25 14:06:47 +02:00 committed by Alonso Torres
parent 6b1e5b4169
commit 43b34aa279
12 changed files with 562 additions and 324 deletions

View file

@ -1,20 +1,43 @@
CREATE TABLE team_font_variant (
id uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE,
profile_id uuid NULL REFERENCES profile(id) ON DELETE SET NULL,
team_id uuid NOT NULL REFERENCES team(id) ON DELETE CASCADE DEFERRABLE,
profile_id uuid NULL REFERENCES profile(id) ON DELETE SET NULL DEFERRABLE,
created_at timestamptz NOT NULL DEFAULT now(),
modified_at timestamptz NOT NULL DEFAULT now(),
deleted_at timestamptz NULL DEFAULT NULL,
font_id text NOT NULL,
font_id uuid NOT NULL,
font_family text NOT NULL,
font_weight smallint NOT NULL,
font_style text NOT NULL,
otf_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL,
ttf_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL,
woff1_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL,
woff2_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL
otf_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE,
ttf_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE,
woff1_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE,
woff2_file_id uuid NULL REFERENCES storage_object(id) ON DELETE SET NULL DEFERRABLE
);
CREATE INDEX team_font_variant_team_id_font_id_idx
ON team_font_variant (team_id, font_id);
CREATE INDEX team_font_variant_profile_id_idx
ON team_font_variant (profile_id);
CREATE INDEX team_font_variant_otf_file_id_idx
ON team_font_variant (otf_file_id);
CREATE INDEX team_font_variant_ttf_file_id_idx
ON team_font_variant (ttf_file_id);
CREATE INDEX team_font_variant_woff1_file_id_idx
ON team_font_variant (woff1_file_id);
CREATE INDEX team_font_variant_woff2_file_id_idx
ON team_font_variant (woff2_file_id);
ALTER TABLE team_font_variant
ALTER COLUMN font_family SET STORAGE external,
ALTER COLUMN font_style SET STORAGE external;

View file

@ -16,20 +16,20 @@
[app.util.services :as sv]
[app.util.time :as dt]
[app.worker :as wrk]
[clojure.spec.alpha :as s]
[cuerdas.core :as str]))
[clojure.spec.alpha :as s]))
(declare create-font-variant)
(def valid-weight #{100 200 300 400 500 600 700 800 900 950})
(def valid-style #{"normal" "italic"})
(s/def ::id ::us/uuid)
(s/def ::profile-id ::us/uuid)
(s/def ::team-id ::us/uuid)
(s/def ::name ::us/not-empty-string)
(s/def ::weight valid-weight)
(s/def ::style valid-style)
(s/def ::font-id (s/and ::us/string #(str/starts-with? % "custom-")))
(s/def ::font-id ::us/uuid)
(s/def ::content-type ::media/font-content-type)
(s/def ::data (s/map-of ::us/string any?))
@ -76,29 +76,57 @@
:otf-file-id (:id otf)
:ttf-file-id (:id ttf)})))
;; --- UPDATE FONT VARIANT
;; --- UPDATE FONT FAMILY
(s/def ::update-font-variant
(s/keys :req-un [::profile-id ::team-id ::id ::font-family ::font-id]))
(s/def ::update-font
(s/keys :req-un [::profile-id ::team-id ::id ::name]))
(sv/defmethod ::update-font-variant
[{:keys [pool] :as cfg} {:keys [id team-id profile-id font-family font-id] :as params}]
(def sql:update-font
"update team_font_variant
set font_family = ?
where team_id = ?
and font_id = ?")
(sv/defmethod ::update-font
[{:keys [pool] :as cfg} {:keys [team-id profile-id id name] :as params}]
(db/with-atomic [conn pool]
(teams/check-edition-permissions! conn profile-id team-id)
(db/update! conn :team-font-variant
{:font-family font-family
:font-id font-id}
{:id id
:team-id team-id})
(db/exec-one! conn [sql:update-font name team-id id])
nil))
;; --- DELETE FONT
(s/def ::delete-font
(s/keys :req-un [::profile-id ::team-id ::id]))
(sv/defmethod ::delete-font
[{:keys [pool] :as cfg} {:keys [id team-id profile-id] :as params}]
(db/with-atomic [conn pool]
(teams/check-edition-permissions! conn profile-id team-id)
(let [items (db/query conn :team-font-variant
{:font-id id :team-id team-id}
{:for-update true})]
(doseq [item items]
;; Schedule object deletion
(wrk/submit! {::wrk/task :delete-object
::wrk/delay cf/deletion-delay
::wrk/conn conn
:id (:id item)
:type :team-font-variant}))
(db/update! conn :team-font-variant
{:deleted-at (dt/now)}
{:font-id id :team-id team-id})
nil)))
;; --- DELETE FONT VARIANT
(s/def ::delete-font-variant
(s/keys :req-un [::profile-id ::team-id ::id]))
(sv/defmethod ::delete-font-variant
[{:keys [pool] :as cfg} {:keys [id team-id profile-id font-family font-id] :as params}]
[{:keys [pool] :as cfg} {:keys [id team-id profile-id] :as params}]
(db/with-atomic [conn pool]
(teams/check-edition-permissions! conn profile-id team-id)
@ -111,6 +139,5 @@
(db/update! conn :team-font-variant
{:deleted-at (dt/now)}
{:id id
:team-id team-id})
{:id id :team-id team-id})
nil))

View file

@ -22,6 +22,7 @@
(let [prof (th/create-profile* 1 {:is-active true})
team-id (:default-team-id prof)
proj-id (:default-project-id prof)
font-id (uuid/custom 10 1)
ttfdata (-> (io/resource "app/tests/_files/font-1.ttf")
(fs/slurp-bytes))
@ -29,7 +30,7 @@
params {::th/type :create-font-variant
:profile-id (:id prof)
:team-id team-id
:font-id "custom-somefont"
:font-id font-id
:font-family "somefont"
:font-weight 400
:font-style "normal"
@ -56,6 +57,7 @@
(let [prof (th/create-profile* 1 {:is-active true})
team-id (:default-team-id prof)
proj-id (:default-project-id prof)
font-id (uuid/custom 10 1)
data (-> (io/resource "app/tests/_files/font-1.woff")
(fs/slurp-bytes))
@ -63,7 +65,7 @@
params {::th/type :create-font-variant
:profile-id (:id prof)
:team-id team-id
:font-id "custom-somefont"
:font-id font-id
:font-family "somefont"
:font-weight 400
:font-style "normal"

View file

@ -70,15 +70,15 @@
[variant]
(cond
(re-seq #"(?i)(?:hairline|thin)" variant) 100
(re-seq #"(?i)(?:extra light|ultra light)" variant) 200
(re-seq #"(?i)(?:extra\s*light|ultra\s*light)" variant) 200
(re-seq #"(?i)(?:light)" variant) 300
(re-seq #"(?i)(?:normal|regular)" variant) 400
(re-seq #"(?i)(?:medium)" variant) 500
(re-seq #"(?i)(?:semi bold|demi bold)" variant) 600
(re-seq #"(?i)(?:semi\s*bold|demi\s*bold)" variant) 600
(re-seq #"(?i)(?:extra\s*bold|ultra\s*bold)" variant) 800
(re-seq #"(?i)(?:bold)" variant) 700
(re-seq #"(?i)(?:extra bold|ultra bold)" variant) 800
(re-seq #"(?i)(?:extra\s*black|ultra\s*black)" variant) 950
(re-seq #"(?i)(?:black|heavy)" variant) 900
(re-seq #"(?i)(?:extra black|ultra black)" variant) 950
:else 400))
(defn parse-font-style

View file

@ -30,8 +30,13 @@
align-items: center;
padding: 0px $big;
> div {
width: 30%;
> .family {
min-width: 200px;
width: 200px;
}
> .variants {
padding-left: 12px;
}
.search-input {
@ -50,20 +55,18 @@
}
}
.fonts-group {
margin-top: $big;
}
.font-item {
margin-top: $big;
color: $color-gray-40;
font-size: $fs14;
background-color: $color-white;
display: flex;
min-width: 1000px;
width: 100%;
height: 97px;
min-height: 97px;
align-items: center;
padding: $big;
justify-content: space-between;
&:not(:first-child) {
border-top: 1px solid $color-gray-10;
@ -77,14 +80,52 @@
font-size: $fs12;
}
> div {
width: 30%;
> .family {
min-width: 200px;
width: 200px;
}
.variant {
font-size: $fs14;
> .filenames {
min-width: 200px;
}
> .variants {
font-size: $fs14;
display: flex;
flex-wrap: wrap;
flex-grow: 1;
.variant {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
cursor: pointer;
.icon {
display: flex;
height: 16px;
width: 16px;
margin-left: 6px;
align-items: center;
svg {
fill: transparent;
width: 12px;
height: 12px;
transform: rotate(45deg);
}
}
&:hover {
.icon svg {
fill: $color-gray-30;
}
}
}
}
.filenames {
display: flex;
flex-direction: column;
@ -117,7 +158,6 @@
}
}
.dashboard-fonts-upload {
max-width: 1000px;
width: 100%;
@ -164,4 +204,31 @@
color: $color-gray-40;
}
}
.fonts-placeholder {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
max-width: 1000px;
width: 100%;
height: 161px;
border: 1px dashed $color-gray-20;
margin-top: 16px;
.icon {
svg {
fill: $color-gray-40;
width: 32px;
height: 32px;
}
}
.label {
color: $color-gray-40;
font-size: $fs14;
}
}
}

View file

@ -1,94 +0,0 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; Copyright (c) UXBOX Labs SL
(ns app.main.data.dashboard.fonts
(:require
[app.common.exceptions :as ex]
[app.common.data :as d]
[app.common.media :as cm]
[app.common.spec :as us]
[app.common.uuid :as uuid]
[app.main.repo :as rp]
[app.util.time :as dt]
[app.util.timers :as ts]
[app.main.data.messages :as dm]
[app.util.webapi :as wa]
[app.util.object :as obj]
[app.util.transit :as t]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[cuerdas.core :as str]
[potok.core :as ptk]))
(defn fetch-fonts
[{:keys [id] :as team}]
(ptk/reify ::fetch-fonts
ptk/WatchEvent
(watch [_ state stream]
(->> (rp/query! :team-font-variants {:team-id id})
(rx/map (fn [items]
#(assoc % :dashboard-fonts (d/index-by :id items))))))))
(defn add-font
[font]
(ptk/reify ::add-font
ptk/UpdateEvent
(update [_ state]
(update state :dashboard-fonts assoc (:id font) font))))
(defn update-font
[{:keys [id font-family] :as font}]
(ptk/reify ::update-font
ptk/UpdateEvent
(update [_ state]
(let [font (assoc font :font-id (str "custom-" (str/slug font-family)))]
(update state :dashboard-fonts assoc id font)))
ptk/WatchEvent
(watch [_ state stream]
(let [font (get-in state [:dashboard-fonts id])]
(->> (rp/mutation! :update-font-variant font)
(rx/ignore))))))
(defn delete-font
[{:keys [id] :as font}]
(ptk/reify ::delete-font
ptk/UpdateEvent
(update [_ state]
(update state :dashboard-fonts dissoc id))
ptk/WatchEvent
(watch [_ state stream]
(let [params (select-keys font [:id :team-id])]
(->> (rp/mutation! :delete-font-variant params)
(rx/ignore))))))
;; (defn upload-font
;; [{:keys [id] :as font}]
;; (ptk/reify ::upload-font
;; ptk/WatchEvent
;; (watch [_ state stream]
;; (let [{:keys [on-success on-error]
;; :or {on-success identity
;; on-error rx/throw}} (meta params)]
;; (->> (rp/mutation! :create-font-variant font)
;; (rx/tap on-success)
;; (rx/catch on-error))))))
;; (defn add-font
;; "Add fonts to the state in a pending to upload state."
;; [font]
;; (ptk/reify ::add-font
;; ptk/UpdateEvent
;; (update [_ state]
;; (let [id (uuid/next)
;; font (-> font
;; (assoc :created-at (dt/now))
;; (assoc :id id)
;; (assoc :status :draft))]
;; (js/console.log (clj->js font))
;; (assoc-in state [:dashboard-fonts id] font)))))

View file

@ -6,20 +6,46 @@
(ns app.main.data.fonts
(:require
["opentype.js" :as ot]
[app.common.data :as d]
[app.common.spec :as us]
[app.common.media :as cm]
[app.common.uuid :as uuid]
[app.main.fonts :as fonts]
[app.main.repo :as rp]
[app.util.i18n :as i18n :refer [tr]]
[app.util.logging :as log]
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[app.util.webapi :as wa]
[cuerdas.core :as str]
[potok.core :as ptk]))
(defn prepare-font-variant
[item]
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; General purpose events & IMPL
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn team-fonts-loaded
[fonts]
(letfn [;; Prepare font to the internal font database format.
(prepare-font [[id [item :as items]]]
{:id id
:name (:font-family item)
:family (:font-family item)
:variants (->> items
(map prepare-font-variant)
(sort-by variant-sort-fn)
(vec))})
(variant-sort-fn [item]
[(:weight item)
(if (= "normal" (:style item)) 1 2)])
(prepare-font-variant [item]
{:id (str (:font-style item) "-" (:font-weight item))
:name (str (cm/font-weight->name (:font-weight item)) " "
(str/capital (:font-style item)))
:name (str (cm/font-weight->name (:font-weight item))
(when (not= "normal" (:font-style item))
(str " " (str/capital (:font-style item)))))
:style (:font-style item)
:weight (str (:font-weight item))
::fonts/woff1-file-id (:woff1-file-id item)
@ -27,21 +53,21 @@
::fonts/ttf-file-id (:ttf-file-id item)
::fonts/otf-file-id (:otf-file-id item)})
(defn prepare-font
[[id [item :as items]]]
{:id id
:name (:font-family item)
:family (:font-family item)
:variants (mapv prepare-font-variant items)})
(adapt-font-id [variant]
(update variant :font-id #(str "custom-" %)))]
(defn team-fonts-loaded
[fonts]
(ptk/reify ::team-fonts-loaded
ptk/UpdateEvent
(update [_ state]
(assoc state :dashboard-fonts (d/index-by :id fonts)))
ptk/EffectEvent
(effect [_ state stream]
(let [fonts (->> (group-by :font-id fonts)
(let [fonts (->> fonts
(map adapt-font-id)
(group-by :font-id)
(mapv prepare-font))]
(fonts/register! :custom fonts)))))
(fonts/register! :custom fonts))))))
(defn load-team-fonts
[team-id]
@ -51,7 +77,168 @@
(->> (rp/query :team-font-variants {:team-id team-id})
(rx/map team-fonts-loaded)))))
(defn process-upload
"Given a seq of blobs and the team id, creates a ready-to-use fonts
map with temporal ID's associated to each font entry."
[blobs team-id]
(letfn [(prepare [{:keys [font type name data] :as params}]
(let [family (or (.getEnglishName ^js font "preferredFamily")
(.getEnglishName ^js font "fontFamily"))
variant (or (.getEnglishName ^js font "preferredSubfamily")
(.getEnglishName ^js font "fontSubfamily"))]
{:content {:data (js/Uint8Array. data)
:name name
:type type}
:font-family family
:font-weight (cm/parse-font-weight variant)
:font-style (cm/parse-font-style variant)}))
(defn get-fonts
[backend]
(get @fonts/fonts backend []))
(join [res {:keys [content] :as font}]
(let [key-fn (juxt :font-family :font-weight :font-style)
existing (d/seek #(= (key-fn font) (key-fn %)) (vals res))]
(if existing
(update res
(:id existing)
(fn [existing]
(-> existing
(update :data assoc (:type content) (:data content))
(update :names conj (:name content)))))
(let [tmp-id (uuid/next)]
(assoc res tmp-id
(-> font
(assoc :id tmp-id)
(assoc :team-id team-id)
(assoc :names #{(:name content)})
(assoc :data {(:type content)
(:data content)})
(dissoc :content)))))))
(parse-mtype [mtype]
(case mtype
"application/vnd.oasis.opendocument.formula-template" "font/otf"
mtype))
(parse-font [{:keys [data] :as params}]
(try
(assoc params :font (ot/parse data))
(catch :default e
(log/warn :msg (str/fmt "skiping file %s, unsupported format" (:name params)))
nil)))
(read-blob [blob]
(->> (wa/read-file-as-array-buffer blob)
(rx/map (fn [data]
{:data data
:name (.-name blob)
:type (parse-mtype (.-type blob))}))))]
(->> (rx/from blobs)
(rx/mapcat read-blob)
(rx/map parse-font)
(rx/filter some?)
(rx/map prepare)
(rx/reduce join {}))))
(defn- calculate-family-to-id-mapping
[existing]
(reduce #(assoc %1 (:font-family %2) (:font-id %2)) {} (vals existing)))
(defn merge-and-group-fonts
"Function responsible to merge (and apropriatelly group) incoming
fonts (processed by `process-upload`) into existing fonts
in local state, preserving correct font-id references."
[current-fonts installed-fonts incoming-fonts]
(loop [famdb (-> (merge current-fonts installed-fonts)
(calculate-family-to-id-mapping))
items (vals incoming-fonts)
result current-fonts]
(if-let [{:keys [id font-family] :as item} (first items)]
(let [font-id (or (get famdb font-family)
(uuid/next))
font (assoc item :font-id font-id)]
(recur (assoc famdb font-family font-id)
(rest items)
(assoc result id font)))
result)))
(defn rename-and-regroup
"Function responsible to rename a font in a local state and properly
regroup it to the apropriate `font-id` having in account current
fonts and installed fonts."
[current-fonts id name installed-fonts]
(let [famdb (-> (merge current-fonts installed-fonts)
(calculate-family-to-id-mapping))
font-id (or (get famdb name)
(uuid/next))]
(update current-fonts id (fn [font]
(-> font
(assoc :name name)
(assoc :font-id font-id))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Dashboard related events
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn add-font
[font]
(ptk/reify ::add-font
ptk/UpdateEvent
(update [_ state]
(update state :dashboard-fonts assoc (:id font) font))))
(defn update-font
[{:keys [id name] :as params}]
(us/assert ::us/uuid id)
(us/assert ::us/not-empty-string name)
(ptk/reify ::update-font
ptk/UpdateEvent
(update [_ state]
;; Update all variants that has the same font-id with the new
;; name in the local state.
(update state :dashboard-fonts
(fn [fonts]
(d/mapm (fn [_ font]
(cond-> font
(= id (:font-id font))
(assoc :font-family name)))
fonts))))
ptk/WatchEvent
(watch [_ state stream]
(let [team-id (:current-team-id state)]
(->> (rp/mutation! :update-font {:id id :name name :team-id team-id})
(rx/ignore))))))
(defn delete-font
"Delete all variants related to the provided `font-id`."
[font-id]
(us/assert ::us/uuid font-id)
(ptk/reify ::delete-font
ptk/UpdateEvent
(update [_ state]
(update state :dashboard-fonts
(fn [variants]
(d/removem (fn [[id variant]]
(= (:font-id variant) font-id)) variants))))
ptk/WatchEvent
(watch [_ state stream]
(let [team-id (:current-team-id state)]
(->> (rp/mutation! :delete-font {:id font-id :team-id team-id})
(rx/ignore))))))
(defn delete-font-variant
[id]
(us/assert ::us/uuid id)
(ptk/reify ::delete-font-variants
ptk/UpdateEvent
(update [_ state]
(update state :dashboard-fonts
(fn [variants]
(d/removem (fn [[_ variant]]
(= (:id variant) id))
variants))))
ptk/WatchEvent
(watch [_ state stream]
(let [team-id (:current-team-id state)]
(->> (rp/mutation! :delete-font-variant {:id id :team-id team-id})
(rx/ignore))))))

View file

@ -54,10 +54,6 @@
(vec)
(reset! fonts))))
(defn- remove-fonts
[db backend]
(reduce-kv #(cond-> %1 (= backend (:backend %3)) (dissoc %2)) db db))
(defn register!
[backend fonts]
(swap! fontsdb
@ -94,7 +90,6 @@
(unchecked-set node "type" "text/css")
node))
(defn- create-style-element
[css]
(let [node (.createElement js/document "style")]
@ -164,9 +159,9 @@
url(%(otf-uri)s) format('otf');
}")
(defn- font-id->uri
[font-id]
(str (u/join cf/public-uri "assets/by-id/" font-id)))
(defn- asset-id->uri
[asset-id]
(str (u/join cf/public-uri "assets/by-id/" asset-id)))
(defn generate-custom-font-variant-css
[family variant]
@ -174,10 +169,10 @@
{:family family
:style (:style variant)
:weight (:weight variant)
:woff2-uri (font-id->uri (::woff2-file-id variant))
:woff1-uri (font-id->uri (::woff1-file-id variant))
:ttf-uri (font-id->uri (::ttf-file-id variant))
:otf-uri (font-id->uri (::otf-file-id variant))}))
:woff2-uri (asset-id->uri (::woff2-file-id variant))
:woff1-uri (asset-id->uri (::woff1-file-id variant))
:ttf-uri (asset-id->uri (::ttf-file-id variant))
:otf-uri (asset-id->uri (::otf-file-id variant))}))
(defn- generate-custom-font-css
[{:keys [family variants] :as font}]
@ -188,7 +183,7 @@
(defmethod load-font :custom
[{:keys [id family variants ::on-loaded] :as font}]
(when (exists? js/window)
(js/console.log "[debug:fonts]: loading google font" id)
(js/console.log "[debug:fonts]: loading custom font" id)
(let [css (generate-custom-font-css font)]
(add-font-css! css))))

View file

@ -6,11 +6,11 @@
(ns app.main.ui.dashboard.fonts
(:require
["opentype.js" :as ot]
[app.common.data :as d]
[app.common.media :as cm]
[app.common.uuid :as uuid]
[app.main.data.dashboard :as dd]
[app.main.data.dashboard.fonts :as df]
[app.main.data.fonts :as df]
[app.main.data.modal :as modal]
[app.main.ui.components.file-uploader :refer [file-uploader]]
[app.main.ui.components.context-menu :refer [context-menu]]
@ -71,48 +71,15 @@
[:div]]))
(defn- prepare-fonts
[blobs]
(letfn [(prepare [{:keys [font type name data] :as params}]
(let [family (or (.getEnglishName ^js font "preferredFamily")
(.getEnglishName ^js font "fontFamily"))
variant (or (.getEnglishName ^js font "preferredSubfamily")
(.getEnglishName ^js font "fontSubfamily"))]
{:content {:data (js/Uint8Array. data)
:name name
:type type}
:font-id (str "custom-" (str/slug family))
:font-family family
:font-weight (cm/parse-font-weight variant)
:font-style (cm/parse-font-style variant)}))
(parse-mtype [mtype]
(case mtype
"application/vnd.oasis.opendocument.formula-template" "font/otf"
mtype))
(parse-font [{:keys [data] :as params}]
(try
(assoc params :font (ot/parse data))
(catch :default e
(log/warn :msg (str/fmt "skiping file %s, unsupported format" (:name params)))
nil)))
(read-blob [blob]
(->> (wa/read-file-as-array-buffer blob)
(rx/map (fn [data]
{:data data
:name (.-name blob)
:type (parse-mtype (.-type blob))}))))]
(->> (rx/from blobs)
(rx/mapcat read-blob)
(rx/map parse-font)
(rx/filter some?)
(rx/map prepare))))
(mf/defc font-variant-display-name
[{:keys [variant]}]
[:*
[:span (cm/font-weight->name (:font-weight variant))]
(when (not= "normal" (:font-style variant))
[:span " " (str/capital (:font-style variant))])])
(mf/defc fonts-upload
[{:keys [team] :as props}]
[{:keys [team installed-fonts] :as props}]
(let [fonts (mf/use-state {})
input-ref (mf/use-ref)
@ -126,19 +93,11 @@
on-selected
(mf/use-callback
(mf/deps team)
(mf/deps team installed-fonts)
(fn [blobs]
(->> (prepare-fonts blobs)
(rx/subs (fn [{:keys [content] :as font}]
(let [key (font-key-fn font)]
(swap! fonts update key
(fn [val]
(-> (or val font)
(assoc :team-id (:id team))
(update :id #(or % (uuid/next)))
(update :data assoc (:type content) (:data content))
(update :names (fnil conj #{}) (:name content))
(dissoc :content))))))
(->> (df/process-upload blobs (:id team))
(rx/subs (fn [result]
(swap! fonts df/merge-and-group-fonts installed-fonts result))
(fn [error]
(js/console.error "error" error))))))
@ -146,22 +105,26 @@
(mf/use-callback
(mf/deps team)
(fn [item]
(let [key (font-key-fn item)]
(swap! uploading conj (:id item))
(->> (rp/mutation! :create-font-variant item)
(rx/delay-at-least 2000)
(rx/subs (fn [font]
(swap! fonts dissoc key)
(swap! fonts dissoc (:id item))
(swap! uploading disj (:id item))
(st/emit! (df/add-font font)))
(fn [error]
(js/console.log "error" error)))))))
(js/console.log "error" error))))))
on-blur-name
(fn [id event]
(let [name (dom/get-target-val event)]
(swap! fonts df/rename-and-regroup id name installed-fonts)))
on-delete
(mf/use-callback
(mf/deps team)
(fn [item]
(swap! fonts dissoc (font-key-fn item))))]
(fn [{:keys [id] :as item}]
(swap! fonts dissoc id)))]
[:div.dashboard-fonts-upload
[:div.dashboard-fonts-hero
@ -177,7 +140,7 @@
[:div.btn-primary
{:on-click on-click}
[:span "Add custom font"]
[:span (tr "labels.add-custom-font")]
[:& file-uploader {:input-id "font-upload"
:accept cm/str-font-types
:multi true
@ -190,11 +153,11 @@
[:div.font-item.table-row {:key (:id item)}
[:div.table-field.family
[:input {:type "text"
:on-blur #(on-blur-name (:id item) %)
:default-value (:font-family item)}]]
[:div.table-field.variant
[:span (cm/font-weight->name (:font-weight item))]
(when (not= "normal" (:font-style item))
[:span " " (str/capital (:font-style item))])]
[:div.table-field.variants
[:span.label
[:& font-variant-display-name {:variant item}]]]
[:div.table-field.filenames
(for [item (:names item)]
[:span item])]
@ -210,56 +173,67 @@
[:span.icon.close {:on-click #(on-delete item)} i/close]]]))]]))
(mf/defc installed-font
[{:keys [font] :as props}]
(let [open-menu? (mf/use-state false)
[{:keys [font-id variants] :as props}]
(let [font (first variants)
variants (sort-by (fn [item]
[(:font-weight item)
(if (= "normal" (:font-style item)) 1 2)])
variants)
open-menu? (mf/use-state false)
edit? (mf/use-state false)
state (mf/use-var (:font-family font))
on-change
(mf/use-callback
(mf/deps font)
(fn [event]
(reset! state (dom/get-target-val event))))
(reset! state (dom/get-target-val event)))
on-save
(mf/use-callback
(mf/deps font)
(fn [event]
(let [font (assoc font :font-family @state)]
(st/emit! (df/update-font font))
(reset! edit? false))))
(let [font-family @state]
(st/emit! (df/update-font
{:id font-id
:name font-family}))
(reset! edit? false)))
on-key-down
(mf/use-callback
(mf/deps font)
(fn [event]
(when (kbd/enter? event)
(on-save event))))
(on-save event)))
on-cancel
(mf/use-callback
(mf/deps font)
(fn [event]
(reset! edit? false)
(reset! state (:font-family font))))
(reset! state (:font-family font)))
delete-fn
(mf/use-callback
(mf/deps font)
(st/emitf (df/delete-font font)))
delete-font-fn
(fn [] (st/emit! (df/delete-font font-id)))
delete-variant-fn
(fn [id] (st/emit! (df/delete-font-variant id)))
on-delete
(mf/use-callback
(mf/deps font)
(st/emitf (modal/show
(fn []
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-font.title")
:message (tr "modals.delete-font.message")
:accept-label (tr "labels.delete")
:on-accept delete-fn})))]
:on-accept (fn [props]
(delete-font-fn))})))
on-delete-variant
(fn [id]
(st/emit! (modal/show
{:type :confirm
:title (tr "modals.delete-font-variant.title")
:message (tr "modals.delete-font-variant.message")
:accept-label (tr "labels.delete")
:on-accept (fn [props]
(delete-variant-fn id))})))]
[:div.font-item.table-row {:key (:id font)}
[:div.font-item.table-row
[:div.table-field.family
(if @edit?
[:input {:type "text"
@ -268,10 +242,14 @@
:on-change on-change}]
[:span (:font-family font)])]
[:div.table-field.variant
[:span (cm/font-weight->name (:font-weight font))]
(when (not= "normal" (:font-style font))
[:span " " (str/capital (:font-style font))])]
[:div.table-field.variants
(for [item variants]
[:div.variant
[:span.label
[:& font-variant-display-name {:variant item}]]
[:span.icon.close
{:on-click #(on-delete-variant (:id item))}
i/plus]])]
[:div]
@ -281,7 +259,7 @@
{:disabled (str/blank? @state)
:on-click on-save
:class (dom/classnames :btn-disabled (str/blank? @state))}
"Save"]
(tr "labels.save")]
[:span.icon.close {:on-click on-cancel} i/close]]
[:div.table-field.options
@ -313,41 +291,45 @@
[:h3 (tr "labels.installed-fonts")]
[:div.installed-fonts-header
[:div.table-field.family (tr "labels.font-family")]
[:div.table-field.variant (tr "labels.font-variant")]
[:div.table-field.variants (tr "labels.font-variants")]
[:div]
[:div.table-field.search-input
[:input {:placeholder (tr "labels.search-font")
:default-value ""
:on-change on-change
}]]]
(for [[font-id fonts] (->> fonts
(cond
(seq fonts)
(for [[font-id variants] (->> (vals fonts)
(filter matches?)
(group-by :font-id))]
[:div.fonts-group
(for [font (sort-by (juxt :font-weight :font-style) fonts)]
[:& installed-font {:key (:id font) :font font}])])]))
[:& installed-font {:key (str font-id)
:font-id font-id
:variants variants}])
(nil? fonts)
[:div.fonts-placeholder
[:div.icon i/loader]
[:div.label (tr "dashboard.loading-fonts")]]
:else
[:div.fonts-placeholder
[:div.icon i/text]
[:div.label (tr "dashboard.fonts.empty-placeholder")]])]))
(mf/defc fonts-page
[{:keys [team] :as props}]
(let [fonts-map (mf/deref refs/dashboard-fonts)
fonts (vals fonts-map)]
(mf/use-effect
(mf/deps team)
(st/emitf (df/fetch-fonts team)))
(let [fonts (mf/deref refs/dashboard-fonts)]
[:*
[:& header {:team team :section :fonts}]
[:section.dashboard-container.dashboard-fonts
[:& fonts-upload {:team team}]
[:& fonts-upload {:team team :installed-fonts fonts}]
[:& installed-fonts {:team team :fonts fonts}]]]))
(when fonts
[:& installed-fonts {:team team
:fonts fonts}])]]))
(mf/defc font-providers-page
[{:keys [team] :as props}]
[:*
[:& header {:team team :section :providers}]
[:section.dashboard-container
[:span "hello world font providers"]]])
[:span "font providers"]]])

View file

@ -310,7 +310,16 @@
[:div.row-flex
[:div.input-select.font-option
{:on-click #(reset! open-selector? true)}
(:name font)]]
(cond
(= :multiple font-id)
"--"
(some? font)
(:name font)
:else
(tr "dashboard.fonts.deleted-placeholder"))]]
[:div.row-flex
(let [size-options [8 9 10 11 12 14 18 24 36 48 72]

View file

@ -206,6 +206,12 @@ msgstr "Duplicate %s files"
msgid "dashboard.empty-files"
msgstr "You still have no files here"
msgid "dashboard.fonts.deleted-placeholder"
msgstr "Font deleted"
msgid "dashboard.fonts.empty-placeholder"
msgstr "You still have no custom fonts installed."
#, markdown
msgid "dashboard.fonts.hero-text1"
msgstr ""
@ -239,6 +245,9 @@ msgstr "Shared Libraries"
msgid "dashboard.loading-files"
msgstr "loading your files …"
msgid "dashboard.loading-fonts"
msgstr "loading your fonts …"
#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "dashboard.move-to"
msgstr "Move to"
@ -790,6 +799,9 @@ msgstr "You are seeing version %s"
msgid "labels.accept"
msgstr "Accept"
msgid "labels.add-custom-font"
msgstr "Add custom font"
#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs
msgid "labels.admin"
msgstr "Admin"
@ -891,8 +903,8 @@ msgstr "Font Family"
msgid "labels.font-providers"
msgstr "Font providers"
msgid "labels.font-variant"
msgstr "Style"
msgid "labels.font-variants"
msgstr "Styles"
msgid "labels.fonts"
msgstr "Fonts"
@ -1189,6 +1201,14 @@ msgstr "Are you sure you want to delete %s files?"
msgid "modals.delete-file-multi-confirm.title"
msgstr "Deleting %s files"
msgid "modals.delete-font-variant.message"
msgstr ""
"Are you sure you want to delete this font style? It will not load if is "
"used in a file."
msgid "modals.delete-font-variant.title"
msgstr "Deleting font style"
msgid "modals.delete-font.message"
msgstr ""
"Are you sure you want to delete this font? It will not load if is used in a "

View file

@ -210,6 +210,12 @@ msgstr "Duplicar %s archivos"
msgid "dashboard.empty-files"
msgstr "Todavía no hay ningún archivo aquí"
msgid "dashboard.fonts.deleted-placeholder"
msgstr "Fuente eliminada."
msgid "dashboard.fonts.empty-placeholder"
msgstr "Aun no tienes fuentes personalizadas."
#, markdown
msgid "dashboard.fonts.hero-text1"
msgstr ""
@ -243,6 +249,9 @@ msgstr "Bibliotecas Compartidas"
msgid "dashboard.loading-files"
msgstr "cargando tus ficheros …"
msgid "dashboard.loading-fonts"
msgstr "cargando tus fuentes …"
#: src/app/main/ui/dashboard/project_menu.cljs, src/app/main/ui/dashboard/file_menu.cljs
msgid "dashboard.move-to"
msgstr "Mover a"
@ -796,6 +805,9 @@ msgstr "Estás viendo la versión %s"
msgid "labels.accept"
msgstr "Aceptar"
msgid "labels.add-custom-font"
msgstr "Añadir fuentes personalizada"
#: src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs, src/app/main/ui/dashboard/team.cljs
msgid "labels.admin"
msgstr "Administración"
@ -893,8 +905,8 @@ msgstr "Familia de fuente"
msgid "labels.font-providers"
msgstr "Proveedores de fuentes"
msgid "labels.font-variant"
msgstr "Estilo"
msgid "labels.font-variants"
msgstr "Estilos"
msgid "labels.fonts"
msgstr "Fuentes"
@ -1191,10 +1203,18 @@ msgstr "¿Seguro que quieres eliminar %s archivos?"
msgid "modals.delete-file-multi-confirm.title"
msgstr "Eliminando %s archivos"
msgid "modals.delete-font-variant.message"
msgstr ""
"Estas seguro de querer eliminar esta estilo de fuente? Dejara de cargar si "
"es usada en algun fichero."
msgid "modals.delete-font-variant.title"
msgstr "Eliminando estilo de fuente"
msgid "modals.delete-font.message"
msgstr ""
"Are you sure you want to delete this font? It will not load if is used in a "
"file."
"Estas seguro de querer eliminar esta fuente? Dejara de cargar si es usada "
"en algun fichero."
msgid "modals.delete-font.title"
msgstr "Eliminando fuente"