mirror of
https://github.com/penpot/penpot.git
synced 2026-05-31 12:48:09 +00:00
234 lines
7.6 KiB
Clojure
234 lines
7.6 KiB
Clojure
;; 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) KALEIDOS INC
|
|
|
|
(ns app.main.features
|
|
"A thin, frontend centric abstraction layer and collection of
|
|
helpers for `app.common.features` namespace."
|
|
(:require
|
|
[app.common.data.macros :as dm]
|
|
[app.common.features :as cfeat]
|
|
[app.common.logging :as log]
|
|
[app.config :as cf]
|
|
[app.main.router :as rt]
|
|
[app.main.store :as st]
|
|
[app.render-wasm :as wasm]
|
|
[clojure.set :as set]
|
|
[cuerdas.core :as str]
|
|
[okulary.core :as l]
|
|
[potok.v2.core :as ptk]
|
|
[rumext.v2 :as mf]))
|
|
|
|
(log/set-level! :trace)
|
|
|
|
(def global-enabled-features
|
|
(cfeat/get-enabled-features cf/flags))
|
|
|
|
(defn setup-wasm-features
|
|
[features state]
|
|
(let [params (rt/get-params state)
|
|
wasm (get params :wasm)
|
|
renderer (when (contains? cf/flags :render-switch)
|
|
(-> state :profile :props :renderer))
|
|
enable-wasm (or (= "true" wasm) (and (= renderer :wasm) (not= "false" wasm)))
|
|
disable-wasm (or (= "false" wasm) (and (= renderer :svg) (not= "true" wasm)))
|
|
features (cond-> features
|
|
enable-wasm (conj "render-wasm/v1")
|
|
disable-wasm (disj "render-wasm/v1"))]
|
|
;; If wasm render is enabled text-editor/v2 must be used
|
|
(cond-> features
|
|
(contains? features "render-wasm/v1")
|
|
(conj "text-editor/v2"))))
|
|
|
|
(defn get-enabled-features
|
|
"An explicit lookup of enabled features for the current team"
|
|
[state team-id]
|
|
(let [team (dm/get-in state [:teams team-id])]
|
|
(-> global-enabled-features
|
|
(set/union (get state :features-runtime #{}))
|
|
(set/intersection cfeat/no-migration-features)
|
|
(set/union (get team :features))
|
|
(setup-wasm-features state))))
|
|
|
|
(defn enabled-by-flags?
|
|
[{:keys [features-runtime features]} feature]
|
|
(or (contains? features-runtime feature)
|
|
(contains? features feature)))
|
|
|
|
(defn enabled-without-migration?
|
|
[{:keys [features-runtime features]} feature]
|
|
(or (contains? features-runtime feature)
|
|
(contains? global-enabled-features feature)
|
|
(contains? features feature)))
|
|
|
|
(defn wasm-url-override
|
|
[state]
|
|
(case (get (rt/get-params state) :wasm)
|
|
"true" true
|
|
"false" false
|
|
nil))
|
|
|
|
(def wasm-url-override-ref
|
|
(l/derived wasm-url-override st/state))
|
|
|
|
(defn- wasm-enabled?
|
|
[state]
|
|
(let [override (wasm-url-override state)
|
|
renderer (when (contains? cf/flags :render-switch)
|
|
(-> state :profile :props :renderer))]
|
|
(cond
|
|
(some? override)
|
|
override
|
|
|
|
(contains? cf/flags :render-switch)
|
|
(case renderer
|
|
:wasm true
|
|
:svg false
|
|
;; SVG renderer as default until profile data arrives OR if render-switch
|
|
;; flag is disabled.
|
|
false)
|
|
|
|
(contains? cfeat/no-migration-features "render-wasm/v1")
|
|
(enabled-without-migration? state "render-wasm/v1")
|
|
|
|
:else
|
|
(enabled-by-flags? state "render-wasm/v1"))))
|
|
|
|
(defn active-feature?
|
|
"Given a state and feature, check if feature is enabled."
|
|
[state feature]
|
|
(assert (contains? cfeat/supported-features feature)
|
|
"feature not supported")
|
|
|
|
(cond
|
|
(= feature "render-wasm/v1")
|
|
(wasm-enabled? state)
|
|
|
|
(contains? cfeat/no-migration-features feature)
|
|
(enabled-without-migration? state feature)
|
|
|
|
:else
|
|
(enabled-by-flags? state feature)))
|
|
|
|
(defn active-features?
|
|
"Given a state and a set of features, check if the features are all enabled."
|
|
([state a]
|
|
(js/console.warn "Please, use active-feature? instead")
|
|
(active-feature? state a))
|
|
([state a b]
|
|
(and ^boolean (active-feature? state a)
|
|
^boolean (active-feature? state b)))
|
|
([state a b c]
|
|
(and ^boolean (active-feature? state a)
|
|
^boolean (active-feature? state b)
|
|
^boolean (active-feature? state c)))
|
|
([state a b c & others]
|
|
(and ^boolean (active-feature? state a)
|
|
^boolean (active-feature? state b)
|
|
^boolean (active-feature? state c)
|
|
^boolean (every? #(active-feature? state %) others))))
|
|
|
|
(def ^:private features-ref
|
|
(l/derived (l/key :features) st/state))
|
|
|
|
(defn use-feature
|
|
"A react hook that checks if feature is currently enabled"
|
|
[feature]
|
|
(let [enabled-features (mf/deref features-ref)
|
|
wasm-override (mf/deref wasm-url-override-ref)
|
|
renderer (mf/deref (l/derived #(-> % :profile :props :renderer) st/state))
|
|
wasm-enabled (cond
|
|
(some? wasm-override)
|
|
wasm-override
|
|
|
|
(contains? cf/flags :render-switch)
|
|
(= renderer :wasm)
|
|
|
|
:else
|
|
(contains? enabled-features "render-wasm/v1"))]
|
|
(cond
|
|
(= feature "render-wasm/v1")
|
|
wasm-enabled
|
|
|
|
:else
|
|
(contains? enabled-features feature))))
|
|
|
|
(defn toggle-feature
|
|
"An event constructor for runtime feature toggle.
|
|
|
|
Warning: if a feature is active globally or by team, it can't be
|
|
disabled."
|
|
[feature]
|
|
(ptk/reify ::toggle-feature
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(assert (contains? cfeat/supported-features feature) "not supported feature")
|
|
(-> state
|
|
(update :features-runtime (fn [features]
|
|
(if (contains? features feature)
|
|
(do
|
|
(log/trc :hint "feature disabled" :feature feature)
|
|
(disj features feature))
|
|
(do
|
|
(log/trc :hint "feature enabled" :feature feature)
|
|
(conj features feature)))))
|
|
(update :features-runtime set/intersection cfeat/no-migration-features)))))
|
|
|
|
(defn enable-feature
|
|
[feature]
|
|
(ptk/reify ::enable-feature
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(assert (contains? cfeat/supported-features feature) "not supported feature")
|
|
(if (active-feature? state feature)
|
|
state
|
|
(do
|
|
(log/trc :hint "feature enabled" :feature feature)
|
|
(-> state
|
|
(update :features-runtime (fnil conj #{}) feature)
|
|
(update :features-runtime set/intersection cfeat/no-migration-features)))))))
|
|
|
|
(defn initialize
|
|
[features]
|
|
(ptk/reify ::initialize
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(let [features (-> global-enabled-features
|
|
(set/union (get state :features-runtime #{}))
|
|
(set/union features)
|
|
(setup-wasm-features state))]
|
|
(assoc state :features features)))
|
|
|
|
ptk/EffectEvent
|
|
(effect [_ state _]
|
|
(let [features (get state :features)]
|
|
(if (active-feature? state "render-wasm/v1")
|
|
(wasm/initialize true)
|
|
(wasm/initialize false))
|
|
|
|
(log/inf :hint "initialized"
|
|
:enabled (str/join " " features))))))
|
|
|
|
(defn recompute-features
|
|
[]
|
|
(ptk/reify ::recompute-features
|
|
ptk/UpdateEvent
|
|
(update [_ state]
|
|
(let [previous (or (get state :features) #{})
|
|
features (setup-wasm-features previous state)]
|
|
(if (= previous features)
|
|
state
|
|
(assoc state :features features))))
|
|
|
|
ptk/EffectEvent
|
|
(effect [_ state _]
|
|
(let [features (get state :features)]
|
|
(if (active-feature? state "render-wasm/v1")
|
|
(wasm/initialize true)
|
|
(wasm/initialize false))
|
|
|
|
(log/inf :hint "recomputed features"
|
|
:enabled (str/join " " features))))))
|