refactor: reformat all clojure code with zprint (#14589)

Co-authored-by: refactor-only <auto@status.im>
This commit is contained in:
yqrashawn 2022-12-20 22:45:37 +08:00 committed by GitHub
parent 39e29cfb5a
commit 0a8993bbf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
804 changed files with 37007 additions and 28310 deletions

View File

@ -1,22 +1,25 @@
(ns i18n.i18n
(:require
["i18n-js" :as i18n]
[clojure.string :as string]
[status-im.goog.i18n :as goog.i18n]))
(:require ["i18n-js" :as i18n]
[clojure.string :as string]
[status-im.goog.i18n :as goog.i18n]))
(defn init [default-device-language translations-by-locale]
(defn init
[default-device-language translations-by-locale]
(set! (.-fallbacks i18n) true)
(set! (.-defaultSeparator i18n) "/")
(set! (.-locale i18n) default-device-language)
(set! (.-translations i18n) translations-by-locale))
(defn get-translations []
(defn get-translations
[]
(.-translations i18n))
(defn set-language [lang]
(defn set-language
[lang]
(set! (.-locale i18n) lang))
;;:zh, :zh-hans-xx, :zh-hant-xx have been added until this bug will be fixed https://github.com/fnando/i18n-js/issues/460
;;:zh, :zh-hans-xx, :zh-hant-xx have been added until this bug will be fixed
;;https://github.com/fnando/i18n-js/issues/460
(def delimeters
"This function is a hack: mobile Safari doesn't support toLocaleString(), so we need to pass
@ -29,7 +32,8 @@
{:delimiter ""
:separator (subs n 4 5)})))
(defn label-number [number]
(defn label-number
[number]
(when number
(let [{:keys [delimiter separator]} delimeters]
(.toNumber i18n
@ -41,7 +45,8 @@
(def default-option-value "<no value>")
(defn label-options [options]
(defn label-options
[options]
;; i18n ignores nil value, leading to misleading messages
(into {} (for [[k v] options] [k (or v default-option-value)])))
@ -55,7 +60,8 @@
(def label (memoize label-fn))
(defn label-pluralize [count path & options]
(defn label-pluralize
[count path & options]
(if (exists? (.t i18n))
(.p i18n count (name path) (clj->js options))
(name path)))

View File

@ -4,11 +4,13 @@
(:require [status-im.utils.test :as utils.test])
(:require [status-im.chat.default-chats :refer (default-chats)]))
;; to generate a js Proxy at js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ that accept any (.xxx) call and return itself
;; to generate a js Proxy at js/__STATUS_MOBILE_JS_IDENTITY_PROXY__ that accept any (.xxx) call and
;; return itself
;; For the convenience to mock eg.
;; (-> reanimated/slide-out-up-animation .springify (.damping 20) (.stiffness 300))
;; (-> reanimated/slide-out-up-animation (.damping 20) .springify (.stiffness 300))
(js/eval "
(js/eval
"
var globalThis
if (typeof window === \"undefined\") {
globalThis = global
@ -18,24 +20,24 @@ if (typeof window === \"undefined\") {
globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return () => globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__}})
")
(def action-button #js {:default #js {:Item #js {}}})
(def config #js {:default #js {}})
(def camera #js {:RNCamera #js {:Constants #js {}}})
(def dialogs #js {})
(def dismiss-keyboard #js {})
(def emoji-picker #js {:default #js {}})
(def fs #js {})
(def i18n #js {:locale "en"})
(def image-crop-picker #js {})
(def image-resizer #js {})
(def qr-code #js {})
(def svg #js {})
(def action-button #js {:default #js {:Item #js {}}})
(def config #js {:default #js {}})
(def camera #js {:RNCamera #js {:Constants #js {}}})
(def dialogs #js {})
(def dismiss-keyboard #js {})
(def emoji-picker #js {:default #js {}})
(def fs #js {})
(def i18n #js {:locale "en"})
(def image-crop-picker #js {})
(def image-resizer #js {})
(def qr-code #js {})
(def svg #js {})
(def react-native
(clj->js {:NativeModules {:RNGestureHandlerModule {:Direction (fn [])}
:PushNotifications {}
:Status utils.test/status
:ReanimatedModule {:configureProps (fn [])}}
(clj->js {:NativeModules {:RNGestureHandlerModule {:Direction (fn [])}
:PushNotifications {}
:Status utils.test/status
:ReanimatedModule {:configureProps (fn [])}}
:View {}
:RefreshControl {}
@ -78,9 +80,10 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
:Platform {:select (fn [])}
:I18nManager {:isRTL ""}
:NativeEventEmitter (fn [])
:LayoutAnimation {:Presets #js {:easeInEaseOut nil
:linear nil
:spring nil}
:LayoutAnimation {:Presets #js
{:easeInEaseOut nil
:linear nil
:spring nil}
:Types #js {}
:Properties #{}
:create (fn [])
@ -91,168 +94,199 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
(set! js/ReactNative react-native)
(def reanimated-bottom-sheet #js {:default #js {}})
(def reanimated-bottom-sheet #js {:default #js {}})
(def icons #js {:default #js {}})
(def webview #js {:WebView #js {}})
(def status-keycard #js {:default #js {:nfcIsSupported (fn [] #js {:then identity})
:nfcIsEnabled (fn [] #js {:then identity})}})
(def icons #js {:default #js {}})
(def webview #js {:WebView #js {}})
(def status-keycard
#js
{:default #js
{:nfcIsSupported (fn [] #js {:then identity})
:nfcIsEnabled (fn [] #js {:then identity})}})
(def snoopy #js {:default #js {}})
(def snoopy-filter #js {:default #js {}})
(def snoopy-bars #js {:default #js {}})
(def snoopy-buffer #js {:default #js {}})
(def fetch #js {})
(def snoopy #js {:default #js {}})
(def snoopy-filter #js {:default #js {}})
(def snoopy-bars #js {:default #js {}})
(def snoopy-buffer #js {:default #js {}})
(def fetch #js {})
(def async-storage-atom (atom {}))
(def async-storage #js {:default #js {:setItem #(js/Promise.resolve)
:multiGet #(js/Promise.resolve)
:getItem #(js/Promise.resolve)}})
(def async-storage
#js
{:default #js
{:setItem #(js/Promise.resolve)
:multiGet #(js/Promise.resolve)
:getItem #(js/Promise.resolve)}})
(def background-timer (clj->js {:default {:setTimeout js/setTimeout
:setInterval js/setInterval
:clearTimeout js/clearTimeout
:clearInterval js/clearInterval}}))
(def background-timer
(clj->js {:default {:setTimeout js/setTimeout
:setInterval js/setInterval
:clearTimeout js/clearTimeout
:clearInterval js/clearInterval}}))
(def keychain #js {:setGenericPassword (constantly (.resolve js/Promise true))
:setInternetCredentials #(js/Promise.resolve)
:resetInternetCredentials #(js/Promise.resolve)
"ACCESSIBLE" {}
"ACCESS_CONTROL" {}})
(def keychain
#js
{:setGenericPassword (constantly (.resolve js/Promise true))
:setInternetCredentials #(js/Promise.resolve)
:resetInternetCredentials #(js/Promise.resolve)
"ACCESSIBLE" {}
"ACCESS_CONTROL" {}})
(def react-native-mail #js {:mail #js {}})
(def react-native-screens #js {})
(def react-native-shake #js {})
(def react-native-screens #js {})
(def react-native-shake #js {})
(def react-native-share #js {:default {}})
(def react-native-svg #js {:SvgUri #js {:render identity}
:SvgXml #js {:render identity}
:default #js {:render identity}
:Path #js {:render identity}})
(def react-native-svg
#js
{:SvgUri #js {:render identity}
:SvgXml #js {:render identity}
:default #js {:render identity}
:Path #js {:render identity}})
(def react-native-webview #js {:default {}})
(def react-native-audio-toolkit #js {:MediaStates {}})
(def net-info #js {})
(def touchid #js {})
(def net-info #js {})
(def touchid #js {})
(def react-native-image-viewing #js {:default {}})
(def safe-area-context (clj->js {:SafeAreaProvider {:_reactNativeIphoneXHelper {:getStatusBarHeight (fn [])}}
:SafeAreaInsetsContext {:Consumer (fn [])}
:SafeAreaView {}}))
(def safe-area-context
(clj->js {:SafeAreaProvider {:_reactNativeIphoneXHelper {:getStatusBarHeight (fn [])}}
:SafeAreaInsetsContext {:Consumer (fn [])}
:SafeAreaView {}}))
(def back-handler #js {:addEventListener identity
:removeEventListener identity})
(def react #js {:useCallback nil
:useEffect nil
:useRef nil
:createRef nil
:Fragment identity})
(def react-navigation-native #js {:NavigationContainer #js {}
:useFocusEffect identity
:CommonActions #js {}
:StackActions #js {}})
(def back-handler
#js
{:addEventListener identity
:removeEventListener identity})
(def react
#js
{:useCallback nil
:useEffect nil
:useRef nil
:createRef nil
:Fragment identity})
(def react-navigation-native
#js
{:NavigationContainer #js {}
:useFocusEffect identity
:CommonActions #js {}
:StackActions #js {}})
(def react-native-navigation #js {:Navigation #js {:constants (fn [] #js {:then identity})
:setDefaultOptions identity
:setRoot identity
:dismissOverlay #(js/Promise.resolve)
:showOverlay identity
:setLazyComponentRegistrator identity
:pop identity
:push identity
:mergeOptions identity
:popToRoot identity
:showModal identity
:dismissModal identity
:registerComponent identity
:events
(fn []
#js {:registerModalDismissedListener identity
:registerAppLaunchedListener identity
:registerBottomTabSelectedListener identity
:registerComponentDidDisappearListener identity
:registerComponentDidAppearListener identity
:registerNavigationButtonPressedListener identity})}})
(def react-native-navigation
#js
{:Navigation #js
{:constants (fn [] #js {:then identity})
:setDefaultOptions identity
:setRoot identity
:dismissOverlay #(js/Promise.resolve)
:showOverlay identity
:setLazyComponentRegistrator identity
:pop identity
:push identity
:mergeOptions identity
:popToRoot identity
:showModal identity
:dismissModal identity
:registerComponent identity
:events
(fn []
#js
{:registerModalDismissedListener identity
:registerAppLaunchedListener identity
:registerBottomTabSelectedListener identity
:registerComponentDidDisappearListener identity
:registerComponentDidAppearListener identity
:registerNavigationButtonPressedListener identity})}})
(def react-navigation-stack #js {:createStackNavigator identity
:TransitionPresets #js {:ModalPresentationIOS #js {}}})
(def react-navigation-stack
#js
{:createStackNavigator identity
:TransitionPresets #js {:ModalPresentationIOS #js {}}})
(def react-navigation-bottom-tabs #js {:createBottomTabNavigator identity})
(def react-native-haptic-feedback #js {:default #js {:trigger nil}})
(def react-native-reanimated #js {:default #js {:createAnimatedComponent identity
:eq nil
:greaterOrEq nil
:greaterThan nil
:lessThan nil
:lessOrEq nil
:add nil
:diff nil
:divide nil
:sub nil
:multiply nil
:abs nil
:min nil
:max nil
:neq nil
:and nil
:or nil
:not nil
:set nil
:startClock nil
:stopClock nil
:Value nil
:Clock nil
:debug nil
:log nil
:event nil
:cond nil
:block nil
:interpolateNode nil
:call nil
:timing nil
:onChange nil
:View #js {}
:Image #js {}
:ScrollView #js {}
:Text #js {}
:Extrapolate #js {:CLAMP nil}
:Code #js {}}
:EasingNode #js {:bezier identity
:linear identity}
:clockRunning nil
:useSharedValue (fn [])
:useAnimatedStyle (fn [])
:withTiming (fn [])
:withDelay (fn [])
:Easing #js {:bezier identity}
:Keyframe (fn [])
:SlideOutUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__
:SlideInUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__
:LinearTransition js/__STATUS_MOBILE_JS_IDENTITY_PROXY__})
(def react-native-gesture-handler #js {:default #js {}
:State #js {:BEGAN nil
:ACTIVE nil
:CANCELLED nil
:END nil
:FAILED nil
:UNDETERMINED nil}
:PureNativeButton #js {}
:TapGestureHandler #js {}
:PanGestureHandler #js {}
:TouchableHighlight #js {}
:LongPressGestureHandler #js {}
:TouchableWithoutFeedback #js {}
:NativeViewGestureHandler #js {}
:FlatList #js {}
:ScrollView #js {}
:TouchableOpacity #js {}
:GestureDetector #js {}
:Gesture #js {:Pan nil}
:createNativeWrapper identity})
(def react-native-reanimated
#js
{:default #js
{:createAnimatedComponent identity
:eq nil
:greaterOrEq nil
:greaterThan nil
:lessThan nil
:lessOrEq nil
:add nil
:diff nil
:divide nil
:sub nil
:multiply nil
:abs nil
:min nil
:max nil
:neq nil
:and nil
:or nil
:not nil
:set nil
:startClock nil
:stopClock nil
:Value nil
:Clock nil
:debug nil
:log nil
:event nil
:cond nil
:block nil
:interpolateNode nil
:call nil
:timing nil
:onChange nil
:View #js {}
:Image #js {}
:ScrollView #js {}
:Text #js {}
:Extrapolate #js {:CLAMP nil}
:Code #js {}}
:EasingNode #js
{:bezier identity
:linear identity}
:clockRunning nil
:useSharedValue (fn [])
:useAnimatedStyle (fn [])
:withTiming (fn [])
:withDelay (fn [])
:Easing #js {:bezier identity}
:Keyframe (fn [])
:SlideOutUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__
:SlideInUp js/__STATUS_MOBILE_JS_IDENTITY_PROXY__
:LinearTransition js/__STATUS_MOBILE_JS_IDENTITY_PROXY__})
(def react-native-gesture-handler
#js
{:default #js {}
:State #js
{:BEGAN nil
:ACTIVE nil
:CANCELLED nil
:END nil
:FAILED nil
:UNDETERMINED nil}
:PureNativeButton #js {}
:TapGestureHandler #js {}
:PanGestureHandler #js {}
:TouchableHighlight #js {}
:LongPressGestureHandler #js {}
:TouchableWithoutFeedback #js {}
:NativeViewGestureHandler #js {}
:FlatList #js {}
:ScrollView #js {}
:TouchableOpacity #js {}
:GestureDetector #js {}
:Gesture #js {:Pan nil}
:createNativeWrapper identity})
(def react-native-redash #js {:clamp nil})
(def react-native-languages
(clj->js {:default {:language "en",
:addEventListener (fn []),
(clj->js {:default {:language "en"
:addEventListener (fn [])
:removeEventListener (fn [])}}))
(def react-native-device-info
@ -262,8 +296,9 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
#js {:CameraKitCamera #js {}})
(def react-native-push-notification
#js {:localNotification identity
:requestPermission identity})
#js
{:localNotification identity
:requestPermission identity})
(def react-native-gradien #js {:default #js {}})
@ -291,12 +326,16 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
(def react-native-camera-roll
(clj->js {:default #js {}}))
(def wallet-connect-client #js {:default #js {}
:CLIENT_EVENTS #js {:session #js {:request nil
:created nil
:deleted nil
:proposal nil
:updated nil}}})
(def wallet-connect-client
#js
{:default #js {}
:CLIENT_EVENTS #js
{:session #js
{:request nil
:created nil
:deleted nil
:proposal nil
:updated nil}}})
(def worklet-factory
#js {:applyAnimationsToStyle (fn [])})
@ -306,70 +345,71 @@ globalThis.__STATUS_MOBILE_JS_IDENTITY_PROXY__ = new Proxy({}, {get() { return (
(def record-audio-worklets #js {})
;; Update i18n_resources.cljs
(defn mock [module]
(defn mock
[module]
(case module
"react-native-languages" react-native-languages
"react-native-background-timer" background-timer
"react-native-image-crop-picker" image-crop-picker
"react-native-gesture-handler" react-native-gesture-handler
"react-native-safe-area-context" safe-area-context
"react-native-config" config
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
:getBottomSpace (fn [])})
"react-native-screens" (clj->js {})
"react-native-reanimated" react-native-reanimated
"react-native-redash/lib/module/v1" react-native-redash
"react-native-fetch-polyfill" fetch
"react-native-status-keycard" status-keycard
"react-native-keychain" keychain
"react-native-touch-id" touchid
"@react-native-community/netinfo" net-info
"react-native-dialogs" dialogs
"react-native" react-native
"react-native-fs" fs
"react-native-mail" react-native-mail
"react-native-image-resizer" image-resizer
"react-native-haptic-feedback" react-native-haptic-feedback
"react-native-device-info" react-native-device-info
"react-native-push-notification" react-native-push-notification
"react-native-linear-gradient" react-native-gradien
"@react-native-community/masked-view" masked-view
"react-native-blob-util" react-native-blob-util
"react-native-navigation" react-native-navigation
"react-native-languages" react-native-languages
"react-native-background-timer" background-timer
"react-native-image-crop-picker" image-crop-picker
"react-native-gesture-handler" react-native-gesture-handler
"react-native-safe-area-context" safe-area-context
"react-native-config" config
"react-native-iphone-x-helper" (clj->js {:getStatusBarHeight (fn [])
:getBottomSpace (fn [])})
"react-native-screens" (clj->js {})
"react-native-reanimated" react-native-reanimated
"react-native-redash/lib/module/v1" react-native-redash
"react-native-fetch-polyfill" fetch
"react-native-status-keycard" status-keycard
"react-native-keychain" keychain
"react-native-touch-id" touchid
"@react-native-community/netinfo" net-info
"react-native-dialogs" dialogs
"react-native" react-native
"react-native-fs" fs
"react-native-mail" react-native-mail
"react-native-image-resizer" image-resizer
"react-native-haptic-feedback" react-native-haptic-feedback
"react-native-device-info" react-native-device-info
"react-native-push-notification" react-native-push-notification
"react-native-linear-gradient" react-native-gradien
"@react-native-community/masked-view" masked-view
"react-native-blob-util" react-native-blob-util
"react-native-navigation" react-native-navigation
"@react-native-community/push-notification-ios" push-notification-ios
"@react-native-community/blur" react-native-blur
"@react-native-community/cameraroll" react-native-camera-roll
"react-native-camera-kit" react-native-camera-kit
"react-native-permissions" react-native-permissions
"rn-emoji-keyboard" rn-emoji-keyboard
"react-native-hole-view" react-native-hole-view
"react-native-draggable-flatlist" react-native-draggable-flatlist
"react-native-webview" react-native-webview
"@react-native-community/audio-toolkit" react-native-audio-toolkit
"react-native-image-viewing" react-native-image-viewing
"react-native-share" react-native-share
"@react-native-async-storage/async-storage" async-storage
"react-native-svg" react-native-svg
"../src/js/worklet_factory.js" worklet-factory
"../src/js/shell_worklets.js" shell-worklets
"../src/js/record_audio_worklets.js" record-audio-worklets
"./fleets.js" default-fleets
"./chats.js" default-chats
"@walletconnect/client" wallet-connect-client
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
"../translations/de.json" (js/JSON.parse (slurp "./translations/de.json"))
"../translations/en.json" (js/JSON.parse (slurp "./translations/en.json"))
"../translations/es.json" (js/JSON.parse (slurp "./translations/es.json"))
"../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json"))
"../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json"))
"../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json"))
"../translations/id.json" (js/JSON.parse (slurp "./translations/id.json"))
"../translations/it.json" (js/JSON.parse (slurp "./translations/it.json"))
"../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json"))
"../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json"))
"../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json"))
"../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json"))
"../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json"))
"../translations/zh_hant.json" (js/JSON.parse (slurp "./translations/zh_hant.json"))
"../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json"))
"@react-native-community/blur" react-native-blur
"@react-native-community/cameraroll" react-native-camera-roll
"react-native-camera-kit" react-native-camera-kit
"react-native-permissions" react-native-permissions
"rn-emoji-keyboard" rn-emoji-keyboard
"react-native-hole-view" react-native-hole-view
"react-native-draggable-flatlist" react-native-draggable-flatlist
"react-native-webview" react-native-webview
"@react-native-community/audio-toolkit" react-native-audio-toolkit
"react-native-image-viewing" react-native-image-viewing
"react-native-share" react-native-share
"@react-native-async-storage/async-storage" async-storage
"react-native-svg" react-native-svg
"../src/js/worklet_factory.js" worklet-factory
"../src/js/shell_worklets.js" shell-worklets
"../src/js/record_audio_worklets.js" record-audio-worklets
"./fleets.js" default-fleets
"./chats.js" default-chats
"@walletconnect/client" wallet-connect-client
"../translations/ar.json" (js/JSON.parse (slurp "./translations/ar.json"))
"../translations/de.json" (js/JSON.parse (slurp "./translations/de.json"))
"../translations/en.json" (js/JSON.parse (slurp "./translations/en.json"))
"../translations/es.json" (js/JSON.parse (slurp "./translations/es.json"))
"../translations/es_419.json" (js/JSON.parse (slurp "./translations/es_419.json"))
"../translations/fil.json" (js/JSON.parse (slurp "./translations/fil.json"))
"../translations/fr.json" (js/JSON.parse (slurp "./translations/fr.json"))
"../translations/id.json" (js/JSON.parse (slurp "./translations/id.json"))
"../translations/it.json" (js/JSON.parse (slurp "./translations/it.json"))
"../translations/ko.json" (js/JSON.parse (slurp "./translations/ko.json"))
"../translations/pt_BR.json" (js/JSON.parse (slurp "./translations/pt_BR.json"))
"../translations/ru.json" (js/JSON.parse (slurp "./translations/ru.json"))
"../translations/tr.json" (js/JSON.parse (slurp "./translations/tr.json"))
"../translations/zh.json" (js/JSON.parse (slurp "./translations/zh.json"))
"../translations/zh_hant.json" (js/JSON.parse (slurp "./translations/zh_hant.json"))
"../translations/zh_TW.json" (js/JSON.parse (slurp "./translations/zh_TW.json"))
nil))

View File

@ -1,12 +1,12 @@
(ns quo.animated
(:refer-clojure :exclude [abs set delay divide])
(:require [reagent.core :as reagent]
[quo.react-native :as rn]
[quo.gesture-handler :as gh]
[oops.core :refer [oget ocall]]
["react-native-reanimated" :default animated :refer (clockRunning EasingNode)]
(:require ["react-native-reanimated" :default animated :refer (clockRunning EasingNode)]
["react-native-redash/lib/module/v1" :as redash]
quo.react)
[oops.core :refer [ocall oget]]
[quo.gesture-handler :as gh]
quo.react
[quo.react-native :as rn]
[reagent.core :as reagent])
(:require-macros [quo.react :refer [maybe-js-deps]]))
(def create-animated-component (comp reagent/adapt-react-class (.-createAnimatedComponent animated)))
@ -15,9 +15,10 @@
(def text (reagent/adapt-react-class (.-Text animated)))
(def scroll-view (reagent/adapt-react-class (.-ScrollView animated)))
(def code (reagent/adapt-react-class (.-Code animated)))
(def animated-flat-list (create-animated-component gh/flat-list-raw))
(def animated-flat-list (create-animated-component gh/flat-list-raw))
(defn flat-list [props]
(defn flat-list
[props]
[animated-flat-list (rn/base-list-props props)])
(def useCode (.-useCode animated))
@ -58,37 +59,42 @@
(def bezier (.-bezier ^js EasingNode))
(def linear (.-linear ^js EasingNode))
(def easings {:linear linear
:ease-in (bezier 0.42 0 1 1)
:ease-out (bezier 0 0 0.58 1)
:ease-in-out (bezier 0.42 0 0.58 1)
:cubic (bezier 0.55 0.055 0.675 0.19)
:keyboard (bezier 0.17 0.59 0.4 0.77)})
(def easings
{:linear linear
:ease-in (bezier 0.42 0 1 1)
:ease-out (bezier 0 0 0.58 1)
:ease-in-out (bezier 0.42 0 0.58 1)
:cubic (bezier 0.55 0.055 0.675 0.19)
:keyboard (bezier 0.17 0.59 0.4 0.77)})
(def springs {:lazy {:damping 50
:mass 0.3
:stiffness 120
:overshootClamping true
:bouncyFactor 1}
:jump {:damping 13
:mass 0.5
:stiffness 170
:overshootClamping false
:bouncyFactor 1
:restSpeedThreshold 0.001
:restDisplacementThreshold 0.001}})
(def springs
{:lazy {:damping 50
:mass 0.3
:stiffness 120
:overshootClamping true
:bouncyFactor 1}
:jump {:damping 13
:mass 0.5
:stiffness 170
:overshootClamping false
:bouncyFactor 1
:restSpeedThreshold 0.001
:restDisplacementThreshold 0.001}})
(defn set-value [anim val]
(defn set-value
[anim val]
(ocall anim "setValue" val))
(def Value (oget animated "Value"))
(defn value [x]
(defn value
[x]
(new Value x))
(def Clock (oget animated "Clock"))
(defn clock []
(defn clock
[]
(new Clock))
(def debug (oget animated "debug"))
@ -100,8 +106,10 @@
([config options]
(ocall animated "event" (clj->js config) (clj->js options))))
(defn on-change [state node]
(ocall animated "onChange"
(defn on-change
[state node]
(ocall animated
"onChange"
state
(if (vector? node)
(clj->js node)
@ -124,24 +132,31 @@
(clj->js else-node)
else-node))))
(defn block [opts]
(defn block
[opts]
(.block ^js animated (to-array opts)))
(defn interpolate [anim-value config]
(defn interpolate
[anim-value config]
(.interpolateNode ^js animated anim-value (clj->js config)))
(defn call* [args callback]
(defn call*
[args callback]
(.call ^js animated (to-array args) callback))
(defn timing [clock-value opts config]
(defn timing
[clock-value opts config]
(.timing ^js animated
clock-value
(clj->js opts)
(clj->js config)))
(defn spring [clock-value opts config]
(.spring ^js animated clock-value
(clj->js opts) (clj->js config)))
(defn spring
[clock-value opts config]
(.spring ^js animated
clock-value
(clj->js opts)
(clj->js config)))
(def extrapolate {:clamp (oget animated "Extrapolate" "CLAMP")})
@ -150,37 +165,48 @@
(def clamp (oget redash "clamp"))
(def diff-clamp (.-diffClamp ^js redash))
(defn with-spring [config]
(defn with-spring
[config]
(ocall redash "withSpring" (clj->js config)))
(defn with-decay [config]
(defn with-decay
[config]
(.withDecay ^js redash (clj->js config)))
(defn with-offset [config]
(defn with-offset
[config]
(.withOffset ^js redash (clj->js config)))
(defn with-spring-transition [val config]
(defn with-spring-transition
[val config]
(.withSpringTransition ^js redash val (clj->js config)))
(defn with-timing-transition [val config]
(defn with-timing-transition
[val config]
(.withTimingTransition ^js redash val (clj->js config)))
(defn use-spring-transition [val config]
(defn use-spring-transition
[val config]
(.useSpringTransition ^js redash val (clj->js config)))
(defn use-timing-transition [val config]
(defn use-timing-transition
[val config]
(.useTimingTransition ^js redash val (clj->js config)))
(defn re-timing [config]
(defn re-timing
[config]
(.timing ^js redash (clj->js config)))
(defn re-spring [config]
(defn re-spring
[config]
(.spring ^js redash (clj->js config)))
(defn on-scroll [opts]
(defn on-scroll
[opts]
(ocall redash "onScrollEvent" (clj->js opts)))
(defn on-gesture [opts]
(defn on-gesture
[opts]
(let [gesture-event (event #js [#js {:nativeEvent (clj->js opts)}])]
{:onHandlerStateChange gesture-event
:onGestureEvent gesture-event}))
@ -191,19 +217,22 @@
(def delay (.-delay ^js redash))
(defn loop* [opts]
(defn loop*
[opts]
(ocall redash "loop" (clj->js opts)))
(def use-value (.-useValue ^js redash))
(def use-clock (.-useClock ^js redash))
(defn use-gesture [opts]
(defn use-gesture
[opts]
(let [gesture (.useGestureHandler ^js redash (clj->js opts))]
{:onHandlerStateChange (.-onHandlerStateChange ^js gesture)
:onGestureEvent (.-onGestureEvent ^js gesture)}))
(defn snap-point [value velocity snap-points]
(defn snap-point
[value velocity snap-points]
(.snapPoint ^js redash value velocity (to-array snap-points)))
(defn cancelable-loop
@ -256,12 +285,13 @@
(set position (add offset val))])
(cond* (and* (eq state (:end gh/states))
(not* animation-over))
[(set position (re-timing
{:clock c
:easing easing
:duration duration
:from position
:to to}))
[(set position
(re-timing
{:clock c
:easing easing
:duration duration
:from position
:to to}))
(cond* (not* (clock-running c))
finish-animation)])
position])))

View File

@ -1,9 +1,9 @@
(ns quo.components.animated.pressable
(:require [quo.animated :as animated]
(:require [cljs-bean.core :as bean]
[quo.animated :as animated]
[quo.gesture-handler :as gesture-handler]
[quo.react :as react]
[reagent.core :as reagent]
[cljs-bean.core :as bean]
[quo.gesture-handler :as gesture-handler]))
[reagent.core :as reagent]))
(def long-press-duration 500)
(def scale-down-small 0.95)
@ -54,7 +54,8 @@
:right 0
:position :absolute})
(defn pressable-hooks [props]
(defn pressable-hooks
[props]
(let [{background-color :bgColor
border-radius :borderRadius
border-color :borderColor
@ -69,23 +70,24 @@
:or {border-radius 0
type "primary"}}
(bean/bean props)
long-press-ref (react/create-ref)
state (animated/use-value (:undetermined gesture-handler/states))
active (animated/eq state (:began gesture-handler/states))
gesture-handler (animated/use-gesture {:state state})
animation (react/use-memo
(fn []
(animated/with-timing-transition active
{:duration (animated/cond* active time-in time-out)
:easing (:ease-in animated/easings)}))
[])
long-press-ref (react/create-ref)
state (animated/use-value (:undetermined gesture-handler/states))
active (animated/eq state (:began gesture-handler/states))
gesture-handler (animated/use-gesture {:state state})
animation (react/use-memo
(fn []
(animated/with-timing-transition active
{:duration (animated/cond* active time-in time-out)
:easing (:ease-in animated/easings)}))
[])
{:keys [background
foreground]} (react/use-memo
(fn []
(type->animation {:type (keyword type)
:animation animation}))
[type])
handle-press (fn [] (when on-press (on-press)))
foreground]}
(react/use-memo
(fn []
(type->animation {:type (keyword type)
:animation animation}))
[type])
handle-press (fn [] (when on-press (on-press)))
long-gesture-handler (react/callback
(fn [^js evt]
(let [gesture-state (-> evt .-nativeEvent .-state)]
@ -111,8 +113,9 @@
:min-duration-ms long-press-duration
:max-dist 22
:ref long-press-ref}
[animated/view {:accessible true
:accessibility-label accessibility-label}
[animated/view
{:accessible true
:accessibility-label accessibility-label}
[gesture-handler/tap-gesture-handler
(merge gesture-handler
{:shouldCancelWhenOutside true
@ -120,12 +123,13 @@
:enabled (boolean (and (or on-press on-long-press on-press-start)
(not disabled)))})
[animated/view
[animated/view {:style (merge absolute-fill
background
{:background-color background-color
:border-radius border-radius
:border-color border-color
:border-width border-width})}]
[animated/view
{:style (merge absolute-fill
background
{:background-color background-color
:border-radius border-radius
:border-color border-color
:border-width border-width})}]
(into [animated/view {:style foreground}]
(react/get-children children))]]]])))

View File

@ -7,7 +7,8 @@
[quo.platform :as platform]
[reagent.core :as reagent]))
(defn header-wrapper-style [{:keys [value offset]}]
(defn header-wrapper-style
[{:keys [value offset]}]
(merge
{:background-color (:ui-background @colors/theme)}
(when (and offset platform/android?)
@ -27,66 +28,78 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})))
(defn title-style [layout]
{:flex 1
(defn title-style
[layout]
{:flex 1
:justify-content :center
:padding-right (get-in layout [:right :width])})
:padding-right (get-in layout [:right :width])})
(defn header-container []
(defn header-container
[]
(let [y (animated/value 0)
animation-value (animated/value 0)
animation (animated/with-timing-transition
animation-value
{:duration 250
:easing (:ease-in animated/easings)})
animation-value
{:duration 250
:easing (:ease-in animated/easings)})
on-scroll (animated/on-scroll {:y y})
layout (reagent/atom {})
offset (reagent/atom 0)
on-layout (fn [evt]
(reset! offset (oget evt "nativeEvent" "layout" "height")))]
(fn [{:keys [extended-header refresh-control refreshing-sub refreshing-counter] :as props} children]
[animated/view {:flex 1
:pointer-events :box-none}
[animated/code {:key (str @offset)
:exec (animated/cond*
(animated/and* (animated/greater-or-eq y @offset)
(animated/greater-or-eq y 1))
(animated/set animation-value 1)
(animated/set animation-value 0))}]
[animated/view {:pointer-events :box-none
:style (header-wrapper-style {:value y
:offset @offset})}
[header/header (merge
{:get-layout (fn [el l] (swap! layout assoc el l))
:border-bottom false
:title-component [animated/view {:style (title-style @layout)}
[extended-header {:value y
:animation animation
:minimized true
:offset @offset}]]
:title-align :left}
(dissoc props :extended-header))]]
(into [animated/scroll-view {:on-scroll on-scroll
:refreshControl (when refresh-control
(refresh-control
(and @refreshing-sub
@refreshing-counter)))
:style {:z-index 1}
:scrollEventThrottle 16}
[animated/view
{:flex 1
:pointer-events :box-none}
[animated/code
{:key (str @offset)
:exec (animated/cond*
(animated/and* (animated/greater-or-eq y @offset)
(animated/greater-or-eq y 1))
(animated/set animation-value 1)
(animated/set animation-value 0))}]
[animated/view
{:pointer-events :box-none
:style (header-wrapper-style {:value y
:offset @offset})}
[header/header
(merge
{:get-layout (fn [el l] (swap! layout assoc el l))
:border-bottom false
:title-component [animated/view {:style (title-style @layout)}
[extended-header
{:value y
:animation animation
:minimized true
:offset @offset}]]
:title-align :left}
(dissoc props :extended-header))]]
(into [animated/scroll-view
{:on-scroll on-scroll
:refreshControl (when refresh-control
(refresh-control
(and @refreshing-sub
@refreshing-counter)))
:style {:z-index 1}
:scrollEventThrottle 16}
[animated/view {:pointer-events :box-none}
[animated/view {:pointer-events :box-none
:on-layout on-layout}
[extended-header {:value y
:animation animation
:offset @offset}]]]]
[animated/view
{:pointer-events :box-none
:on-layout on-layout}
[extended-header
{:value y
:animation animation
:offset @offset}]]]]
children)])))
(defn header [{:keys [use-insets] :as props} & children]
(defn header
[{:keys [use-insets] :as props} & children]
(if use-insets
[safe-area/consumer
(fn [insets]
[header-container (-> props
(dissoc :use-insets)
(assoc :insets insets))
[header-container
(-> props
(dissoc :use-insets)
(assoc :insets insets))
children])]
[header-container props children]))

View File

@ -15,13 +15,14 @@
:flex 1
:justify-content :flex-end})
(defn backdrop []
{:flex 1
:position :absolute
:left 0
:top 0
:right 0
:bottom 0})
(defn backdrop
[]
{:flex 1
:position :absolute
:left 0
:top 0
:right 0
:bottom 0})
(defn content-container
[window-height]

View File

@ -1,25 +1,27 @@
(ns quo.components.bottom-sheet.view
(:require [reagent.core :as reagent]
(:require [cljs-bean.core :as bean]
[quo.animated :as animated]
[quo.react-native :as rn]
[quo.react :as react]
[quo.platform :as platform]
[cljs-bean.core :as bean]
[quo.components.safe-area :as safe-area]
[quo.components.bottom-sheet.style :as styles]
[quo.components.safe-area :as safe-area]
[quo.design-system.colors :as colors]
[quo.gesture-handler :as gesture-handler]
[quo.design-system.colors :as colors]))
[quo.platform :as platform]
[quo.react :as react]
[quo.react-native :as rn]
[reagent.core :as reagent]))
(def opacity-coeff 0.8)
(def close-duration 150)
(def spring-config {:damping 15
:mass 0.7
:stiffness 150
:overshootClamping false
:restSpeedThreshold 0.1
:restDisplacementThreshold 0.1})
(def spring-config
{:damping 15
:mass 0.7
:stiffness 150
:overshootClamping false
:restSpeedThreshold 0.1
:restDisplacementThreshold 0.1})
(defn bottom-sheet-hooks [props]
(defn bottom-sheet-hooks
[props]
(let [{on-cancel :onCancel
disable-drag? :disableDrag?
show-handle? :showHandle?
@ -31,77 +33,83 @@
backdrop-dismiss? true
back-button-cancel true}}
(bean/bean props)
body-ref (react/create-ref)
body-ref (react/create-ref)
master-ref (react/create-ref)
height (react/state 0)
{window-height :height} (rn/use-window-dimensions)
height (react/state 0)
{window-height :height} (rn/use-window-dimensions)
{:keys [keyboard-shown
keyboard-height]} (rn/use-keyboard)
keyboard-height-android-delta (if (and platform/android? keyboard-shown) (+ keyboard-height 20) 0)
safe-area (safe-area/use-safe-area)
window-height (- window-height (if platform/android?
(+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when react-native-navigation v8 will be implemented https://github.com/wix/react-native-navigation/issues/7225
0))
min-height (+ (* styles/vertical-padding 2) (:bottom safe-area))
max-height (- window-height (:top safe-area))
visible (react/state false)
keyboard-height]}
(rn/use-keyboard)
keyboard-height-android-delta
(if (and platform/android? keyboard-shown) (+ keyboard-height 20) 0)
safe-area (safe-area/use-safe-area)
window-height (- window-height
(if platform/android?
(+ 50 keyboard-height-android-delta) ;; TODO : remove 50 when
;; react-native-navigation v8 will be
;; implemented
;; https://github.com/wix/react-native-navigation/issues/7225
0))
min-height (+ (* styles/vertical-padding 2) (:bottom safe-area))
max-height (- window-height (:top safe-area))
visible (react/state false)
master-translation-y (animated/use-value 0)
master-velocity-y (animated/use-value (:undetermined gesture-handler/states))
master-state (animated/use-value (:undetermined gesture-handler/states))
tap-state (animated/use-value 0)
manual-open (animated/use-value 0)
manual-close (animated/use-value 0)
offset (animated/use-value 0)
drag-over (animated/use-value 1)
clock (animated/use-clock)
tap-gesture-handler (animated/use-gesture {:state tap-state})
on-master-event (animated/use-gesture
{:translationY master-translation-y
:state master-state
:velocityY master-velocity-y})
on-body-event on-master-event
sheet-height (min max-height
(+ styles/border-radius @height))
master-velocity-y (animated/use-value (:undetermined gesture-handler/states))
master-state (animated/use-value (:undetermined gesture-handler/states))
tap-state (animated/use-value 0)
manual-open (animated/use-value 0)
manual-close (animated/use-value 0)
offset (animated/use-value 0)
drag-over (animated/use-value 1)
clock (animated/use-clock)
tap-gesture-handler (animated/use-gesture {:state tap-state})
on-master-event (animated/use-gesture
{:translationY master-translation-y
:state master-state
:velocityY master-velocity-y})
on-body-event on-master-event
sheet-height (min max-height
(+ styles/border-radius @height))
open-snap-point (animated/use-value 0)
open-snap-point (animated/use-value 0)
close-snap-point 0
on-close (fn []
(when @visible
(reset! visible false)
(reset! height 0)
(when on-cancel
(on-cancel))))
close-sheet (fn []
(animated/set-value manual-close 1))
on-snap (fn [pos]
(when (= close-snap-point (aget pos 0))
(on-close)))
interrupted (animated/and* (animated/eq master-state (:began gesture-handler/states))
(animated/clock-running clock))
translate-y (react/use-memo
(fn []
(animated/with-easing
{:value (animated/cond* (animated/less-or-eq master-translation-y 0)
(animated/divide master-translation-y 2)
master-translation-y)
:velocity master-velocity-y
:offset offset
:state master-state
:animation-over drag-over
:snap-points [open-snap-point close-snap-point]}))
[])
opacity (react/use-memo
(fn []
(animated/cond*
open-snap-point
(animated/interpolate
translate-y
{:inputRange [(animated/multiply open-snap-point opacity-coeff) 0]
:outputRange [1 0]
:extrapolate (:clamp animated/extrapolate)})))
[])]
on-close (fn []
(when @visible
(reset! visible false)
(reset! height 0)
(when on-cancel
(on-cancel))))
close-sheet (fn []
(animated/set-value manual-close 1))
on-snap (fn [pos]
(when (= close-snap-point (aget pos 0))
(on-close)))
interrupted (animated/and* (animated/eq master-state (:began gesture-handler/states))
(animated/clock-running clock))
translate-y (react/use-memo
(fn []
(animated/with-easing
{:value (animated/cond* (animated/less-or-eq master-translation-y 0)
(animated/divide master-translation-y 2)
master-translation-y)
:velocity master-velocity-y
:offset offset
:state master-state
:animation-over drag-over
:snap-points [open-snap-point close-snap-point]}))
[])
opacity (react/use-memo
(fn []
(animated/cond*
open-snap-point
(animated/interpolate
translate-y
{:inputRange [(animated/multiply open-snap-point opacity-coeff) 0]
:outputRange [1 0]
:extrapolate (:clamp animated/extrapolate)})))
[])]
(animated/code!
(fn []
(animated/cond* (animated/and* (animated/eq master-state (:end gesture-handler/states))
@ -177,39 +185,48 @@
(close-sheet)))
[visible?])
(reagent/as-element
[rn/view {:style styles/container
:pointer-events :box-none}
[gesture-handler/tap-gesture-handler (merge {:enabled backdrop-dismiss?}
tap-gesture-handler)
[animated/view {:style (merge (styles/backdrop)
(when platform/ios?
{:opacity opacity
:background-color (:backdrop @colors/theme)}))}]]
[animated/view {:style (merge (styles/content-container window-height)
{:transform [{:translateY (if (= sheet-height max-height)
(animated/add translate-y keyboard-height-android-delta)
translate-y)}
{:translateY (* window-height 2)}]})}
[gesture-handler/pan-gesture-handler (merge on-master-event
{:ref master-ref
:wait-for body-ref
:enabled (not disable-drag?)})
[animated/view {:style styles/content-header}
[rn/view
{:style styles/container
:pointer-events :box-none}
[gesture-handler/tap-gesture-handler
(merge {:enabled backdrop-dismiss?}
tap-gesture-handler)
[animated/view
{:style (merge (styles/backdrop)
(when platform/ios?
{:opacity opacity
:background-color (:backdrop @colors/theme)}))}]]
[animated/view
{:style (merge (styles/content-container window-height)
{:transform [{:translateY (if (= sheet-height max-height)
(animated/add translate-y
keyboard-height-android-delta)
translate-y)}
{:translateY (* window-height 2)}]})}
[gesture-handler/pan-gesture-handler
(merge on-master-event
{:ref master-ref
:wait-for body-ref
:enabled (not disable-drag?)})
[animated/view {:style styles/content-header}
(when show-handle?
[rn/view {:style styles/handle}])]]
[gesture-handler/pan-gesture-handler (merge on-body-event
{:ref body-ref
:wait-for master-ref
:enabled (and (not disable-drag?)
(not= sheet-height max-height))})
[animated/view {:height sheet-height
:flex 1}
[animated/view {:style {:padding-top styles/vertical-padding
:padding-bottom (+ styles/vertical-padding
(if (and platform/ios? keyboard-shown)
keyboard-height
(:bottom safe-area)))}
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
[gesture-handler/pan-gesture-handler
(merge on-body-event
{:ref body-ref
:wait-for master-ref
:enabled (and (not disable-drag?)
(not= sheet-height max-height))})
[animated/view
{:height sheet-height
:flex 1}
[animated/view
{:style {:padding-top styles/vertical-padding
:padding-bottom (+ styles/vertical-padding
(if (and platform/ios? keyboard-shown)
keyboard-height
(:bottom safe-area)))}
:on-layout #(reset! height (.-nativeEvent.layout.height ^js %))}
(into [:<>]
(react/get-children children))]]]]])))

View File

@ -1,14 +1,14 @@
(ns quo.components.button.view
(:require [quo.components.animated.pressable :as animation]
[quo.react-native :as rn]
[quo.haptic :as haptic]
[quo.components.text :as text] ;; FIXME:
[quo.design-system.colors :as colors]
[quo.design-system.spacing :as spacing]
[quo.components.text :as text]
;; FIXME:
[quo.haptic :as haptic]
[quo.react-native :as rn]
[status-im.ui.components.icons.icons :as icons]))
(defn style-container [type]
(defn style-container
[type]
(merge {:height 44
:align-items :center
:justify-content :center
@ -19,14 +19,16 @@
:icon {}
nil)))
(defn content-style [type]
(defn content-style
[type]
(case type
:primary (:base spacing/padding-horizontal)
:secondary (:x-tiny spacing/padding-horizontal)
:icon (:tiny spacing/padding-horizontal)
nil))
(defn themes [theme]
(defn themes
[theme]
(case theme
:main {:icon-color (:icon-04 @colors/theme)
:background-color (:interactive-02 @colors/theme)
@ -54,44 +56,46 @@
:text-color (:text-01 @colors/theme)
:border-color (:ui-01 @colors/theme)}))
(defn button [{:keys [on-press disabled type theme before after
haptic-feedback haptic-type on-long-press on-press-start
accessibility-label loading border-radius style test-ID]
:or {theme :main
type :primary
haptic-feedback true
border-radius 8
haptic-type :selection}}
children]
(let [theme' (cond
disabled :disabled
:else theme)
(defn button
[{:keys [on-press disabled type theme before after
haptic-feedback haptic-type on-long-press on-press-start
accessibility-label loading border-radius style test-ID]
:or {theme :main
type :primary
haptic-feedback true
border-radius 8
haptic-type :selection}}
children]
(let [theme' (cond
disabled :disabled
:else theme)
{:keys [icon-color background-color text-color border-color]}
(themes theme')
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))]
[animation/pressable (merge {:bg-color background-color
:border-radius border-radius
:type type
:disabled disabled
:accessibility-label accessibility-label}
(when border-color
{:border-color border-color
:border-width 1})
(when on-press
{:on-press (fn []
(optional-haptic)
(on-press))})
(when on-long-press
{:on-long-press (fn []
(optional-haptic)
(on-long-press))})
(when on-press-start
{:on-press-start (fn []
(optional-haptic)
(on-press-start))}))
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))]
[animation/pressable
(merge {:bg-color background-color
:border-radius border-radius
:type type
:disabled disabled
:accessibility-label accessibility-label}
(when border-color
{:border-color border-color
:border-width 1})
(when on-press
{:on-press (fn []
(optional-haptic)
(on-press))})
(when on-long-press
{:on-long-press (fn []
(optional-haptic)
(on-long-press))})
(when on-press-start
{:on-press-start (fn []
(optional-haptic)
(on-press-start))}))
[rn/view {:test-ID test-ID :style (merge (style-container type) style)}
(when before
[rn/view
@ -99,17 +103,19 @@
(when loading
[rn/view {:style {:position :absolute}}
[rn/activity-indicator]])
[rn/view {:style (merge (content-style type)
(when loading
{:opacity 0}))}
[rn/view
{:style (merge (content-style type)
(when loading
{:opacity 0}))}
(cond
(= type :icon)
[icons/icon children {:color icon-color}]
(string? children)
[text/text {:weight :medium
:number-of-lines 1
:style {:color text-color}}
[text/text
{:weight :medium
:number-of-lines 1
:style {:color text-color}}
children]
(vector? children)

View File

@ -2,7 +2,8 @@
(:require [quo.animated :as animated]
[quo.design-system.colors :as colors]))
(defn switch-style [state disabled]
(defn switch-style
[state disabled]
{:width 52
:height 28
:border-radius 14
@ -13,7 +14,8 @@
(:interactive-04 @colors/theme)
(:interactive-01 @colors/theme)))})
(defn switch-bullet-style [state hold]
(defn switch-bullet-style
[state hold]
{:width 20
:height 20
:border-radius 10
@ -26,7 +28,8 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})
(defn radio-style [state disabled]
(defn radio-style
[state disabled]
{:width 20
:height 20
:border-radius 10
@ -37,7 +40,8 @@
(:interactive-04 @colors/theme)
(:interactive-01 @colors/theme)))})
(defn radio-bullet-style [state hold]
(defn radio-bullet-style
[state hold]
{:width 12
:height 12
:border-radius 6
@ -50,7 +54,8 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})
(defn animated-checkbox-style [state disabled]
(defn animated-checkbox-style
[state disabled]
{:width 18
:height 18
:border-radius 4
@ -62,7 +67,8 @@
(:interactive-04 @colors/theme)
(:interactive-01 @colors/theme)))})
(defn checkbox-style [value disabled]
(defn checkbox-style
[value disabled]
{:width 18
:height 18
:border-radius 4
@ -74,9 +80,11 @@
(:interactive-01 @colors/theme))
(:ui-01 @colors/theme))})
(defn animated-check-icon-style [state hold]
{:opacity (animated/mix hold 1 0.6)
:transform [{:scale (animated/mix state 0.0001 1)}]})
(defn animated-check-icon-style
[state hold]
{:opacity (animated/mix hold 1 0.6)
:transform [{:scale (animated/mix state 0.0001 1)}]})
(defn check-icon-style [value]
(defn check-icon-style
[value]
{:opacity (if value 1 0)})

View File

@ -1,34 +1,37 @@
(ns quo.components.controls.view
(:require [reagent.core :as reagent]
[cljs-bean.core :as bean]
(:require [cljs-bean.core :as bean]
[quo.animated :as animated]
[quo.components.controls.styles :as styles]
[quo.design-system.colors :as colors]
[quo.gesture-handler :as gh]
[quo.react :as react]
[quo.react-native :as rn]
[quo.animated :as animated]
[quo.gesture-handler :as gh]
[quo.design-system.colors :as colors]
[quo.components.controls.styles :as styles]
[reagent.core :as reagent]
[status-im.ui.components.icons.icons :as icons]))
(defn control-builder [component]
(defn control-builder
[component]
(fn [props]
(let [{:keys [value onChange disabled]}
(bean/bean props)
state (animated/use-value 0)
tap-state (animated/use-value (:undetermined gh/states))
tap-handler (animated/on-gesture {:state tap-state})
hold (react/use-memo
(fn []
(animated/with-timing-transition
(animated/eq tap-state (:began gh/states))
{}))
[])
transition (react/use-memo
(fn []
(animated/with-spring-transition state (:lazy animated/springs)))
[])
press-end (fn []
(when (and (not disabled) onChange)
(onChange (not value))))]
state (animated/use-value 0)
tap-state (animated/use-value (:undetermined gh/states))
tap-handler (animated/on-gesture {:state tap-state})
hold (react/use-memo
(fn []
(animated/with-timing-transition
(animated/eq tap-state (:began gh/states))
{}))
[])
transition (react/use-memo
(fn []
(animated/with-spring-transition state
(:lazy
animated/springs)))
[])
press-end (fn []
(when (and (not disabled) onChange)
(onChange (not value))))]
(animated/code!
(fn []
(animated/cond* (animated/eq tap-state (:end gh/states))
@ -41,38 +44,47 @@
(animated/set state (if (true? value) 1 0)))
[value])
(reagent/as-element
[gh/tap-gesture-handler (merge tap-handler
{:shouldCancelWhenOutside true
:enabled (boolean (and onChange (not disabled)))})
[gh/tap-gesture-handler
(merge tap-handler
{:shouldCancelWhenOutside true
:enabled (boolean (and onChange (not disabled)))})
[animated/view
[component {:transition transition
:hold hold
:disabled disabled}]]]))))
[component
{:transition transition
:hold hold
:disabled disabled}]]]))))
(defn switch-view [{:keys [transition hold disabled value]}]
[animated/view {:style (styles/switch-style transition disabled)
:accessibility-label (str "switch-" (if value "on" "off"))
:accessibility-role :switch}
(defn switch-view
[{:keys [transition hold disabled value]}]
[animated/view
{:style (styles/switch-style transition disabled)
:accessibility-label (str "switch-" (if value "on" "off"))
:accessibility-role :switch}
[animated/view {:style (styles/switch-bullet-style transition hold)}]])
(defn radio-view [{:keys [transition hold disabled value]}]
[animated/view {:style (styles/radio-style transition disabled)
:accessibility-label (str "radio-" (if value "on" "off"))
:accessibility-role :radio}
(defn radio-view
[{:keys [transition hold disabled value]}]
[animated/view
{:style (styles/radio-style transition disabled)
:accessibility-label (str "radio-" (if value "on" "off"))
:accessibility-role :radio}
[animated/view {:style (styles/radio-bullet-style transition hold)}]])
(defn checkbox-view [props]
(defn checkbox-view
[props]
(let [{:keys [value onChange disabled]} (bean/bean props)]
(reagent/as-element
[rn/touchable-without-feedback
{:on-press (when (and onChange (not disabled)) onChange)}
[rn/view {:style (styles/checkbox-style value disabled)
:accessibility-label (str "checkbox-" (if value "on" "off"))
:accessibility-role :checkbox}
[rn/view
{:style (styles/checkbox-style value disabled)
:accessibility-label (str "checkbox-" (if value "on" "off"))
:accessibility-role :checkbox}
[rn/view {:style (styles/check-icon-style value)}
[icons/tiny-icon :tiny-icons/tiny-check {:color colors/white}]]]])))
(defn animated-checkbox-view [{:keys [transition hold disabled value]}]
(defn animated-checkbox-view
[{:keys [transition hold disabled value]}]
[animated/view
{:style (styles/animated-checkbox-style transition disabled)
:accessibility-label (str "checkbox-" (if value "on" "off"))

View File

@ -10,7 +10,8 @@
(def header-height 56)
(defn header-wrapper-style [{:keys [height border-bottom background]}]
(defn header-wrapper-style
[{:keys [height border-bottom background]}]
(merge
(:x-tiny spacing/padding-horizontal)
{:background-color (:ui-background @colors/theme)
@ -21,32 +22,37 @@
{:border-bottom-width 1
:border-bottom-color (:ui-01 @colors/theme)})))
(def absolute-fill {:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def absolute-fill
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def content {:flex 1
:flex-direction :row
:align-items :center
:justify-content :center})
(def content
{:flex 1
:flex-direction :row
:align-items :center
:justify-content :center})
(def left {:position :absolute
:left 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-start})
(def left
{:position :absolute
:left 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-start})
(def right {:position :absolute
:right 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-end})
(def right
{:position :absolute
:right 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-end})
(defn title-style [{:keys [left right]} title-align]
(defn title-style
[{:keys [left right]} title-align]
(merge
absolute-fill
(case title-align
@ -59,66 +65,75 @@
(def header-actions-style
(merge
{:flex 1
:flex-direction :row
:align-items :center
:justify-content :center}
{:flex 1
:flex-direction :row
:align-items :center
:justify-content :center}
(:x-tiny spacing/padding-horizontal)))
(def header-action-placeholder
{:width (:base spacing/spacing)})
(def element {:align-items :center
:justify-content :center
:flex 1})
(def element
{:align-items :center
:justify-content :center
:flex 1})
(defn header-action [{:keys [icon label on-press disabled accessibility-label]}]
[button/button (merge {:on-press on-press
:disabled disabled}
(cond
icon {:type :icon
:theme :icon}
label {:type :secondary})
(when accessibility-label
{:accessibility-label accessibility-label}))
(defn header-action
[{:keys [icon label on-press disabled accessibility-label]}]
[button/button
(merge {:on-press on-press
:disabled disabled}
(cond
icon {:type :icon
:theme :icon}
label {:type :secondary})
(when accessibility-label
{:accessibility-label accessibility-label}))
(cond
icon icon
label label)])
(defn header-actions [{:keys [accessories component]}]
(defn header-actions
[{:keys [accessories component]}]
[rn/view {:style element}
(cond
(seq accessories)
(into [rn/view {:style header-actions-style}]
(map header-action accessories))
component component
component component
:else
[rn/view {:style header-action-placeholder}])])
(defn header-title [{:keys [title subtitle component title-align]}]
(defn header-title
[{:keys [title subtitle component title-align]}]
[:<>
(cond
component component
component component
(and title subtitle)
[:<>
[text/text {:weight :medium
:number-of-lines 1}
[text/text
{:weight :medium
:number-of-lines 1}
title]
[text/text {:weight :regular
:color :secondary
:number-of-lines 1}
[text/text
{:weight :regular
:color :secondary
:number-of-lines 1}
subtitle]]
title [text/text {:weight :bold
:number-of-lines 0
:align title-align
:size :large}
title])])
title [text/text
{:weight :bold
:number-of-lines 0
:align title-align
:size :large}
title])])
(defn header [{:keys [left-width right-width]}]
(defn header
[{:keys [left-width right-width]}]
(let [layout (reagent/atom {:left {:width (or left-width 8)
:height header-height}
:right {:width (or right-width 8)
@ -130,46 +145,61 @@
(let [width (oget evt "nativeEvent" "layout" "width")
height (oget evt "nativeEvent" "layout" "height")]
(when get-layout
(get-layout el {:width width
:height height}))
(swap! layout assoc el {:width width
:height height}))))]
(fn [{:keys [left-accessories left-component border-bottom
right-accessories right-component insets get-layout
title subtitle title-component style title-align
background]
:or {title-align :center
border-bottom true}}]
(get-layout el
{:width width
:height height}))
(swap! layout assoc
el
{:width width
:height height}))))]
(fn
[{:keys [left-accessories left-component border-bottom
right-accessories right-component insets get-layout
title subtitle title-component style title-align
background]
:or {title-align :center
border-bottom true}}]
(let [status-bar-height (get insets :top 0)
height (+ header-height status-bar-height)]
[animated/view {:style (header-wrapper-style {:height height
:background background
:border-bottom border-bottom})}
[rn/view {:pointer-events :box-none
:height status-bar-height}]
[rn/view {:style (merge {:height header-height}
style)
:pointer-events :box-none}
[rn/view {:style absolute-fill
:pointer-events :box-none}
[rn/view {:style content
:pointer-events :box-none}
[rn/view {:style left
:on-layout (handle-layout :left get-layout)
:pointer-events :box-none}
[header-actions {:accessories left-accessories
:component left-component}]]
[animated/view
{:style (header-wrapper-style {:height height
:background background
:border-bottom border-bottom})}
[rn/view
{:pointer-events :box-none
:height status-bar-height}]
[rn/view
{:style (merge {:height header-height}
style)
:pointer-events :box-none}
[rn/view
{:style absolute-fill
:pointer-events :box-none}
[rn/view
{:style content
:pointer-events :box-none}
[rn/view
{:style left
:on-layout (handle-layout :left get-layout)
:pointer-events :box-none}
[header-actions
{:accessories left-accessories
:component left-component}]]
[rn/view {:style (title-style @layout title-align)
:on-layout (handle-layout :title get-layout)
:pointer-events :box-none}
[header-title {:title title
:subtitle subtitle
:title-align title-align
:component title-component}]]
[rn/view
{:style (title-style @layout title-align)
:on-layout (handle-layout :title get-layout)
:pointer-events :box-none}
[header-title
{:title title
:subtitle subtitle
:title-align title-align
:component title-component}]]
[rn/view {:style right
:on-layout (handle-layout :right get-layout)
:pointer-events :box-none}
[header-actions {:accessories right-accessories
:component right-component}]]]]]]))))
[rn/view
{:style right
:on-layout (handle-layout :right get-layout)
:pointer-events :box-none}
[header-actions
{:accessories right-accessories
:component right-component}]]]]]]))))

View File

@ -1,14 +1,17 @@
(ns quo.components.list.footer
(:require [quo.react-native :as rn]
(:require [quo.components.text :as text]
[quo.design-system.spacing :as spacing]
[quo.components.text :as text]
[quo.react-native :as rn]
[reagent.core :as reagent]))
(defn footer []
(let [this (reagent/current-component)
(defn footer
[]
(let [this (reagent/current-component)
{:keys [color]
:or {color :secondary}} (reagent/props this)]
[rn/view {:style (merge (:base spacing/padding-horizontal)
(:small spacing/padding-vertical))}
:or {color :secondary}}
(reagent/props this)]
[rn/view
{:style (merge (:base spacing/padding-horizontal)
(:small spacing/padding-vertical))}
(into [text/text {:color color}]
(reagent/children this))]))

View File

@ -1,15 +1,19 @@
(ns quo.components.list.header
(:require [reagent.core :as reagent]
[quo.react-native :as rn]
(:require [quo.components.text :as text]
[quo.design-system.spacing :as spacing]
[quo.components.text :as text]))
[quo.react-native :as rn]
[reagent.core :as reagent]))
(defn header []
(let [this (reagent/current-component)
(defn header
[]
(let [this (reagent/current-component)
{:keys [color]
:or {color :secondary}} (reagent/props this)]
[rn/view {:style (merge (:base spacing/padding-horizontal)
(:x-tiny spacing/padding-vertical))}
(into [text/text {:color color
:style {:margin-top 10}}]
:or {color :secondary}}
(reagent/props this)]
[rn/view
{:style (merge (:base spacing/padding-horizontal)
(:x-tiny spacing/padding-vertical))}
(into [text/text
{:color color
:style {:margin-top 10}}]
(reagent/children this))]))

View File

@ -1,16 +1,18 @@
(ns quo.components.list.index
(:require [quo.react-native :as rn]
[quo.components.text :as text]
[quo.design-system.colors :as colors]))
(:require [quo.components.text :as text]
[quo.design-system.colors :as colors]
[quo.react-native :as rn]))
(defn index [{:keys [title]}]
(defn index
[{:keys [title]}]
[rn/view {:style {:padding-right 16}}
[rn/view {:style {:border-top-width 1
:border-bottom-width 1
:border-right-width 1
:border-color (colors/get-color :border-01)
:padding-vertical 3
:padding-horizontal 16
:border-top-right-radius 16
:border-bottom-right-radius 16}}
[rn/view
{:style {:border-top-width 1
:border-bottom-width 1
:border-right-width 1
:border-color (colors/get-color :border-01)
:padding-vertical 3
:padding-horizontal 16
:border-top-right-radius 16
:border-bottom-right-radius 16}}
[text/text title]]])

View File

@ -1,28 +1,29 @@
(ns quo.components.list.item
(:require [quo.react-native :as rn]
[quo.platform :as platform]
[quo.haptic :as haptic]
[quo.gesture-handler :as gh]
[quo.design-system.spacing :as spacing]
[quo.design-system.colors :as colors]
[quo.components.text :as text]
(:require [quo.components.animated.pressable :as animated]
[quo.components.controls.view :as controls]
[quo.components.text :as text]
[quo.components.tooltip :as tooltip]
[status-im.ui.components.icons.icons :as icons]
[quo.components.animated.pressable :as animated]))
[quo.design-system.colors :as colors]
[quo.design-system.spacing :as spacing]
[quo.gesture-handler :as gh]
[quo.haptic :as haptic]
[quo.platform :as platform]
[quo.react-native :as rn]
[status-im.ui.components.icons.icons :as icons]))
(defn themes [theme]
(defn themes
[theme]
(case theme
:main {:icon-color (:icon-04 @colors/theme)
:icon-bg-color (:interactive-02 @colors/theme)
:active-background (:interactive-02 @colors/theme)
:passive-background (:ui-background @colors/theme)
:text-color (:text-01 @colors/theme)}
:accent {:icon-color (:icon-04 @colors/theme)
:icon-bg-color (:interactive-02 @colors/theme)
:active-background (:interactive-02 @colors/theme)
:passive-background (:ui-background @colors/theme)
:text-color (:text-04 @colors/theme)}
:main {:icon-color (:icon-04 @colors/theme)
:icon-bg-color (:interactive-02 @colors/theme)
:active-background (:interactive-02 @colors/theme)
:passive-background (:ui-background @colors/theme)
:text-color (:text-01 @colors/theme)}
:accent {:icon-color (:icon-04 @colors/theme)
:icon-bg-color (:interactive-02 @colors/theme)
:active-background (:interactive-02 @colors/theme)
:passive-background (:ui-background @colors/theme)
:text-color (:text-04 @colors/theme)}
:negative {:icon-color (:negative-01 @colors/theme)
:icon-bg-color (:negative-02 @colors/theme)
:active-background (:negative-02 @colors/theme)
@ -39,28 +40,34 @@
:passive-background (:ui-background @colors/theme)
:text-color (:text-02 @colors/theme)}))
(defn size->icon-size [size]
(defn size->icon-size
[size]
(case size
:small 36
40))
(defn size->container-size [size]
(defn size->container-size
[size]
(case size
:small 52
64))
(defn size->single-title-size [size]
(defn size->single-title-size
[size]
(case size
:small :base
:large))
(defn container [{:keys [size container-style]} & children]
(into [rn/view {:style (merge (:tiny spacing/padding-horizontal)
{:min-height (size->container-size size)
:padding-vertical 8
:flex-direction :row
:align-items :center
:justify-content :space-between} container-style)}]
(defn container
[{:keys [size container-style]} & children]
(into [rn/view
{:style (merge (:tiny spacing/padding-horizontal)
{:min-height (size->container-size size)
:padding-vertical 8
:flex-direction :row
:align-items :center
:justify-content :space-between}
container-style)}]
children))
(defn icon-column
@ -72,103 +79,118 @@
(vector? icon)
icon
(keyword? icon)
[rn/view {:style {:width icon-size
:height icon-size
:align-items :center
:justify-content :center
:border-radius (/ icon-size 2)
:background-color icon-bg-color}}
[rn/view
{:style {:width icon-size
:height icon-size
:align-items :center
:justify-content :center
:border-radius (/ icon-size 2)
:background-color icon-bg-color}}
[icons/icon icon {:color icon-color}]])])))
(defn title-column
[{:keys [title text-color subtitle subtitle-max-lines subtitle-secondary
title-accessibility-label size text-size title-text-weight
right-side-present?]}]
[rn/view {:style (merge (:tiny spacing/padding-horizontal)
;; make left-side title grow if nothing is present on right-side
(when-not right-side-present?
{:flex 1
:justify-content :center}))}
[rn/view
{:style (merge (:tiny spacing/padding-horizontal)
;; make left-side title grow if nothing is present on right-side
(when-not right-side-present?
{:flex 1
:justify-content :center}))}
(cond
(and title subtitle)
[:<>
;; FIXME(Ferossgp): ReactNative 63 will support view inside text on andrid, remove thess if when migrating
;; FIXME(Ferossgp): ReactNative 63 will support view inside text on andrid, remove thess if when
;; migrating
(if (string? title)
[text/text {:weight (or title-text-weight :medium)
:style {:color text-color}
:accessibility-label title-accessibility-label
:ellipsize-mode :tail
:number-of-lines 1
:size text-size}
[text/text
{:weight (or title-text-weight :medium)
:style {:color text-color}
:accessibility-label title-accessibility-label
:ellipsize-mode :tail
:number-of-lines 1
:size text-size}
title]
title)
(if (string? subtitle-secondary)
[rn/view {:flex-direction :row}
[text/text {:style {:max-width "56.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
[text/text
{:style {:max-width "56.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
subtitle]
[text/text {:style {:width "7%" :text-align :center}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
[text/text
{:style {:width "7%" :text-align :center}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
"•"]
[text/text {:style {:max-width "36.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
[text/text
{:style {:max-width "36.5%"}
:weight :regular
:color :secondary
:ellipsize-mode :middle
:number-of-lines subtitle-max-lines
:size text-size}
subtitle-secondary]]
(if (string? subtitle)
[text/text {:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
[text/text
{:weight :regular
:color :secondary
:ellipsize-mode :tail
:number-of-lines subtitle-max-lines
:size text-size}
subtitle]
subtitle))]
title
(if (string? title)
[text/text {:weight (or title-text-weight :regular)
:number-of-lines 1
:style {:color text-color}
:title-accessibility-label title-accessibility-label
:ellipsize-mode :tail
:size (or text-size (size->single-title-size size))}
[text/text
{:weight (or title-text-weight :regular)
:number-of-lines 1
:style {:color text-color}
:title-accessibility-label title-accessibility-label
:ellipsize-mode :tail
:size (or text-size (size->single-title-size size))}
title]
title))])
(defn left-side [props]
[rn/view {:style {:flex-direction :row
;; Occupy only content width, never grow, but shrink if need be
:flex-grow 0
:flex-shrink 1
:padding-right 16
:align-items (or (:left-side-alignment props) :center)}}
(defn left-side
[props]
[rn/view
{:style {:flex-direction :row
;; Occupy only content width, never grow, but shrink if need be
:flex-grow 0
:flex-shrink 1
:padding-right 16
:align-items (or (:left-side-alignment props) :center)}}
[icon-column props]
[title-column props]])
(defn right-side [{:keys [chevron active disabled accessory accessory-text accessory-style animated-accessory?]}]
(defn right-side
[{:keys [chevron active disabled accessory accessory-text accessory-style animated-accessory?]}]
(when (or chevron accessory)
[rn/view {:style (merge {:align-items :center
:justify-content :flex-end
:flex-direction :row
;; Grow to occupy full space, shrink when need be, but always maitaining 16px left gutter
:flex-grow 1
:flex-shrink 0
:margin-left 16
;; When the left-side leaves no room for right-side, the rendered element is pushed out. A flex-basis ensures that there is some room reserved.
;; The number 80px was determined by trial and error.
:flex-basis 80}
accessory-style)}
[rn/view
{:style (merge {:align-items :center
:justify-content :flex-end
:flex-direction :row
;; Grow to occupy full space, shrink when need be, but always maitaining 16px left
;; gutter
:flex-grow 1
:flex-shrink 0
:margin-left 16
;; When the left-side leaves no room for right-side, the rendered element is pushed
;; out. A flex-basis ensures that there is some room reserved.
;; The number 80px was determined by trial and error.
:flex-basis 80}
accessory-style)}
[rn/view {:style (:tiny spacing/padding-horizontal)}
(case accessory
:radio [controls/radio {:value active :disabled disabled}]
@ -177,25 +199,28 @@
controls/checkbox)
{:value active :disabled disabled}]
:switch [controls/switch {:value active :disabled disabled}]
:text [text/text {:color :secondary
:ellipsize-mode :middle
:number-of-lines 1}
:text [text/text
{:color :secondary
:ellipsize-mode :middle
:number-of-lines 1}
accessory-text]
accessory)]
(when (and chevron platform/ios?)
[rn/view {:style {:padding-right (:tiny spacing/spacing)}}
[icons/icon :main-icons/next {:container-style {:opacity 0.4
:align-items :center
:justify-content :center}
:resize-mode :center
:color (:icon-02 @colors/theme)}]])]))
[icons/icon :main-icons/next
{:container-style {:opacity 0.4
:align-items :center
:justify-content :center}
:resize-mode :center
:color (:icon-02 @colors/theme)}]])]))
(defn list-item
[{:keys [theme accessory disabled subtitle-max-lines icon icon-container-style
left-side-alignment icon-color icon-bg-color
title subtitle subtitle-secondary active on-press on-long-press chevron size text-size
accessory-text accessibility-label title-accessibility-label accessory-style
haptic-feedback haptic-type error animated animated-accessory? title-text-weight container-style
haptic-feedback haptic-type error animated animated-accessory? title-text-weight
container-style
active-background-enabled background-color]
:or {subtitle-max-lines 1
theme :main
@ -203,26 +228,29 @@
animated platform/ios?
active-background-enabled true
haptic-type :selection}}]
(let [theme (if disabled :disabled theme)
(let [theme (if disabled :disabled theme)
{:keys [text-color active-background passive-background]}
(themes theme)
icon-color (or icon-color (:icon-color (themes theme)))
icon-bg-color (or icon-bg-color (:icon-bg-color (themes theme)))
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))
component (cond
(and (not on-press)
(not on-long-press))
rn/view
animated animated/pressable
:else gh/touchable-highlight)]
[rn/view {:background-color (cond (not= background-color nil)
background-color
(and (= accessory :radio) active)
active-background
:else
passive-background)}
icon-color (or icon-color
(:icon-color (themes theme)))
icon-bg-color (or icon-bg-color
(:icon-bg-color (themes theme)))
optional-haptic (fn []
(when haptic-feedback
(haptic/trigger haptic-type)))
component (cond
(and (not on-press)
(not on-long-press))
rn/view
animated animated/pressable
:else gh/touchable-highlight)]
[rn/view
{:background-color (cond (not= background-color nil)
background-color
(and (= accessory :radio) active)
active-background
:else
passive-background)}
[component
(merge {:type :list-item
:disabled disabled
@ -237,35 +265,39 @@
(optional-haptic)
(on-long-press))}))
[container {:size size :container-style container-style}
[left-side {:icon-color icon-color
:text-color (if on-press
text-color
(:text-color (themes :main)))
:left-side-alignment left-side-alignment
:icon-bg-color icon-bg-color
:title-accessibility-label title-accessibility-label
:icon icon
:icon-container-style icon-container-style
:title title
:title-text-weight title-text-weight
:size size
:text-size text-size
:subtitle subtitle
:subtitle-max-lines subtitle-max-lines
:subtitle-secondary subtitle-secondary
:right-side-present? (or accessory chevron)}]
[right-side {:chevron chevron
:active active
:disabled disabled
:on-press on-press
:accessory-text accessory-text
:animated-accessory? animated-accessory?
:accessory-style accessory-style
:accessory accessory}]]]
[left-side
{:icon-color icon-color
:text-color (if on-press
text-color
(:text-color (themes :main)))
:left-side-alignment left-side-alignment
:icon-bg-color icon-bg-color
:title-accessibility-label title-accessibility-label
:icon icon
:icon-container-style icon-container-style
:title title
:title-text-weight title-text-weight
:size size
:text-size text-size
:subtitle subtitle
:subtitle-max-lines subtitle-max-lines
:subtitle-secondary subtitle-secondary
:right-side-present? (or accessory chevron)}]
[right-side
{:chevron chevron
:active active
:disabled disabled
:on-press on-press
:accessory-text accessory-text
:animated-accessory? animated-accessory?
:accessory-style accessory-style
:accessory accessory}]]]
(when error
[tooltip/tooltip (merge {:bottom-value 0}
(when accessibility-label
{:accessibility-label (str (name accessibility-label) "-error")}))
[text/text {:color :negative
:size :small}
[tooltip/tooltip
(merge {:bottom-value 0}
(when accessibility-label
{:accessibility-label (str (name accessibility-label) "-error")}))
[text/text
{:color :negative
:size :small}
error]])]))

View File

@ -1,19 +1,21 @@
(ns quo.components.safe-area
(:require ["react-native-safe-area-context" :as safe-area-context
:refer (SafeAreaView SafeAreaProvider SafeAreaInsetsContext useSafeAreaInsets)]
(:require ["react-native-safe-area-context" :as safe-area-context :refer
(SafeAreaView SafeAreaProvider SafeAreaInsetsContext useSafeAreaInsets)]
[reagent.core :as reagent]))
(def provider (reagent/adapt-react-class SafeAreaProvider))
(def ^:private consumer-raw (reagent/adapt-react-class (.-Consumer ^js SafeAreaInsetsContext)))
(def view (reagent/adapt-react-class SafeAreaView))
(defn consumer [component]
(defn consumer
[component]
[consumer-raw
(fn [insets]
(reagent/as-element
[component (js->clj insets :keywordize-keys true)]))])
(defn use-safe-area []
(defn use-safe-area
[]
(let [insets (useSafeAreaInsets)]
{:top (.-top ^js insets)
:bottom (.-bottom ^js insets)

View File

@ -1,8 +1,9 @@
(ns quo.components.separator
(:require [quo.react-native :as react]
[quo.design-system.colors :as colors]))
(:require [quo.design-system.colors :as colors]
[quo.react-native :as react]))
(defn separator [{:keys [color style]}]
(defn separator
[{:keys [color style]}]
[react/view
{:style
(merge

View File

@ -5,7 +5,8 @@
[quo.react-native :as rn]
[reagent.core :as reagent]))
(defn text-style [{:keys [size align weight monospace color style]}]
(defn text-style
[{:keys [size align weight monospace color style]}]
;; NOTE(Ferossgo): or in destructoring will keep nil as a value
(merge (if monospace
;; TODO(Ferossgp): Add all weights for monospace
@ -38,12 +39,15 @@
{:text-align (or align :auto)}
style))
(defn text []
(defn text
[]
(let [this (reagent/current-component)
props (reagent/props this)
component (if (:animated? props) animated/text rn/text)]
(into [component (merge {:style (text-style props)}
(dissoc props
:style :size :weight :color
:align :animated?))]
(into [component
(merge {:style (text-style props)}
(dissoc props
:style :size
:weight :color
:align :animated?))]
(reagent/children this))))

View File

@ -1,16 +1,15 @@
(ns quo.components.text-input
(:require [clojure.spec.alpha :as s]
[reagent.core :as reagent]
[oops.core :refer [ocall]]
[quo.react-native :as rn]
;; TODO(Ferossgp): Move icon component to lib
[status-im.ui.components.icons.icons :as icons]
[quo.components.text :as text]
[quo.components.tooltip :as tooltip]
[quo.platform :as platform]
[quo.design-system.typography :as typography]
[quo.design-system.spacing :as spacing]
[quo.design-system.colors :as colors]
[quo.components.text :as text]))
[quo.design-system.spacing :as spacing]
[quo.design-system.typography :as typography]
[quo.platform :as platform]
[quo.react-native :as rn] ;; TODO(Ferossgp): Move icon component to lib
[reagent.core :as reagent]
[status-im.ui.components.icons.icons :as icons]))
;; NOTE(Ferossgp): Refactor with hooks when available
;; We track all currently mounted text input refs
@ -22,19 +21,26 @@
(s/def ::multiline boolean?)
(s/def ::secure-text-entry boolean?)
(s/def ::show-cancel boolean?)
(s/def ::label (s/nilable (s/or :string string? :component vector?)))
(s/def ::label
(s/nilable (s/or :string string?
:component vector?)))
(s/def ::cancel-label (s/nilable string?))
(s/def ::default-value (s/nilable string?))
(s/def ::placeholder (s/nilable string?))
(s/def ::keyboard-type (s/nilable (s/or :string string? :keyword keyword?))) ; TODO: make set
(s/def ::accessibility-label (s/nilable (s/or :string string? :keyword keyword?)))
(s/def ::keyboard-type
(s/nilable (s/or :string string?
:keyword keyword?))) ; TODO: make set
(s/def ::accessibility-label
(s/nilable (s/or :string string?
:keyword keyword?)))
(s/def ::on-focus fn?)
(s/def ::on-blur fn?)
(s/def ::on-press fn?)
(s/def ::accessory (s/keys :opt-un [::on-press
::icon
::component]))
(s/def ::accessory
(s/keys :opt-un [::on-press
::icon
::component]))
(s/def ::after (s/nilable ::accessory))
(s/def ::before (s/nilable ::accessory))
@ -42,25 +48,27 @@
(s/def ::input-style ::style)
(s/def ::container-style ::style)
(s/def ::text-input (s/keys :opt-un
[::label
::multiline
::error
::style
::input-style
::keyboard-type
::before
::after
::cancel-label
::on-focus
::on-blur
::container-style
::show-cancel
::accessibility-label
::bottom-value
::secure-text-entry]))
(s/def ::text-input
(s/keys :opt-un
[::label
::multiline
::error
::style
::input-style
::keyboard-type
::before
::after
::cancel-label
::on-focus
::on-blur
::container-style
::show-cancel
::accessibility-label
::bottom-value
::secure-text-entry]))
(defn check-spec [spec prop]
(defn check-spec
[spec prop]
(if (s/valid? spec prop)
true
(do
@ -70,14 +78,17 @@
(def height 44) ; 22 line-height + 11*2 vertical padding
(def multiline-height 88) ; 3 * 22 three line-height + 11* vertical padding
(defn label-style []
(defn label-style
[]
{:margin-bottom (:tiny spacing/spacing)})
(defn text-input-row-style []
(defn text-input-row-style
[]
{:flex-direction :row
:align-items :center})
(defn text-input-view-style [style]
(defn text-input-view-style
[style]
(merge {:border-radius 8
:flex-direction :row
:flex 1
@ -85,7 +96,8 @@
:background-color (:ui-01 @colors/theme)}
style))
(defn text-input-style [multiline input-style monospace before after]
(defn text-input-style
[multiline input-style monospace before after]
(merge (if monospace
typography/monospace
typography/font-regular)
@ -107,66 +119,76 @@
:height multiline-height})
input-style))
(defn cancel-style []
(defn cancel-style
[]
{:margin-left (:tiny spacing/spacing)
:padding-left (:tiny spacing/spacing)
:justify-content :center
:align-self :stretch})
(defn accessory-style []
(defn accessory-style
[]
(merge (:base spacing/padding-horizontal)
{:flex 1
:justify-content :center}))
(defn accessory-element [{:keys [icon component icon-opts style accessibility-label on-press]}]
(defn accessory-element
[{:keys [icon component icon-opts style accessibility-label on-press]}]
(let [el (if on-press
rn/touchable-opacity
rn/view)]
[el (merge {:style {:align-self :stretch}}
(when on-press
{:on-press on-press}))
[rn/view (merge {:style (merge (accessory-style)
style)}
(when accessibility-label
{:accessibility-label accessibility-label}))
[el
(merge {:style {:align-self :stretch}}
(when on-press
{:on-press on-press}))
[rn/view
(merge {:style (merge (accessory-style)
style)}
(when accessibility-label
{:accessibility-label accessibility-label}))
(cond
icon
[icons/icon icon (merge {:color (:icon-01 @colors/theme)}
icon-opts)]
[icons/icon icon
(merge {:color (:icon-01 @colors/theme)}
icon-opts)]
component
component
:else
nil)]]))
(defn text-input-raw []
(defn text-input-raw
[]
(let [focused (reagent/atom nil)
visible (reagent/atom false)
ref (atom nil)
blur (fn []
(some-> @ref (ocall "blur")))]
(fn [{:keys [label multiline error style input-style keyboard-type before after
cancel-label on-focus on-blur show-cancel accessibility-label
bottom-value secure-text-entry container-style get-ref on-cancel
monospace auto-complete-type auto-correct]
:or {cancel-label "Cancel"}
:as props}]
(some-> @ref
(ocall "blur")))]
(fn
[{:keys [label multiline error style input-style keyboard-type before after
cancel-label on-focus on-blur show-cancel accessibility-label
bottom-value secure-text-entry container-style get-ref on-cancel
monospace auto-complete-type auto-correct]
:or {cancel-label "Cancel"}
:as props}]
{:pre [(check-spec ::text-input props)]}
(let [show-cancel (if (nil? show-cancel)
;; Enabled by default on iOs and disabled on Android
platform/ios?
show-cancel)
after (cond
(and secure-text-entry @visible)
{:icon :main-icons/hide
:on-press #(reset! visible false)}
(let [show-cancel (if (nil? show-cancel)
;; Enabled by default on iOs and disabled on Android
platform/ios?
show-cancel)
after (cond
(and secure-text-entry @visible)
{:icon :main-icons/hide
:on-press #(reset! visible false)}
(and secure-text-entry (not @visible))
{:icon :main-icons/show
:on-press #(reset! visible true)}
(and secure-text-entry (not @visible))
{:icon :main-icons/show
:on-press #(reset! visible true)}
:else after)
secure (boolean (and secure-text-entry (not @visible))) ; must be a boolean to work on iOS
:else after)
secure (boolean (and secure-text-entry (not @visible))) ; must be a boolean to work on
; iOS
auto-complete (cond
(= keyboard-type :visible-password)
:off
@ -176,7 +198,9 @@
:else
auto-complete-type)
auto-correct (and (not= keyboard-type :visible-password) (not secure-text-entry) auto-correct)
auto-correct (and (not= keyboard-type :visible-password)
(not secure-text-entry)
auto-correct)
on-cancel (fn []
(when on-cancel
(on-cancel))
@ -185,8 +209,10 @@
(and platform/ios? (= keyboard-type :visible-password))
:default
; the correct approach on Android would be keep secure-text-entry on set keyboard type
; to visible-password. But until https://github.com/facebook/react-native/issues/27946
; the correct approach on Android would be keep secure-text-entry on set
; keyboard type
; to visible-password. But until
; https://github.com/facebook/react-native/issues/27946
; is solved that's the second best way.
(and platform/android? secure-text-entry)
:default
@ -198,59 +224,71 @@
[text/text {:style (label-style)}
label])
[rn/view {:style (text-input-row-style)}
[rn/view {:style (text-input-view-style style)
:important-for-accessibility (if secure-text-entry
:no-hide-descendants
:auto)}
[rn/view
{:style (text-input-view-style style)
:important-for-accessibility (if secure-text-entry
:no-hide-descendants
:auto)}
(when before
[accessory-element before])
[rn/text-input
(merge {:style (text-input-style multiline input-style monospace before after)
:ref (fn [r]
(reset! ref r)
(when get-ref (get-ref r)))
:placeholder-text-color (:text-02 @colors/theme)
:underline-color-android :transparent
:auto-capitalize :none
:secure-text-entry secure
:auto-correct auto-correct
:auto-complete-type auto-complete
:on-focus (fn [evt]
(when on-focus (on-focus evt))
(when show-cancel
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets)))
(reset! focused true))
:on-blur (fn [evt]
(when on-blur (on-blur evt))
(when show-cancel
(rn/configure-next (:ease-in-ease-out rn/layout-animation-presets)))
(reset! focused false))
:keyboard-type keyboard-type}
(when (and platform/ios? (not after))
{:clear-button-mode :while-editing})
(dissoc props
:style :keyboard-type :on-focus :on-blur
:secure-text-entry :ref :get-ref :auto-correct :auto-complete-type))]
(merge
{:style (text-input-style multiline input-style monospace before after)
:ref (fn [r]
(reset! ref r)
(when get-ref (get-ref r)))
:placeholder-text-color (:text-02 @colors/theme)
:underline-color-android :transparent
:auto-capitalize :none
:secure-text-entry secure
:auto-correct auto-correct
:auto-complete-type auto-complete
:on-focus (fn [evt]
(when on-focus (on-focus evt))
(when show-cancel
(rn/configure-next (:ease-in-ease-out
rn/layout-animation-presets)))
(reset! focused true))
:on-blur (fn [evt]
(when on-blur (on-blur evt))
(when show-cancel
(rn/configure-next (:ease-in-ease-out
rn/layout-animation-presets)))
(reset! focused false))
:keyboard-type keyboard-type}
(when (and platform/ios? (not after))
{:clear-button-mode :while-editing})
(dissoc props
:style
:keyboard-type :on-focus
:on-blur
:secure-text-entry
:ref :get-ref
:auto-correct :auto-complete-type))]
(when after
[accessory-element after])]
(when (and show-cancel
(not multiline)
@focused)
[rn/touchable-opacity {:style (cancel-style)
:on-press on-cancel}
[rn/touchable-opacity
{:style (cancel-style)
:on-press on-cancel}
[text/text {:color :link} cancel-label]])
(when error
[tooltip/tooltip (merge {:bottom-value (if bottom-value bottom-value 0)}
(when accessibility-label
{:accessibility-label (str (name accessibility-label) "-error")}))
[text/text {:color :negative
:align :center
:size :small}
[tooltip/tooltip
(merge {:bottom-value (if bottom-value bottom-value 0)}
(when accessibility-label
{:accessibility-label (str (name accessibility-label) "-error")}))
[text/text
{:color :negative
:align :center
:size :small}
error]])]]))))
;; TODO(Ferossgp): Refactor me when hooks available
(defn text-input [{:keys [preserve-input?]
:as props}]
(defn text-input
[{:keys [preserve-input?]
:as props}]
(if preserve-input?
[text-input-raw props]
(let [id (random-uuid)]
@ -261,10 +299,11 @@
:reagent-render
(fn [{:keys [get-ref default-value]
:as props}]
[text-input-raw (merge props
{:get-ref (fn [r]
;; Store input and its defaultValue
;; one we receive a non-nil ref
(when r
(swap! text-input-refs assoc id {:ref r :value default-value}))
(when get-ref (get-ref r)))})])}))))
[text-input-raw
(merge props
{:get-ref (fn [r]
;; Store input and its defaultValue
;; one we receive a non-nil ref
(when r
(swap! text-input-refs assoc id {:ref r :value default-value}))
(when get-ref (get-ref r)))})])}))))

View File

@ -1,17 +1,17 @@
(ns quo.components.tooltip
(:require [reagent.core :as reagent]
[oops.core :refer [oget]]
(:require [oops.core :refer [oget]]
[quo.animated :as animated]
[quo.react-native :as rn]
[quo.design-system.colors :as colors]
[quo.design-system.spacing :as spacing]
[quo.platform :as platform]
;; FIXME(Ferossgp): Dependecy on status
[quo.platform :as platform] ;; FIXME(Ferossgp): Dependecy on status
[quo.react-native :as rn]
[reagent.core :as reagent]
[status-im.ui.components.icons.icons :as icons]))
(def ^:private initial-height 22)
(defn tooltip-style [{:keys [bottom-value animation]}]
(defn tooltip-style
[{:keys [bottom-value animation]}]
(merge
(:base spacing/padding-horizontal)
{:position :absolute
@ -22,7 +22,8 @@
:opacity animation
:transform [{:translateY (animated/mix animation 10 0)}]}))
(defn container-style []
(defn container-style
[]
{:z-index 2
:align-items :center
:shadow-radius 16
@ -30,43 +31,50 @@
:shadow-color (:shadow-01 @colors/theme)
:shadow-offset {:width 0 :height 4}})
(defn content-style []
(defn content-style
[]
(merge (:base spacing/padding-horizontal)
{:padding-vertical 6
:elevation 2
:background-color (:ui-background @colors/theme)
:border-radius 8}))
(defn tooltip []
(defn tooltip
[]
(let [layout (reagent/atom {:height initial-height})
animation-v (animated/value 0)
animation (animated/with-timing-transition
animation-v
{:easing (:ease-in animated/easings)})
animation-v
{:easing (:ease-in animated/easings)})
on-layout (fn [evt]
(let [width (oget evt "nativeEvent" "layout" "width")
height (oget evt "nativeEvent" "layout" "height")]
(reset! layout {:width width
:height height})))]
(fn [{:keys [bottom-value accessibility-label]} & children]
(fn [{:keys [bottom-value accessibility-label]} & children]
[:<>
[animated/code {:exec (animated/cond* (animated/not* animation-v)
(animated/set animation-v 1))}]
[animated/view {:style (tooltip-style {:bottom-value (- (get @layout :height)
bottom-value)
:animation animation})
:pointer-events :box-none}
[animated/view {:style (container-style)
:pointer-events :box-none}
(into [rn/view {:style (content-style)
:pointer-events :box-none
:accessibility-label accessibility-label
:on-layout on-layout}]
[animated/code
{:exec (animated/cond* (animated/not* animation-v)
(animated/set animation-v 1))}]
[animated/view
{:style (tooltip-style {:bottom-value (- (get @layout :height)
bottom-value)
:animation animation})
:pointer-events :box-none}
[animated/view
{:style (container-style)
:pointer-events :box-none}
(into [rn/view
{:style (content-style)
:pointer-events :box-none
:accessibility-label accessibility-label
:on-layout on-layout}]
children)
(when platform/ios?
;; NOTE(Ferossgp): Android does not show elevation for tooltip making it lost on white bg
[icons/icon :icons/tooltip-tip {:width 18
:height 8
:container-style {:elevation 3}
:color (:ui-background @colors/theme)}])]]])))
[icons/icon :icons/tooltip-tip
{:width 18
:height 8
:container-style {:elevation 3}
:color (:ui-background @colors/theme)}])]]])))

View File

@ -1,18 +1,18 @@
(ns quo.core
(:require [quo.components.animated-header :as animated-header]
[quo.components.bottom-sheet.view :as bottom-sheet]
[quo.components.button.view :as button]
[quo.components.controls.view :as controls]
[quo.components.header :as header]
[quo.components.list.footer :as list-footer]
[quo.components.list.header :as list-header]
[quo.components.list.index :as list-index]
[quo.components.list.item :as list-item]
[quo.components.safe-area :as safe-area]
[quo.components.separator :as separator]
[quo.components.text :as text]
[quo.components.text-input :as text-input]
[quo.components.tooltip :as tooltip]
[quo.components.text :as text]
[quo.components.button.view :as button]
[quo.components.list.header :as list-header]
[quo.components.list.footer :as list-footer]
[quo.components.list.item :as list-item]
[quo.components.list.index :as list-index]
[quo.components.controls.view :as controls]
[quo.components.bottom-sheet.view :as bottom-sheet]
[quo.components.separator :as separator]
[quo.design-system.colors :as colors]))
(def text text/text)

View File

@ -1,6 +1,6 @@
(ns quo.design-system.colors
(:require [reagent.core :as reagent]
[clojure.string :as string]))
(:require [clojure.string :as string]
[reagent.core :as reagent]))
;; Colors mapping from figma to code, note that theme is more extended and
;; one can follow the comments from the light theme to choose what to use in a component.
@ -19,14 +19,17 @@
(def light-theme
{:positive-01 "rgba(68,208,88,1)" ; Primary Positive, text, icons color
:positive-02 "rgba(78,188,96,0.1)" ; Secondary Positive, Supporting color for success illustrations
:positive-02 "rgba(78,188,96,0.1)" ; Secondary Positive, Supporting color for success
; illustrations
:positive-03 "rgba(78,188,96,1)" ; Lighter Positive, Supporting color for success illustrations
:negative-01 "rgba(255,45,85,1)" ; Primary Negative, text, icons color
:negative-02 "rgba(255,45,85,0.1))" ; Secondary Negative, Supporting color for errors illustrations
:negative-02 "rgba(255,45,85,0.1))" ; Secondary Negative, Supporting color for errors
; illustrations
:warning-01 "rgba(255, 202, 15, 1)"
:warning-02 "rgba(255, 202, 15, 0.1)"
:interactive-01 "rgba(67,96,223,1)" ; Accent color, buttons, own message, actions,active state
:interactive-02 "rgba(236,239,252,1)" ; Light Accent, buttons background, actions background, messages
:interactive-02 "rgba(236,239,252,1)" ; Light Accent, buttons background, actions background,
; messages
:interactive-03 "rgba(255,255,255,0.1)" ; Background for interactive above accent
:interactive-04 "rgba(147,155,161,1)" ; Disabled state
:ui-background "rgba(255,255,255,1)" ; Default view background
@ -85,12 +88,14 @@
(def theme (reagent/atom light-theme))
(defn get-color [color]
(defn get-color
[color]
(get @theme color))
;; LEGACY COLORS
(defn alpha [value opacity]
(defn alpha
[value opacity]
(if (string/starts-with? value "#")
(let [hex (string/replace value #"#" "")
r (js/parseInt (subs hex 0 2) 16)
@ -100,13 +105,15 @@
(let [rgb (string/split value #",")]
(str (string/join "," (butlast rgb)) "," opacity ")"))))
(def old-colors-mapping-light {:mentioned-background "#def6fc"
:mentioned-border "#b8ecf9"
:pin-background "#FFEECC"})
(def old-colors-mapping-light
{:mentioned-background "#def6fc"
:mentioned-border "#b8ecf9"
:pin-background "#FFEECC"})
(def old-colors-mapping-dark {:mentioned-background "#2a4046"
:mentioned-border "#2a4046"
:pin-background "#34232B"})
(def old-colors-mapping-dark
{:mentioned-background "#2a4046"
:mentioned-border "#2a4046"
:pin-background "#34232B"})
(def old-colors-mapping-themes {:dark old-colors-mapping-dark :light old-colors-mapping-light})
@ -127,7 +134,8 @@
;; BLACK
(def black (:text-01 light-theme)) ;; Used as the default text color
(def black-persist (:ui-background dark-theme)) ;; this doesn't with theme
(def black-transparent (:ui-02 light-theme)) ;; Used as background color for rounded button on dark background and as background color for containers like "Backup recovery phrase"
(def black-transparent (:ui-02 light-theme)) ;; Used as background color for rounded button on dark background and as background
;; color for containers like "Backup recovery phrase"
(def black-transparent-20 (:backdrop light-theme)) ; accounts divider
(def black-transparent-40 (:backdrop light-theme))
(def black-transparent-40-persist (:backdrop light-theme))
@ -136,7 +144,8 @@
(def black-transparent-86 (:ui-03 light-theme))
;; DARK GREY
(def gray (:text-02 light-theme)) ;; Dark grey, used as a background for a light foreground and as section header and secondary text color
(def gray (:text-02 light-theme)) ;; Dark grey, used as a background for a light foreground and as
;; section header and secondary text color
(def gray-transparent-10 (alpha gray 0.1))
(def gray-transparent-40 (alpha gray 0.4))
;; LIGHT GREY
@ -164,20 +173,22 @@
(def purple "#887af9")
(def orange "#FE8F59")
(def chat-colors ["#fa6565"
"#7cda00"
purple
"#51d0f0"
orange
"#d37ef4"])
(def chat-colors
["#fa6565"
"#7cda00"
purple
"#51d0f0"
orange
"#d37ef4"])
(def account-colors ["#9B832F"
"#D37EF4"
"#1D806F"
"#FA6565"
"#7CDA00"
purple
"#8B3131"])
(def account-colors
["#9B832F"
"#D37EF4"
"#1D806F"
"#FA6565"
"#7CDA00"
purple
"#8B3131"])
(def mention-incoming "#0DA4C9")
(def mention-outgoing "#9EE8FA")
@ -191,10 +202,12 @@
(def theme-type (reagent/atom :light))
(defn dark? []
(defn dark?
[]
(= :dark @theme-type))
(defn set-legacy-theme-type [type]
(defn set-legacy-theme-type
[type]
(when-not (= type @theme-type)
(let [old-colors-mapping-colors (get old-colors-mapping-themes type)]
(set! white (:ui-background @theme))

View File

@ -1,19 +1,22 @@
(ns quo.design-system.spacing)
(def spacing {:x-tiny 4
:tiny 8
:small 12
:base 16
:large 24
:x-large 32
:xx-large 48})
(def spacing
{:x-tiny 4
:tiny 8
:small 12
:base 16
:large 24
:x-large 32
:xx-large 48})
(def padding-horizontal (reduce-kv (fn [m k v]
(assoc m k {:padding-horizontal v}))
{}
spacing))
(def padding-horizontal
(reduce-kv (fn [m k v]
(assoc m k {:padding-horizontal v}))
{}
spacing))
(def padding-vertical (reduce-kv (fn [m k v]
(assoc m k {:padding-vertical v}))
{}
spacing))
(def padding-vertical
(reduce-kv (fn [m k v]
(assoc m k {:padding-vertical v}))
{}
spacing))

View File

@ -1,25 +1,32 @@
(ns quo.design-system.typography)
(def tiny {:font-size 10
:line-height 14})
(def tiny
{:font-size 10
:line-height 14})
(def x-small {:font-size 12
:line-height 16})
(def x-small
{:font-size 12
:line-height 16})
(def small {:font-size 13
:line-height 18})
(def small
{:font-size 13
:line-height 18})
(def base {:font-size 15
:line-height 22})
(def base
{:font-size 15
:line-height 22})
(def large {:font-size 17
:line-height 24})
(def large
{:font-size 17
:line-height 24})
(def x-large {:font-size 22
:line-height 30})
(def x-large
{:font-size 22
:line-height 30})
(def xx-large {:font-size 28
:line-height 38})
(def xx-large
{:font-size 28
:line-height 38})
(def font-regular {:font-family "Inter-Regular"}) ; 400

View File

@ -1,12 +1,17 @@
(ns quo.gesture-handler
(:require [oops.core :refer [oget]]
[reagent.core :as reagent]
(:require ["react-native-gesture-handler" :refer
(TapGestureHandler PanGestureHandler
LongPressGestureHandler
TouchableWithoutFeedback
TouchableOpacity
TouchableHighlight
State
NativeViewGestureHandler
FlatList
ScrollView)]
[oops.core :refer [oget]]
[quo.design-system.colors :as colors]
["react-native-gesture-handler"
:refer (TapGestureHandler PanGestureHandler LongPressGestureHandler
TouchableWithoutFeedback TouchableOpacity
TouchableHighlight State NativeViewGestureHandler
FlatList ScrollView)]))
[reagent.core :as reagent]))
(def flat-list-raw FlatList)
@ -30,9 +35,11 @@
(def touchable-highlight-class (reagent/adapt-react-class TouchableHighlight))
(defn touchable-highlight [props & children]
(into [touchable-highlight-class (merge {:underlay-color (:interactive-02 @colors/theme)}
props)]
(defn touchable-highlight
[props & children]
(into [touchable-highlight-class
(merge {:underlay-color (:interactive-02 @colors/theme)}
props)]
children))
(def touchable-opacity
@ -40,9 +47,10 @@
(def native-view-gesture-handler (reagent/adapt-react-class NativeViewGestureHandler))
(def states {:began (oget State "BEGAN")
:active (oget State "ACTIVE")
:cancelled (oget State "CANCELLED")
:end (oget State "END")
:failed (oget State "FAILED")
:undetermined (oget State "UNDETERMINED")})
(def states
{:began (oget State "BEGAN")
:active (oget State "ACTIVE")
:cancelled (oget State "CANCELLED")
:end (oget State "END")
:failed (oget State "FAILED")
:undetermined (oget State "UNDETERMINED")})

View File

@ -9,16 +9,17 @@
:notification-success "notificationSuccess"
:notification-warning "notificationWarning"
:notification-error "notificationError"
:clock-tick "clockTick" ; (Android only)
:context-click "contextClick" ; (Android only)
:keyboard-press "keyboardPress" ; (Android only)
:keyboard-release "keyboardRelease" ; (Android only)
:keyboard-tap "keyboardTap" ; (Android only)
:long-press "longPress" ; (Android only)
:text-handle-move "textHandleMove" ; (Android only)
:virtual-key "virtualKey" ; (Android only)
:virtual-key-release "virtualKeyRelease" ; (Android only)
})
:clock-tick "clockTick" ; (Android only)
:context-click "contextClick" ; (Android only)
:keyboard-press "keyboardPress" ; (Android only)
:keyboard-release "keyboardRelease" ; (Android only)
:keyboard-tap "keyboardTap" ; (Android only)
:long-press "longPress" ; (Android only)
:text-handle-move "textHandleMove" ; (Android only)
:virtual-key "virtualKey" ; (Android only)
:virtual-key-release "virtualKeyRelease" ; (Android only)
})
(defn trigger [method]
(defn trigger
[method]
(.trigger ^js react-native-haptic-feedback (get haptic-methods method)))

View File

@ -1,27 +1,29 @@
(ns quo.previews.bottom-sheet
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
[quo.previews.preview :as preview]
[quo.react-native :as rn]
[reagent.core :as reagent]))
(def descriptor [{:label "Show handle:"
:key :show-handle?
:type :boolean}
{:label "Backdrop dismiss:"
:key :backdrop-dismiss?
:type :boolean}
{:label "Disable drag:"
:key :disable-drag?
:type :boolean}
{:label "Android back cancel:"
:key :back-button-cancel
:type :boolean}
{:label "Scrollable:"
:key :scrollable
:type :boolean}])
(def descriptor
[{:label "Show handle:"
:key :show-handle?
:type :boolean}
{:label "Backdrop dismiss:"
:key :backdrop-dismiss?
:type :boolean}
{:label "Disable drag:"
:key :disable-drag?
:type :boolean}
{:label "Android back cancel:"
:key :back-button-cancel
:type :boolean}
{:label "Scrollable:"
:key :scrollable
:type :boolean}])
(defn cool-preview []
(defn cool-preview
[]
(let [state (reagent/atom {:show-handle? true
:backdrop-dismiss? true
:disable-drag? false
@ -29,38 +31,47 @@
visible (reagent/atom false)
scrollable (reagent/cursor state [:scrollable])]
(fn []
[rn/view {:margin-bottom 50
:padding 16}
[rn/view
{:margin-bottom 50
:padding 16}
[preview/customizer state descriptor]
[:<>
[rn/view {:style {:align-items :center
:padding 16}}
[rn/view
{:style {:align-items :center
:padding 16}}
[rn/touchable-opacity {:on-press #(reset! visible true)}
[rn/view {:style {:padding-horizontal 16
:padding-vertical 8
:border-radius 4
:background-color (:interactive-01 @colors/theme)}}
[rn/view
{:style {:padding-horizontal 16
:padding-vertical 8
:border-radius 4
:background-color (:interactive-01 @colors/theme)}}
[quo/text {:color :secondary-inverse}
(str "Open sheet: " @visible)]]]]
[quo/bottom-sheet (merge @state
{:visible? @visible
:on-cancel #(reset! visible false)})
[rn/view {:style {:height (if @scrollable 1200 400)
:justify-content :center
:align-items :center}}
[quo/bottom-sheet
(merge @state
{:visible? @visible
:on-cancel #(reset! visible false)})
[rn/view
{:style {:height (if @scrollable 1200 400)
:justify-content :center
:align-items :center}}
[rn/touchable-opacity {:on-press #(reset! visible false)}
[quo/text {:color :link} "Close"]]
[rn/touchable-opacity {:on-press #(swap! scrollable not)
:style {:padding-vertical 16}}
[rn/touchable-opacity
{:on-press #(swap! scrollable not)
:style {:padding-vertical 16}}
[quo/text {:color :link} "Toggle size"]]
[quo/text "Hello world!"]]]]])))
(defn preview []
(defn preview
[]
(fn []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]]))
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]]))

View File

@ -1,44 +1,46 @@
(ns quo.previews.button
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
[quo.previews.preview :as preview]
[quo.react-native :as rn]
[reagent.core :as reagent]))
(def descriptor [{:label "Type:"
:key :type
:type :select
:options [{:key :primary
:value "Primary"}
{:key :secondary
:value "Secondary"}
{:key :icon
:value "Icon"}]}
{:label "Theme:"
:key :theme
:type :select
:options [{:key :main
:value "Main"}
{:key :negative
:value "Negative"}
{:key :positive
:value "Positive"}
{:key :accent
:value "Accent"}]}
{:label "After icon:"
:key :after
:type :boolean}
{:label "Before icon:"
:key :before
:type :boolean}
{:label "Disabled:"
:key :disabled
:type :boolean}
{:label "Label"
:key :label
:type :text}])
(def descriptor
[{:label "Type:"
:key :type
:type :select
:options [{:key :primary
:value "Primary"}
{:key :secondary
:value "Secondary"}
{:key :icon
:value "Icon"}]}
{:label "Theme:"
:key :theme
:type :select
:options [{:key :main
:value "Main"}
{:key :negative
:value "Negative"}
{:key :positive
:value "Positive"}
{:key :accent
:value "Accent"}]}
{:label "After icon:"
:key :after
:type :boolean}
{:label "Before icon:"
:key :before
:type :boolean}
{:label "Disabled:"
:key :disabled
:type :boolean}
{:label "Label"
:key :label
:type :text}])
(defn cool-preview []
(defn cool-preview
[]
(let [state (reagent/atom {:label "Press Me"
:type :primary
:theme :main
@ -48,28 +50,36 @@
before (reagent/cursor state [:before])
after (reagent/cursor state [:after])]
(fn []
[rn/view {:margin-bottom 50
:padding 16}
[rn/view
{:margin-bottom 50
:padding 16}
[rn/view {:flex 1}
[preview/customizer state descriptor]]
[rn/view {:padding-vertical 16
:flex-direction :row
:justify-content :center}
[quo/button (merge (dissoc @state
:theme :before :after)
{:on-press #(println "Hello world!")}
(when @theme
{:theme @theme})
(when @before
{:before :main-icons/back})
(when @after
{:after :main-icons/next}))
[rn/view
{:padding-vertical 16
:flex-direction :row
:justify-content :center}
[quo/button
(merge (dissoc @state
:theme
:before
:after)
{:on-press #(println "Hello world!")}
(when @theme
{:theme @theme})
(when @before
{:before :main-icons/back})
(when @after
{:after :main-icons/next}))
@label]]])))
(defn preview-button []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]])
(defn preview-button
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:key-fn str}]])

View File

@ -1,44 +1,55 @@
(ns quo.previews.controls
(:require [reagent.core :as reagent]
[quo.core :as quo]
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.react-native :as rn]
[quo.design-system.colors :as colors]))
[reagent.core :as reagent]))
(defn preview []
(defn preview
[]
(let [switch-state (reagent/atom true)
radio-state (reagent/atom true)
checkbox-state (reagent/atom true)]
(fn []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/view {:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity {:style {:margin-vertical 10
:padding 10}
:on-press #(swap! switch-state not)}
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/view
{:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity
{:style {:margin-vertical 10
:padding 10}
:on-press #(swap! switch-state not)}
[quo/text (str "Switch state: " @switch-state)]]
[quo/switch {:value @switch-state
:on-change #(reset! switch-state %)}]]
[quo/switch
{:value @switch-state
:on-change #(reset! switch-state %)}]]
[rn/view {:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity {:style {:margin-vertical 10
:padding 10}
:on-press #(swap! radio-state not)}
[rn/view
{:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity
{:style {:margin-vertical 10
:padding 10}
:on-press #(swap! radio-state not)}
[quo/text (str "Radio state: " @radio-state)]]
[quo/radio {:value @radio-state
:on-change #(reset! radio-state %)}]]
[rn/view {:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity {:style {:margin-vertical 10
:padding 10}
:on-press #(swap! checkbox-state not)}
[quo/radio
{:value @radio-state
:on-change #(reset! radio-state %)}]]
[rn/view
{:padding 20
:flex-direction :row
:align-items :center
:justify-content :space-between}
[rn/touchable-opacity
{:style {:margin-vertical 10
:padding 10}
:on-press #(swap! checkbox-state not)}
[quo/text (str "Checkbox state: " @checkbox-state)]]
[quo/checkbox {:value @checkbox-state
:on-change #(reset! checkbox-state %)}]]])))
[quo/checkbox
{:value @checkbox-state
:on-change #(reset! checkbox-state %)}]]])))

View File

@ -1,44 +1,51 @@
(ns quo.previews.header
(:require [quo.core :as quo]
[quo.react-native :as rn]
[quo.design-system.colors :as colors])
[quo.design-system.colors :as colors]
[quo.react-native :as rn])
(:require-macros [quo.previews.preview :as preview]))
(def accessories [nil
[{:icon :main-icons/close
:on-press identity}]
[{:icon :main-icons/close
:on-press identity}
{:icon :main-icons/add
:on-press identity}]
[{:icon :main-icons/add
:on-press identity}
{:label "Text"
:on-press identity}]
[{:label "Text"
:on-press identity}]])
(def accessories
[nil
[{:icon :main-icons/close
:on-press identity}]
[{:icon :main-icons/close
:on-press identity}
{:icon :main-icons/add
:on-press identity}]
[{:icon :main-icons/add
:on-press identity}
{:label "Text"
:on-press identity}]
[{:label "Text"
:on-press identity}]])
(def all-props (preview/list-comp [left-accessories accessories
right-accessories accessories
title [nil "This is a title" "This is a very long super title"]
subtitle [nil "This is a subtitle"]
title-align [:left :center]]
{:left-accessories left-accessories
:right-accessories right-accessories
:title title
:subtitle subtitle
:title-align title-align}))
(def all-props
(preview/list-comp [left-accessories accessories
right-accessories accessories
title [nil "This is a title" "This is a very long super title"]
subtitle [nil "This is a subtitle"]
title-align [:left :center]]
{:left-accessories left-accessories
:right-accessories right-accessories
:title title
:subtitle subtitle
:title-align title-align}))
(defn render-item [props]
[rn/view {:border-bottom-color "#EEF2F5"
:border-bottom-width 2}
(defn render-item
[props]
[rn/view
{:border-bottom-color "#EEF2F5"
:border-bottom-width 2}
[quo/header props]])
(defn preview-header []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview-header
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:data all-props
:render-fn render-item
:key-fn str}]])

View File

@ -3,9 +3,11 @@
[quo.react-native :as rn]
[status-im.ui.components.icons.icons :as icons]))
(defn preview []
[rn/scroll-view {:background-color (:ui-background @colors/theme)
:flex 1}
(defn preview
[]
[rn/scroll-view
{:background-color (:ui-background @colors/theme)
:flex 1}
(for [i (keys icons/icons)]
[rn/view {:flex-direction :row}
[icons/icon (keyword i)]

View File

@ -1,87 +1,94 @@
(ns quo.previews.lists
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
[quo.previews.preview :as preview]
[quo.react-native :as rn]
[reagent.core :as reagent]))
(def all-props (preview/list-comp [] {}))
(defn avatar []
[rn/view {:border-radius 20
:width 40
:height 40
:justify-content :center
:align-items :center
:background-color :red}
[quo/text {:weight :bold
:size :large}
(defn avatar
[]
[rn/view
{:border-radius 20
:width 40
:height 40
:justify-content :center
:align-items :center
:background-color :red}
[quo/text
{:weight :bold
:size :large}
"T"]])
(defn icon-element [type]
(defn icon-element
[type]
(case type
:icon :main-icons/add-contact
:component [avatar]
nil))
(def descriptor [{:label "Accessory:"
:key :accessory
:type :select
:options [{:key :radio
:value "Radio"}
{:key :checkbox
:value "Checkbox"}
{:key :switch
:value "Switch"}
{:key :text
:value "Text"}
{:key :default
:value "Default"}]}
{:label "Size:"
:key :size
:type :select
:options [{:key :small
:value "Small"}
{:key :default
:value "Default"}]}
{:label "Icon:"
:key :icon
:type :select
:options [{:key :icon
:value "Icon"}
{:key :component
:value "Component"}]}
{:label "Theme:"
:key :theme
:type :select
:options [{:key :main
:value "Main"}
{:key :accent
:value "Accent"}
{:key :negative
:value "Negative"}
{:key :positive
:value "Positive"}]}
{:label "Selectable"
:key :selectable
:type :boolean}
{:label "Chevron"
:key :chevron
:type :boolean}
{:label "Disabled:"
:key :disabled
:type :boolean}
{:label "Title"
:key :title
:type :text}
{:label "Subtitle"
:key :subtitle
:type :text}])
(def descriptor
[{:label "Accessory:"
:key :accessory
:type :select
:options [{:key :radio
:value "Radio"}
{:key :checkbox
:value "Checkbox"}
{:key :switch
:value "Switch"}
{:key :text
:value "Text"}
{:key :default
:value "Default"}]}
{:label "Size:"
:key :size
:type :select
:options [{:key :small
:value "Small"}
{:key :default
:value "Default"}]}
{:label "Icon:"
:key :icon
:type :select
:options [{:key :icon
:value "Icon"}
{:key :component
:value "Component"}]}
{:label "Theme:"
:key :theme
:type :select
:options [{:key :main
:value "Main"}
{:key :accent
:value "Accent"}
{:key :negative
:value "Negative"}
{:key :positive
:value "Positive"}]}
{:label "Selectable"
:key :selectable
:type :boolean}
{:label "Chevron"
:key :chevron
:type :boolean}
{:label "Disabled:"
:key :disabled
:type :boolean}
{:label "Title"
:key :title
:type :text}
{:label "Subtitle"
:key :subtitle
:type :text}])
(defn render-item [_]
[rn/view {:style {:padding-vertical 24}}])
(defn render-item
[_]
[rn/view {:style {:padding-vertical 24}}])
(defn cool-preview []
(defn cool-preview
[]
(let [state (reagent/atom {:title "Title"
:active false})
icon (reagent/cursor state [:icon])
@ -92,19 +99,23 @@
[rn/view {:padding-horizontal 16}
[preview/customizer state descriptor]]
[rn/view {:padding-vertical 16}
[quo/list-item (merge (dissoc @state :active :selectable)
(when @selectable
{:active @active
:on-press #(swap! active not)})
{:accessory-text "Accessory"
:icon (icon-element @icon)})]]])))
[quo/list-item
(merge (dissoc @state :active :selectable)
(when @selectable
{:active @active
:on-press #(swap! active not)})
{:accessory-text "Accessory"
:icon (icon-element @icon)})]]])))
(defn preview []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])

View File

@ -1,75 +1,83 @@
(ns quo.previews.main
(:require [quo.previews.header :as header]
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.bottom-sheet :as bottom-sheet]
[quo.previews.button :as button]
[quo.previews.controls :as controls]
[quo.previews.header :as header]
[quo.previews.icons :as icons]
[quo.previews.lists :as lists]
[quo.previews.text :as text]
[quo.previews.text-input :as text-input]
[quo.previews.tooltip :as tooltip]
[quo.previews.button :as button]
[quo.previews.lists :as lists]
[quo.previews.bottom-sheet :as bottom-sheet]
[quo.previews.controls :as controls]
[quo.react-native :as rn]
[quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.theme :as theme]
[quo.previews.icons :as icons]
[re-frame.core :as re-frame]))
(def screens [{:name :texts
:insets {:top false}
:component text/preview-text}
{:name :tooltip
:insets {:top false}
:component tooltip/preview-tooltip}
{:name :text-input
:insets {:top false}
:component text-input/preview-text}
{:name :headers
:insets {:top false}
:component header/preview-header}
{:name :button
:insets {:top false}
:component button/preview-button}
{:name :lists
:instes {:top false}
:component lists/preview}
{:name :bottom-sheet
:insets {:top false}
:component bottom-sheet/preview}
{:name :controls
:insets {:top false}
:component controls/preview}
{:name :icons
:insets {:top false}
:component icons/preview}])
(def screens
[{:name :texts
:insets {:top false}
:component text/preview-text}
{:name :tooltip
:insets {:top false}
:component tooltip/preview-tooltip}
{:name :text-input
:insets {:top false}
:component text-input/preview-text}
{:name :headers
:insets {:top false}
:component header/preview-header}
{:name :button
:insets {:top false}
:component button/preview-button}
{:name :lists
:instes {:top false}
:component lists/preview}
{:name :bottom-sheet
:insets {:top false}
:component bottom-sheet/preview}
{:name :controls
:insets {:top false}
:component controls/preview}
{:name :icons
:insets {:top false}
:component icons/preview}])
(defn theme-switcher []
[rn/view {:style {:flex-direction :row
:margin-vertical 8
:border-radius 4
:background-color (:ui-01 @colors/theme)
:border-width 1
:border-color (:ui-02 @colors/theme)}}
[rn/touchable-opacity {:style {:padding 8
:flex 1
:justify-content :center
:align-items :center}
:on-press #(theme/set-theme :light)}
(defn theme-switcher
[]
[rn/view
{:style {:flex-direction :row
:margin-vertical 8
:border-radius 4
:background-color (:ui-01 @colors/theme)
:border-width 1
:border-color (:ui-02 @colors/theme)}}
[rn/touchable-opacity
{:style {:padding 8
:flex 1
:justify-content :center
:align-items :center}
:on-press #(theme/set-theme :light)}
[quo/text "Set light theme"]]
[rn/view {:width 1
:margin-vertical 4
:background-color (:ui-02 @colors/theme)}]
[rn/touchable-opacity {:style {:padding 8
:flex 1
:justify-content :center
:align-items :center}
:on-press #(theme/set-theme :dark)}
[rn/view
{:width 1
:margin-vertical 4
:background-color (:ui-02 @colors/theme)}]
[rn/touchable-opacity
{:style {:padding 8
:flex 1
:justify-content :center
:align-items :center}
:on-press #(theme/set-theme :dark)}
[quo/text "Set dark theme"]]])
(defn main-screen []
[rn/scroll-view {:flex 1
:padding-vertical 8
:padding-horizontal 16
:background-color (:ui-background @colors/theme)}
(defn main-screen
[]
[rn/scroll-view
{:flex 1
:padding-vertical 8
:padding-horizontal 16
:background-color (:ui-background @colors/theme)}
[theme-switcher]
[rn/view
(for [{:keys [name]} screens]
@ -78,6 +86,7 @@
[rn/view {:style {:padding-vertical 8}}
[quo/text (str "Preview " name)]]])]])
(def main-screens [{:name :quo-preview
:insets {:top false}
:component main-screen}])
(def main-screens
[{:name :quo-preview
:insets {:top false}
:component main-screen}])

View File

@ -1,15 +1,17 @@
(ns quo.previews.preview)
(defn descriptor->values [{:keys [key options type]}]
(defn descriptor->values
[{:keys [key options type]}]
{key (case type
:boolean [false true]
:text [nil "Just simple text"] ; NOTE(Ferossgp): add example with long text?
:select (mapv :key options))})
(defmacro list-comp [[binding seq-expr & bindings] body-expr]
(defmacro list-comp
[[binding seq-expr & bindings] body-expr]
(cond (not binding)
`(list ~body-expr)
:else
`(mapcat (fn [~binding] (list-comp ~bindings ~body-expr))
~seq-expr)))
~seq-expr)))

View File

@ -1,23 +1,26 @@
(ns quo.previews.preview
(:require [reagent.core :as reagent]
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.react-native :as rn]
[quo.core :as quo]
[quo.design-system.colors :as colors])
[reagent.core :as reagent])
(:require-macros quo.previews.preview))
(def container {:flex-direction :row
:padding-vertical 8
:flex 1
:align-items :center})
(def container
{:flex-direction :row
:padding-vertical 8
:flex 1
:align-items :center})
(defn touchable-style []
(defn touchable-style
[]
{:flex 1
:align-items :center
:justify-content :center
:padding-horizontal 16
:height 44})
(defn select-style []
(defn select-style
[]
{:flex 1
:flex-direction :row
:align-items :center
@ -28,7 +31,8 @@
:border-width 1
:border-color (:ui-02 @colors/theme)})
(defn select-option-style [selected]
(defn select-option-style
[selected]
(merge (select-style)
{:margin-vertical 8
:justify-content :center}
@ -36,23 +40,27 @@
{:background-color (:interactive-02 @colors/theme)}
{:background-color (:ui-01 @colors/theme)})))
(def label-style {:flex 0.4
:padding-right 8})
(def label-style
{:flex 0.4
:padding-right 8})
(defn label-view [state label]
(defn label-view
[state label]
[rn/view {:style label-style}
[quo/text
(when-let [label-color (:preview-label-color @state)]
{:style {:color label-color}})
label]])
(defn modal-container []
(defn modal-container
[]
{:flex 1
:justify-content :center
:padding-horizontal 24
:background-color "rgba(0,0,0,0.4)"})
(defn modal-view []
(defn modal-view
[]
{:padding-horizontal 16
:padding-vertical 8
:border-radius 8
@ -64,21 +72,25 @@
(let [state* (reagent/cursor state [key])]
[rn/view {:style container}
[label-view state label]
[rn/view {:style {:flex-direction :row
:flex 0.6
:border-radius 4
:background-color (:ui-01 @colors/theme)
:border-width 1
:border-color (:ui-02 @colors/theme)}}
[rn/touchable-opacity {:style (touchable-style)
:on-press #(reset! state* true)}
[quo/text {:color (if @state* :link :secondary)}
[rn/view
{:style {:flex-direction :row
:flex 0.6
:border-radius 4
:background-color (:ui-01 @colors/theme)
:border-width 1
:border-color (:ui-02 @colors/theme)}}
[rn/touchable-opacity
{:style (touchable-style)
:on-press #(reset! state* true)}
[quo/text {:color (if @state* :link :secondary)}
"True"]]
[rn/view {:width 1
:margin-vertical 4
:background-color (:ui-02 @colors/theme)}]
[rn/touchable-opacity {:style (touchable-style)
:on-press #(reset! state* false)}
[rn/view
{:width 1
:margin-vertical 4
:background-color (:ui-02 @colors/theme)}]
[rn/touchable-opacity
{:style (touchable-style)
:on-press #(reset! state* false)}
[quo/text {:color (if (not @state*) :link :secondary)}
"False"]]]]))
@ -88,20 +100,22 @@
[rn/view {:style container}
[label-view state label]
[rn/view {:style {:flex 0.6}}
[quo/text-input {:value @state*
:show-cancel false
:style {:border-radius 4
:border-width 1
:border-color (:ui-02 @colors/theme)}
:on-change-text #(do
(reset! state* %)
(reagent/flush))}]]]))
[quo/text-input
{:value @state*
:show-cancel false
:style {:border-radius 4
:border-width 1
:border-color (:ui-02 @colors/theme)}
:on-change-text #(do
(reset! state* %)
(reagent/flush))}]]]))
(defn value-for-key
[id v]
(:value (first (filter #(= (:key %) id) v))))
(defn customizer-select []
(defn customizer-select
[]
(let [open (reagent/atom nil)]
(fn [{:keys [label key state options]}]
(let [state* (reagent/cursor state [key])
@ -109,54 +123,63 @@
[rn/view {:style container}
[label-view state label]
[rn/view {:style {:flex 0.6}}
[rn/modal {:visible @open
:on-request-close #(reset! open false)
:statusBarTranslucent true
:transparent true
:animation :slide}
[rn/modal
{:visible @open
:on-request-close #(reset! open false)
:statusBarTranslucent true
:transparent true
:animation :slide}
[rn/view {:style (modal-container)}
[rn/view {:style (modal-view)}
[rn/scroll-view
(doall
(for [{:keys [key value]} options]
^{:key key}
[rn/touchable-opacity {:style (select-option-style (= @state* key))
:on-press #(do
(reset! open false)
(reset! state* key))}
[rn/touchable-opacity
{:style (select-option-style (= @state* key))
:on-press #(do
(reset! open false)
(reset! state* key))}
[quo/text {:color (if (= @state* key) :link :secondary)}
value]]))
[rn/view {:flex-direction :row}
[rn/touchable-opacity {:style (select-option-style false)
:on-press #(do
(reset! state* nil)
(reset! open false))}
[rn/touchable-opacity
{:style (select-option-style false)
:on-press #(do
(reset! state* nil)
(reset! open false))}
[quo/text "Clear"]]
[rn/view {:width 16}]
[rn/touchable-opacity {:style (select-option-style false)
:on-press #(reset! open false)}
[rn/touchable-opacity
{:style (select-option-style false)
:on-press #(reset! open false)}
[quo/text "Close"]]]]]]]
[rn/touchable-opacity {:style (select-style)
:on-press #(reset! open true)}
[rn/touchable-opacity
{:style (select-style)
:on-press #(reset! open true)}
(if selected
[quo/text {:color :link} selected]
[quo/text "Select option"])
[rn/view {:position :absolute
:right 16
:top 0
:bottom 0
:justify-content :center}
[rn/view
{:position :absolute
:right 16
:top 0
:bottom 0
:justify-content :center}
[quo/text "↓"]]]]]))))
(defn customizer [state descriptors]
[rn/view {:style {:flex 1}
:padding-horizontal 16}
(defn customizer
[state descriptors]
[rn/view
{:style {:flex 1}
:padding-horizontal 16}
(doall
(for [{:keys [key type]
:as desc} descriptors
:let [descriptor (merge desc
{:state state})]]
:as desc}
descriptors
:let [descriptor (merge desc
{:state state})]]
^{:key key}
[:<>
(case type

View File

@ -1,89 +1,99 @@
(ns quo.previews.text
(:require [reagent.core :as reagent]
(:require [quo.animated :as animated]
[quo.core :as quo]
[quo.animated :as animated]
[quo.react-native :as rn]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
[quo.previews.preview :as preview]
[quo.react-native :as rn]
[reagent.core :as reagent]))
(def all-props (preview/list-comp [size [:tiny :small :base :large :x-large :xx-large]
weight [:regular :medium :semi-bold :bold :monospace]]
{:weight weight
:size size}))
(def all-props
(preview/list-comp [size [:tiny :small :base :large :x-large :xx-large]
weight [:regular :medium :semi-bold :bold :monospace]]
{:weight weight
:size size}))
(def descriptor [{:label "Size:"
:key :size
:type :select
:options [{:key :tiny
:value "Tiny"}
{:key :small
:value "Small"}
{:key :base
:value "Base"}
{:key :large
:value "Large"}
{:key :x-large
:value "X-Large"}
{:key :xx-large
:value "XX-Large"}]}
{:label "Weight:"
:key :weight
:type :select
:options [{:key :regular
:value "Regular"}
{:key :medium
:value "Medium"}
{:key :semi-bold
:value "Semi-bold"}
{:key :bold
:value "Bold"}
{:key :monospace
:value "Monospace"}]}
{:label "Color:"
:key :color
:type :select
:options [{:key :main
:value "main"}
{:key :secondary
:value "secondary"}
{:key :secondary-inverse
:value "secondary-inverse"}
{:key :link
:value "link"}
{:key :negative
:value "negative"}
{:key :positive
:value "positive"}]}
{:label "Animated:"
:key :animated?
:type :boolean}])
(def descriptor
[{:label "Size:"
:key :size
:type :select
:options [{:key :tiny
:value "Tiny"}
{:key :small
:value "Small"}
{:key :base
:value "Base"}
{:key :large
:value "Large"}
{:key :x-large
:value "X-Large"}
{:key :xx-large
:value "XX-Large"}]}
{:label "Weight:"
:key :weight
:type :select
:options [{:key :regular
:value "Regular"}
{:key :medium
:value "Medium"}
{:key :semi-bold
:value "Semi-bold"}
{:key :bold
:value "Bold"}
{:key :monospace
:value "Monospace"}]}
{:label "Color:"
:key :color
:type :select
:options [{:key :main
:value "main"}
{:key :secondary
:value "secondary"}
{:key :secondary-inverse
:value "secondary-inverse"}
{:key :link
:value "link"}
{:key :negative
:value "negative"}
{:key :positive
:value "positive"}]}
{:label "Animated:"
:key :animated?
:type :boolean}])
(defn render-item [props]
[rn/view {:style {:padding-vertical 24
:padding-horizontal 16}}
(defn render-item
[props]
[rn/view
{:style {:padding-vertical 24
:padding-horizontal 16}}
[quo/text props
(str "Text size " props " number 0 1x2")]])
(defn cool-preview []
(defn cool-preview
[]
(let [state (reagent/atom {})
animation (animated/value 0)]
animation (animated/value 0)]
(fn []
[rn/view {:margin-bottom 50
:padding 16}
[rn/view
{:margin-bottom 50
:padding 16}
[animated/code {:exec (animated/set animation (animated/loop* {:duration 1000}))}]
[preview/customizer state descriptor]
[rn/view {:padding-vertical 16}
[quo/text (merge @state
(when (:animated? @state)
{:opacity animation}))
[quo/text
(merge @state
(when (:animated? @state)
{:opacity animation}))
"This is a demo text 1 2 0 2x2 0x0"]]])))
(defn preview-text []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview-text
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])

View File

@ -1,57 +1,62 @@
(ns quo.previews.text-input
(:require [reagent.core :as reagent]
[quo.core :as quo]
[quo.react-native :as rn]
(:require [quo.core :as quo]
[quo.design-system.colors :as colors]
[quo.previews.preview :as preview]))
[quo.previews.preview :as preview]
[quo.react-native :as rn]
[reagent.core :as reagent]))
(def all-props (preview/list-comp [multiline [false true]
label [nil "Input label"]
default-value [nil "Test initial value"]
placeholder [nil "Placeholder value"]
before [nil {:icon :main-icons/search}]
after [nil {:icon :main-icons/close}]
error [nil "Something went wrong!"]
secure [false true]
show-cancel [false true]]
{:label label
:default-value default-value
:placeholder placeholder
:multiline multiline
:before before
:after after
:error error
:show-cancel show-cancel
:secure-text-entry secure}))
(def all-props
(preview/list-comp [multiline [false true]
label [nil "Input label"]
default-value [nil "Test initial value"]
placeholder [nil "Placeholder value"]
before [nil {:icon :main-icons/search}]
after [nil {:icon :main-icons/close}]
error [nil "Something went wrong!"]
secure [false true]
show-cancel [false true]]
{:label label
:default-value default-value
:placeholder placeholder
:multiline multiline
:before before
:after after
:error error
:show-cancel show-cancel
:secure-text-entry secure}))
(def descriptor [{:label "Multiline:"
:key :multiline
:type :boolean}
{:label "Show cancel:"
:key :show-cancel
:type :boolean}
{:label "Secure:"
:key :secure-text-entry
:type :boolean}
{:label "After icon:"
:key :after
:type :boolean}
{:label "Before icon:"
:key :before
:type :boolean}
{:label "Show error:"
:key :error
:type :boolean}
{:label "Label"
:key :label
:type :text}])
(def descriptor
[{:label "Multiline:"
:key :multiline
:type :boolean}
{:label "Show cancel:"
:key :show-cancel
:type :boolean}
{:label "Secure:"
:key :secure-text-entry
:type :boolean}
{:label "After icon:"
:key :after
:type :boolean}
{:label "Before icon:"
:key :before
:type :boolean}
{:label "Show error:"
:key :error
:type :boolean}
{:label "Label"
:key :label
:type :text}])
(defn render-item [props]
[rn/view {:style {:padding-horizontal 16
:padding-vertical 24}}
(defn render-item
[props]
[rn/view
{:style {:padding-horizontal 16
:padding-vertical 24}}
[quo/text-input props]])
(defn cool-preview []
(defn cool-preview
[]
(let [state (reagent/atom {:secure false
:show-cancel false
:multiline false
@ -60,22 +65,27 @@
after (reagent/cursor state [:after])
error (reagent/cursor state [:error])]
(fn []
[rn/view {:margin-bottom 50
:padding 16}
[rn/view
{:margin-bottom 50
:padding 16}
[preview/customizer state descriptor]
[quo/text-input (merge @state
{:default-value nil
:placeholder "I'm a cool placeholder"
:before (when @before {:icon :main-icons/search})
:after (when @after {:icon :main-icons/close})
:error (when @error "Something went wrong!")})]])))
[quo/text-input
(merge @state
{:default-value nil
:placeholder "I'm a cool placeholder"
:before (when @before {:icon :main-icons/search})
:after (when @after {:icon :main-icons/close})
:error (when @error "Something went wrong!")})]])))
(defn preview-text []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview-text
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:header [cool-preview]
:data all-props
:render-fn render-item
:key-fn str}]])

View File

@ -1,30 +1,38 @@
(ns quo.previews.tooltip
(:require [quo.core :as quo]
[quo.react-native :as rn]
[quo.design-system.colors :as colors])
[quo.design-system.colors :as colors]
[quo.react-native :as rn])
(:require-macros [quo.previews.preview :as preview]))
(def all-props (preview/list-comp
[child [[quo/text {:size :small} "Simple text"]
[quo/text {:color :negative
:size :small}
"Error text"]
[rn/view {:width 100 :height 20 :background-color :red}]
[quo/text "Just text, but long. Officia autem est repellendus ad quia exercitationem veniam."]]]
child))
(def all-props
(preview/list-comp
[child [[quo/text {:size :small} "Simple text"]
[quo/text
{:color :negative
:size :small}
"Error text"]
[rn/view {:width 100 :height 20 :background-color :red}]
[quo/text
"Just text, but long. Officia autem est repellendus ad quia exercitationem veniam."]]]
child))
(defn render-item [children]
(defn render-item
[children]
[rn/view {:margin-vertical 50}
[rn/view {:height 20
:background-color "rgba(0,0,0,0.1)"}]
[rn/view
{:height 20
:background-color "rgba(0,0,0,0.1)"}]
[quo/tooltip {}
children]])
(defn preview-tooltip []
[rn/view {:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list {:flex 1
:keyboardShouldPersistTaps :always
:data all-props
:render-fn render-item
:key-fn str}]])
(defn preview-tooltip
[]
[rn/view
{:background-color (:ui-background @colors/theme)
:flex 1}
[rn/flat-list
{:flex 1
:keyboardShouldPersistTaps :always
:data all-props
:render-fn render-item
:key-fn str}]])

View File

@ -1,9 +1,11 @@
(ns quo.react)
(defmacro maybe-js-deps [deps]
(defmacro maybe-js-deps
[deps]
`(if ~deps (into-array ~deps) js/undefined))
(defmacro with-deps-check [[prev-deps] f deps]
(defmacro with-deps-check
[[prev-deps] f deps]
`(let [~prev-deps (quo.react/ref ~deps)]
(when (not= @~prev-deps ~deps)
(reset! ~prev-deps ~deps))

View File

@ -2,12 +2,12 @@
(:refer-clojure :exclude [ref])
(:require ["react" :as react]
[oops.core :refer [oget oset!]])
(:require-macros [quo.react :refer [with-deps-check
maybe-js-deps]]))
(:require-macros [quo.react :refer [with-deps-check maybe-js-deps]]))
(def create-ref react/createRef)
(defn current-ref [ref]
(defn current-ref
[ref]
(oget ref "current"))
;; Inspired from UIX, Rum and Rumext
@ -16,33 +16,35 @@
(oset! ref "current" val)
val)
(defn set-native-props [^js ref ^js props]
(defn set-native-props
[^js ref ^js props]
(when-let [curr-ref ^js (current-ref ref)]
(.setNativeProps curr-ref props)))
(deftype StateHook [value set-value]
cljs.core/IHash
(-hash [o] (goog/getUid o))
(-hash [o] (goog/getUid o))
cljs.core/IDeref
(-deref [_o]
value)
(-deref [_o]
value)
cljs.core/IReset
(-reset! [_o new-value]
(set-value new-value))
(-reset! [_o new-value]
(set-value new-value))
cljs.core/ISwap
(-swap! [_o f]
(set-value f))
(-swap! [_o f a]
(set-value #(f % a)))
(-swap! [_o f a b]
(set-value #(f % a b)))
(-swap! [_o f a b xs]
(set-value #(apply f % a b xs))))
(-swap! [_o f]
(set-value f))
(-swap! [_o f a]
(set-value #(f % a)))
(-swap! [_o f a b]
(set-value #(f % a b)))
(-swap! [_o f a b xs]
(set-value #(apply f % a b xs))))
(defn state [value]
(defn state
[value]
(let [[value set-value] (react/useState value)
sh (react/useMemo #(StateHook. value set-value) #js [])]
(react/useMemo (fn []
@ -51,31 +53,33 @@
sh)
#js [value set-value])))
(defn use-ref [val]
(defn use-ref
[val]
(let [ref (react/useRef val)]
(reify
cljs.core/IHash
(-hash [_] (goog/getUid ref))
cljs.core/IHash
(-hash [_] (goog/getUid ref))
cljs.core/IDeref
(-deref [_]
(current-ref ref))
cljs.core/IDeref
(-deref [_]
(current-ref ref))
cljs.core/IReset
(-reset! [_ new-value]
(set-ref-val! ref new-value))
cljs.core/IReset
(-reset! [_ new-value]
(set-ref-val! ref new-value))
cljs.core/ISwap
(-swap! [_ f]
(-reset! ref (f (current-ref ref))))
(-swap! [_ f a]
(-reset! ref (f (current-ref ref) a)))
(-swap! [_ f a b]
(-reset! ref (f (current-ref ref) a b)))
(-swap! [_ f a b xs]
(-reset! ref (apply f (current-ref ref) a b xs))))))
cljs.core/ISwap
(-swap! [_ f]
(-reset! ref (f (current-ref ref))))
(-swap! [_ f a]
(-reset! ref (f (current-ref ref) a)))
(-swap! [_ f a b]
(-reset! ref (f (current-ref ref) a b)))
(-swap! [_ f a b xs]
(-reset! ref (apply f (current-ref ref) a b xs))))))
(defn ref [value]
(defn ref
[value]
(let [vref (use-ref value)]
(react/useMemo (fn [] vref) #js [])))
@ -125,7 +129,8 @@
(def memo react/memo)
(defn get-children [^js children]
(defn get-children
[^js children]
(->> children
(react/Children.toArray)
(into [])))

View File

@ -1,13 +1,13 @@
(ns quo.react-native
(:require [reagent.core :as reagent]
(:require ["@react-native-community/hooks" :as hooks]
["react-native" :as rn]
["react-native-draggable-flatlist" :default DraggableFlatList]
["react-native-hole-view" :refer (RNHoleView)]
["react-native-navigation" :refer (Navigation)]
["rn-emoji-keyboard" :refer (EmojiKeyboard)]
[cljs-bean.core :as bean]
[quo.platform :as platform]
["react-native" :as rn]
["@react-native-community/hooks" :as hooks]
["react-native-navigation" :refer (Navigation)]
["react-native-hole-view" :refer (RNHoleView)]
["rn-emoji-keyboard" :refer (EmojiKeyboard)]
["react-native-draggable-flatlist" :default DraggableFlatList]))
[reagent.core :as reagent]))
(def hole-view (reagent/adapt-react-class RNHoleView))
@ -25,7 +25,9 @@
(def image (reagent/adapt-react-class (.-Image rn)))
(def text (reagent/adapt-react-class (.-Text ^js rn)))
(defn resolve-asset-source [uri] (js->clj (.resolveAssetSource ^js (.-Image ^js rn) uri) :keywordize-keys true))
(defn resolve-asset-source
[uri]
(js->clj (.resolveAssetSource ^js (.-Image ^js rn) uri) :keywordize-keys true))
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js rn)))
(def modal (reagent/adapt-react-class (.-Modal ^js rn)))
@ -42,11 +44,12 @@
(.then (.constants Navigation)
(fn [^js consts]
(reset! navigation-const {:top-bar-height (.-topBarHeight consts)
(reset! navigation-const {:top-bar-height (.-topBarHeight consts)
:bottom-tabs-height (.-bottomTabsHeight consts)
:status-bar-height (.-statusBarHeight consts)})))
:status-bar-height (.-statusBarHeight consts)})))
(defn keyboard-avoiding-view []
(defn keyboard-avoiding-view
[]
(let [this (reagent/current-component)
props (reagent/props this)]
(into [keyboard-avoiding-view-class
@ -66,7 +69,8 @@
(def pan-responder (.-PanResponder ^js rn))
(defn create-pan-responder [opts]
(defn create-pan-responder
[opts]
(.create ^js pan-responder (clj->js opts)))
(def animated (.-Animated rn))
@ -79,82 +83,99 @@
(def animated-view
(reagent/adapt-react-class (.-View ^js animated)))
(def ui-manager (.-UIManager ^js rn))
(def ui-manager (.-UIManager ^js rn))
(def layout-animation (.-LayoutAnimation ^js rn))
(def configure-next (.-configureNext ^js layout-animation))
(def create-animation (.-create ^js layout-animation))
(def layout-animation-presets {:ease-in-ease-out (-> ^js layout-animation .-Presets .-easeInEaseOut)
:linear (-> ^js layout-animation .-Presets .-linear)
:spring (-> ^js layout-animation .-Presets .-spring)})
(def layout-animation-presets
{:ease-in-ease-out (-> ^js layout-animation .-Presets .-easeInEaseOut)
:linear (-> ^js layout-animation .-Presets .-linear)
:spring (-> ^js layout-animation .-Presets .-spring)})
(def layout-animation-types {:spring (-> ^js layout-animation .-Types .-spring)
:linear (-> ^js layout-animation .-Types .-linear)
:ease-in-ease-out (-> ^js layout-animation .-Types .-easeInEaseOut)
:ease-in (-> ^js layout-animation .-Types .-easeIn)
:ease-out (-> ^js layout-animation .-Types .-easeOut)})
(def layout-animation-types
{:spring (-> ^js layout-animation .-Types .-spring)
:linear (-> ^js layout-animation .-Types .-linear)
:ease-in-ease-out (-> ^js layout-animation .-Types .-easeInEaseOut)
:ease-in (-> ^js layout-animation .-Types .-easeIn)
:ease-out (-> ^js layout-animation .-Types .-easeOut)})
(def layout-animation-properties {:opacity (-> ^js layout-animation .-Properties .-opacity)
:scale-x (-> ^js layout-animation .-Properties .-scaleX)
:scale-y (-> ^js layout-animation .-Properties .-scaleY)
:scale-xy (-> ^js layout-animation .-Properties .-scaleXY)})
(def layout-animation-properties
{:opacity (-> ^js layout-animation .-Properties .-opacity)
:scale-x (-> ^js layout-animation .-Properties .-scaleX)
:scale-y (-> ^js layout-animation .-Properties .-scaleY)
:scale-xy (-> ^js layout-animation .-Properties .-scaleXY)})
(def custom-animations {:ease-opacity-200 #js {:duration 200
:create #js {:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}
:update #js {:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}
:delete #js {:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}}})
(def custom-animations
{:ease-opacity-200 #js
{:duration 200
:create #js
{:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}
:update #js
{:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}
:delete #js
{:type (:ease-in-ease-out layout-animation-types)
:property (:opacity layout-animation-properties)}}})
(defonce enable-layout-animations
(when platform/android?
(.setLayoutAnimationEnabledExperimental ^js ui-manager true)))
(when platform/android?
(.setLayoutAnimationEnabledExperimental ^js ui-manager true)))
(def activity-indicator (reagent/adapt-react-class (.-ActivityIndicator ^js rn)))
;; Flat-list
(def ^:private rn-flat-list (reagent/adapt-react-class (.-FlatList ^js rn)))
(defn- wrap-render-fn [f render-data]
(defn- wrap-render-fn
[f render-data]
(fn [data]
(reagent/as-element [f (.-item ^js data) (.-index ^js data)
(.-separators ^js data) render-data
(.-isActive ^js data) (.-drag ^js data)])))
(defn- wrap-on-drag-end-fn [f]
(defn- wrap-on-drag-end-fn
[f]
(fn [data]
(f (.-from ^js data) (.-to ^js data) (.-data ^js data))))
(defn- wrap-key-fn [f]
(defn- wrap-key-fn
[f]
(fn [data index]
{:post [(some? %)]}
(f data index)))
(defn base-list-props
[{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn] :as props}]
(merge {:data (to-array data)}
(when key-fn {:keyExtractor (wrap-key-fn key-fn)})
(when render-fn {:renderItem (wrap-render-fn render-fn render-data)})
(when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))})
(when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))})
(when header {:ListHeaderComponent (reagent/as-element header)})
(when footer {:ListFooterComponent (reagent/as-element footer)})
(when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)})
(dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn)))
[{:keys [key-fn render-fn empty-component header footer separator data render-data on-drag-end-fn]
:as props}]
(merge
{:data (to-array data)}
(when key-fn {:keyExtractor (wrap-key-fn key-fn)})
(when render-fn {:renderItem (wrap-render-fn render-fn render-data)})
(when separator {:ItemSeparatorComponent (fn [] (reagent/as-element separator))})
(when empty-component {:ListEmptyComponent (fn [] (reagent/as-element empty-component))})
(when header {:ListHeaderComponent (reagent/as-element header)})
(when footer {:ListFooterComponent (reagent/as-element footer)})
(when on-drag-end-fn {:onDragEnd (wrap-on-drag-end-fn on-drag-end-fn)})
(dissoc props :data :header :footer :empty-component :separator :render-fn :key-fn :on-drag-end-fn)))
(defn flat-list [props]
(defn flat-list
[props]
[rn-flat-list (base-list-props props)])
(defn draggable-flat-list [props]
(defn draggable-flat-list
[props]
[rn-draggable-flatlist (base-list-props props)])
(defn animated-flat-list [props]
(defn animated-flat-list
[props]
[animated-flat-list-class (base-list-props props)])
;; Hooks
(defn use-window-dimensions []
(defn use-window-dimensions
[]
(let [window (rn/useWindowDimensions)]
{:font-scale (.-fontScale window)
:height (.-height ^js window)
@ -163,12 +184,14 @@
(def use-back-handler (.-useBackHandler hooks))
(defn use-keyboard []
(defn use-keyboard
[]
(let [kb (.useKeyboard hooks)]
{:keyboard-shown (.-keyboardShown ^js kb)
:keyboard-height (.-keyboardHeight ^js kb)}))
(defn use-layout []
(defn use-layout
[]
(let [{:keys [onLayout x y height width]} (bean/bean (.useLayout hooks))]
{:on-layout onLayout
:x x

View File

@ -4,13 +4,16 @@
(def theme (reagent/atom nil))
(defn dark? []
(defn dark?
[]
(= :dark @theme))
(defn get-theme []
(defn get-theme
[]
@theme)
(defn set-theme [value]
(defn set-theme
[value]
(reset! theme value)
(reset! colors/theme (case value
:dark colors/dark-theme

View File

@ -1,14 +1,15 @@
(ns quo2.components.avatars.account-avatar
(:require [react-native.core :as rn]
(:require [quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]
[quo2.theme :as theme]))
[quo2.theme :as theme]
[react-native.core :as rn]))
(def icon-color-value
{:dark (get-in colors/customization [:dark :purple])
:light (get-in colors/customization [:light :purple])})
(defn get-border-radius [size]
(defn get-border-radius
[size]
(case size
80 16
48 12
@ -16,7 +17,8 @@
24 8
20 6))
(defn get-inner-icon-sizes [size]
(defn get-inner-icon-sizes
[size]
(case size
80 36
48 24
@ -28,17 +30,18 @@
[{:keys [size icon]
:or {size 80
icon :main-icons/placeholder}}]
(let [icon-color (if (theme/dark?)
(:dark icon-color-value)
(:light icon-color-value))
(let [icon-color (if (theme/dark?)
(:dark icon-color-value)
(:light icon-color-value))
avatar-border-radius (get-border-radius size)
inner-icon-size (get-inner-icon-sizes size)]
[rn/view {:style {:width size
:background-color icon-color
:height size
:border-radius avatar-border-radius
:justify-content :center
:align-items :center}}
inner-icon-size (get-inner-icon-sizes size)]
[rn/view
{:style {:width size
:background-color icon-color
:height size
:border-radius avatar-border-radius
:justify-content :center
:align-items :center}}
[icons/icon icon
{:no-color true
:size inner-icon-size}]]))

View File

@ -1,41 +1,47 @@
(ns quo2.components.avatars.channel-avatar
(:require [quo2.foundations.colors :as colors]
[react-native.core :as rn]
[quo2.components.icon :as icons]
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.theme :as theme]))
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(defn channel-avatar [{:keys [big? locked? emoji-background-color emoji]}]
(defn channel-avatar
[{:keys [big? locked? emoji-background-color emoji]}]
(let [lock-exists? (some? locked?)
dark? (theme/dark?)]
[rn/view {:style {:width (if big? 32 24)
:height (if big? 32 24)
:border-radius (if big? 32 24)
:justify-content :center
:align-items :center
:background-color emoji-background-color}}
[rn/view {:style {:display :flex
:justify-content :center
:align-items :center}}
[text/text {:size (if big?
:paragraph-1
:label)} emoji]
[rn/view
{:style {:width (if big? 32 24)
:height (if big? 32 24)
:border-radius (if big? 32 24)
:justify-content :center
:align-items :center
:background-color emoji-background-color}}
[rn/view
{:style {:display :flex
:justify-content :center
:align-items :center}}
[text/text
{:size (if big?
:paragraph-1
:label)} emoji]
(when lock-exists?
[rn/view {:style {:position :absolute
:left (if big?
14
8)
:top (if big?
15
8)
:background-color (if dark?
colors/neutral-90
colors/white)
:border-radius 15
:padding 2}}
[icons/icon (if locked?
:main-icons/locked
:main-icons/unlocked)
[rn/view
{:style {:position :absolute
:left (if big?
14
8)
:top (if big?
15
8)
:background-color (if dark?
colors/neutral-90
colors/white)
:border-radius 15
:padding 2}}
[icons/icon
(if locked?
:main-icons/locked
:main-icons/unlocked)
{:color (if dark?
colors/neutral-40
colors/neutral-50)

View File

@ -1,6 +1,6 @@
(ns quo2.components.avatars.group-avatar
(:require [quo2.foundations.colors :as colors]
[quo2.components.icon :as icon]
(:require [quo2.components.icon :as icon]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(def sizes
@ -11,16 +11,20 @@
:medium 32
:large 48}})
;; TODO: this implementation does not support group display picture (can only display default group icon).
(defn group-avatar [_]
;; TODO: this implementation does not support group display picture (can only display default group
;; icon).
(defn group-avatar
[_]
(fn [{:keys [color size]}]
(let [container-size (get-in sizes [:container size])
icon-size (get-in sizes [:icon size])]
[rn/view {:width container-size
:height container-size
:align-items :center
:justify-content :center
:border-radius (/ container-size 2)
:background-color (colors/custom-color-by-theme color 50 60)}
[icon/icon :i/group {:size icon-size
:color colors/white-opa-70}]])))
[rn/view
{:width container-size
:height container-size
:align-items :center
:justify-content :center
:border-radius (/ container-size 2)
:background-color (colors/custom-color-by-theme color 50 60)}
[icon/icon :i/group
{:size icon-size
:color colors/white-opa-70}]])))

View File

@ -1,7 +1,7 @@
(ns quo2.components.avatars.icon-avatar
(:require [react-native.core :as rn]
(:require [quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]))
[react-native.core :as rn]))
(def sizes
{:big 48
@ -12,17 +12,19 @@
[{:keys [size icon color opacity]
:or {opacity 20}}]
(let [component-size (size sizes)
circle-color (colors/custom-color color 50 opacity)
icon-color (colors/custom-color-by-theme color 50 60)
icon-size (case size
:big 20
:medium 16
:small 12)]
[rn/view {:style {:width component-size
:height component-size
:border-radius component-size
:background-color circle-color
:justify-content :center
:align-items :center}}
[icons/icon icon {:size icon-size
:color icon-color}]]))
circle-color (colors/custom-color color 50 opacity)
icon-color (colors/custom-color-by-theme color 50 60)
icon-size (case size
:big 20
:medium 16
:small 12)]
[rn/view
{:style {:width component-size
:height component-size
:border-radius component-size
:background-color circle-color
:justify-content :center
:align-items :center}}
[icons/icon icon
{:size icon-size
:color icon-color}]]))

View File

@ -1,91 +1,96 @@
(ns quo2.components.avatars.user-avatar
(:require [react-native.core :as rn]
[react-native.fast-image :as fast-image]
(:require [clojure.string :refer [blank? split upper-case]]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]
[quo2.theme :refer [dark?]]
[clojure.string :refer [upper-case split blank?]]))
[react-native.core :as rn]
[react-native.fast-image :as fast-image]))
(def sizes {:big {:outer 80
:inner 72
:status-indicator 20
:status-indicator-border 4
:font-size :heading-1}
:medium {:outer 48
:inner 44
:status-indicator 12
:status-indicator-border 2
:font-size :paragraph-1}
:small {:outer 32
:inner 28
:status-indicator 12
:status-indicator-border 2
:font-size :paragraph-2}
:xs {:outer 24
:inner 24
:status-indicator 0
:status-indicator-border 0
:font-size :paragraph-2}
:xxs {:outer 20
:inner 20
:status-indicator 0
:status-indicator-border 0
:font-size :label}
:xxxs {:outer 16
:inner 16
:status-indicator 0
:status-indicator-border 0
:font-size :label}})
(def sizes
{:big {:outer 80
:inner 72
:status-indicator 20
:status-indicator-border 4
:font-size :heading-1}
:medium {:outer 48
:inner 44
:status-indicator 12
:status-indicator-border 2
:font-size :paragraph-1}
:small {:outer 32
:inner 28
:status-indicator 12
:status-indicator-border 2
:font-size :paragraph-2}
:xs {:outer 24
:inner 24
:status-indicator 0
:status-indicator-border 0
:font-size :paragraph-2}
:xxs {:outer 20
:inner 20
:status-indicator 0
:status-indicator-border 0
:font-size :label}
:xxxs {:outer 16
:inner 16
:status-indicator 0
:status-indicator-border 0
:font-size :label}})
(defn dot-indicator
[size status-indicator? online? ring? dark?]
(when status-indicator?
(let [dimensions (get-in sizes [size :status-indicator])
(let [dimensions (get-in sizes [size :status-indicator])
border-width (get-in sizes [size :status-indicator-border])
right (case size
:big 2
:medium 0
:small -2
0)
bottom (case size
:big (if ring?
-1
2)
:medium (if ring?
0
-2)
:small (if ring?
-2
-2)
0)]
[rn/view {:style {:background-color (if online?
colors/success-50
colors/neutral-40)
:width dimensions
:height dimensions
:border-width border-width
:border-radius dimensions
:border-color (if dark?
colors/neutral-100
colors/white)
:position :absolute
:bottom bottom
:right right}}])))
right (case size
:big 2
:medium 0
:small -2
0)
bottom (case size
:big (if ring?
-1
2)
:medium (if ring?
0
-2)
:small (if ring?
-2
-2)
0)]
[rn/view
{:style {:background-color (if online?
colors/success-50
colors/neutral-40)
:width dimensions
:height dimensions
:border-width border-width
:border-radius dimensions
:border-color (if dark?
colors/neutral-100
colors/white)
:position :absolute
:bottom bottom
:right right}}])))
(defn container-styling [inner-dimensions outer-dimensions]
{:width inner-dimensions
:position :absolute
:top (/ (- outer-dimensions inner-dimensions) 2)
:left (/ (- outer-dimensions inner-dimensions) 2)
:height inner-dimensions
(defn container-styling
[inner-dimensions outer-dimensions]
{:width inner-dimensions
:position :absolute
:top (/ (- outer-dimensions inner-dimensions) 2)
:left (/ (- outer-dimensions inner-dimensions) 2)
:height inner-dimensions
:border-radius inner-dimensions})
(defn container [inner-dimensions outer-dimensions & children]
[rn/view {:style (merge {:background-color (colors/custom-color-by-theme :turquoise 50 60)
:justify-content :center
:align-items :center}
(container-styling inner-dimensions outer-dimensions))}
(defn container
[inner-dimensions outer-dimensions & children]
[rn/view
{:style (merge {:background-color (colors/custom-color-by-theme :turquoise 50 60)
:justify-content :center
:align-items :center}
(container-styling inner-dimensions outer-dimensions))}
children])
(def small-sizes #{:xs :xxs :xxxs})
@ -112,9 +117,11 @@
identicon? (contains? identicon-sizes size)
small? (contains? small-sizes size)
outer-dimensions (get-in sizes [size :outer])
inner-dimensions (get-in sizes [size (if ring?
:inner
:outer)])
inner-dimensions (get-in sizes
[size
(if ring?
:inner
:outer)])
font-size (get-in sizes [size :font-size])
icon-text (if-not (or (blank? first-initial-letter)
(blank? initials))
@ -122,22 +129,26 @@
first-initial-letter
initials)
"")]
[rn/view {:accessibility-label :user-avatar
:style {:width outer-dimensions
:height outer-dimensions
:border-radius outer-dimensions}}
[rn/view
{:accessibility-label :user-avatar
:style {:width outer-dimensions
:height outer-dimensions
:border-radius outer-dimensions}}
(when (and ring? identicon?)
[icons/icon :i/identicon-ring {:size outer-dimensions
:no-color true}])
[icons/icon :i/identicon-ring
{:size outer-dimensions
:no-color true}])
(if profile-picture
;; display image
[fast-image/fast-image {:source {:uri profile-picture}
:style (container-styling inner-dimensions outer-dimensions)}]
[fast-image/fast-image
{:source {:uri profile-picture}
:style (container-styling inner-dimensions outer-dimensions)}]
;; else display initials
[container inner-dimensions outer-dimensions
^{:key :icon-text}
[text/text {:weight :semi-bold
:size font-size
:style {:color colors/white-opa-70}}
[text/text
{:weight :semi-bold
:size font-size
:style {:color colors/white-opa-70}}
(upper-case icon-text)]])
[dot-indicator size status-indicator? online? ring? (dark?)]]))

View File

@ -1,31 +1,35 @@
(ns quo2.components.avatars.wallet-user-avatar
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
(:require [clojure.string :as string]
[quo2.components.markdown.text :as text]
[clojure.string :as string]))
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(def circle-sizes {:small 20
:medium 32
:large 48
:x-large 80})
(def circle-sizes
{:small 20
:medium 32
:large 48
:x-large 80})
(def font-sizes {:small :label
:medium :paragraph-2
:large :paragraph-1
:x-large :heading-1})
(def font-sizes
{:small :label
:medium :paragraph-2
:large :paragraph-1
:x-large :heading-1})
(def font-weights {:small :medium
:medium :semi-bold
:large :semi-bold
:x-large :medium})
(def font-weights
{:small :medium
:medium :semi-bold
:large :semi-bold
:x-large :medium})
(defn wallet-user-avatar
"params, first name, last name, color, size
and if it's dark or not!"
[{:keys [f-name l-name color size] :or {f-name "John"
l-name "Doe"
color :red
size :x-large}}]
[{:keys [f-name l-name color size]
:or {f-name "John"
l-name "Doe"
color :red
size :x-large}}]
(let [circle-size (size circle-sizes)
small? (= size :small)
f-name-initial (-> f-name
@ -36,16 +40,18 @@
(subs 0 1))
circle-color (colors/custom-color color 50 20)
text-color (colors/custom-color-by-theme color 50 60)]
[rn/view {:style {:width circle-size
:height circle-size
:border-radius circle-size
:text-align :center
:justify-content :center
:align-items :center
:background-color circle-color}}
[text/text {:size (size font-sizes)
:weight (size font-weights)
:style {:color text-color}}
[rn/view
{:style {:width circle-size
:height circle-size
:border-radius circle-size
:text-align :center
:justify-content :center
:align-items :center
:background-color circle-color}}
[text/text
{:size (size font-sizes)
:weight (size font-weights)
:style {:color text-color}}
(if small?
(str f-name-initial)
(str f-name-initial l-name-initial))]]))

View File

@ -1,142 +1,143 @@
(ns quo2.components.buttons.button
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
(:require [quo2.components.icon :as quo2.icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[reagent.core :as reagent]
[quo2.components.icon :as quo2.icons]))
[react-native.core :as rn]
[reagent.core :as reagent]))
(def themes
{:light {:primary {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/primary-50
:pressed colors/primary-60
:disabled colors/primary-50}}
:secondary {:icon-color colors/primary-50
:label-color colors/primary-50
:background-color {:default colors/primary-50-opa-20
:pressed colors/primary-50-opa-40
:disabled colors/primary-50-opa-20}}
:grey {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:default colors/neutral-10
:pressed colors/neutral-20
:disabled colors/neutral-10}}
:dark-grey {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:default colors/neutral-20
:pressed colors/neutral-30
:disabled colors/neutral-20}}
:outline {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:border-color {:default colors/neutral-20
:pressed colors/neutral-40
:disabled colors/neutral-20}}
:ghost {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:pressed colors/neutral-10}}
:danger {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/danger-50
:pressed colors/danger-60
:disabled colors/danger-50}}
:positive {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/success-50
:pressed colors/success-60
:disabled colors/success-50-opa-30}}
:photo-bg {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:background-color {:default colors/white-opa-40
:pressed colors/white-opa-50
:disabled colors/white-opa-40}}
:blur-bg {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:background-color {:default colors/neutral-80-opa-5
:pressed colors/neutral-80-opa-10
:disabled colors/neutral-80-opa-5}}
:blur-bg-outline {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:border-color {:default colors/neutral-80-opa-10
:pressed colors/neutral-80-opa-20
:disabled colors/neutral-80-opa-10}}
:shell {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/neutral-95
:pressed colors/neutral-95
:disabled colors/neutral-95}}}
:dark {:primary {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/primary-60
:pressed colors/primary-50
:disabled colors/primary-60}}
:secondary {:icon-color colors/primary-50
:label-color colors/primary-50
:background-color {:default colors/primary-50-opa-20
:pressed colors/primary-50-opa-30
:disabled colors/primary-50-opa-20}}
:grey {:icon-color colors/white
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:default colors/neutral-80
:pressed colors/neutral-60
:disabled colors/neutral-80}}
:dark-grey {:icon-color colors/white
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:default colors/neutral-70
:pressed colors/neutral-60
:disabled colors/neutral-70}}
:outline {:icon-color colors/neutral-40
:icon-secondary-color colors/neutral-40
:label-color colors/white
:border-color {:default colors/neutral-70
:pressed colors/neutral-60
:disabled colors/neutral-70}}
:ghost {:icon-color colors/neutral-40
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:pressed colors/neutral-80}}
:danger {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/danger-60
:pressed colors/danger-50
:disabled colors/danger-60}}
:positive {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/success-60
:pressed colors/success-50
:disabled colors/success-60-opa-30}}
:photo-bg {:icon-color colors/white
:icon-secondary-color colors/neutral-30
:label-color colors/white
:background-color {:default colors/neutral-80-opa-40
:pressed colors/neutral-80-opa-50
:disabled colors/neutral-80-opa-40}}
:blur-bg {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
:background-color {:default colors/white-opa-5
:pressed colors/white-opa-10
:disabled colors/white-opa-5}}
:blur-bg-outline {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
:border-color {:default colors/white-opa-10
:pressed colors/white-opa-20
:disabled colors/white-opa-5}}
:shell {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/neutral-95}}}})
{:light {:primary {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/primary-50
:pressed colors/primary-60
:disabled colors/primary-50}}
:secondary {:icon-color colors/primary-50
:label-color colors/primary-50
:background-color {:default colors/primary-50-opa-20
:pressed colors/primary-50-opa-40
:disabled colors/primary-50-opa-20}}
:grey {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:default colors/neutral-10
:pressed colors/neutral-20
:disabled colors/neutral-10}}
:dark-grey {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:default colors/neutral-20
:pressed colors/neutral-30
:disabled colors/neutral-20}}
:outline {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:border-color {:default colors/neutral-20
:pressed colors/neutral-40
:disabled colors/neutral-20}}
:ghost {:icon-color colors/neutral-50
:icon-secondary-color colors/neutral-50
:label-color colors/neutral-100
:background-color {:pressed colors/neutral-10}}
:danger {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/danger-50
:pressed colors/danger-60
:disabled colors/danger-50}}
:positive {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/success-50
:pressed colors/success-60
:disabled colors/success-50-opa-30}}
:photo-bg {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:background-color {:default colors/white-opa-40
:pressed colors/white-opa-50
:disabled colors/white-opa-40}}
:blur-bg {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:background-color {:default colors/neutral-80-opa-5
:pressed colors/neutral-80-opa-10
:disabled colors/neutral-80-opa-5}}
:blur-bg-outline {:icon-color colors/neutral-100
:icon-secondary-color colors/neutral-80-opa-40
:label-color colors/neutral-100
:border-color {:default colors/neutral-80-opa-10
:pressed colors/neutral-80-opa-20
:disabled colors/neutral-80-opa-10}}
:shell {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/neutral-95
:pressed colors/neutral-95
:disabled colors/neutral-95}}}
:dark {:primary {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/primary-60
:pressed colors/primary-50
:disabled colors/primary-60}}
:secondary {:icon-color colors/primary-50
:label-color colors/primary-50
:background-color {:default colors/primary-50-opa-20
:pressed colors/primary-50-opa-30
:disabled colors/primary-50-opa-20}}
:grey {:icon-color colors/white
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:default colors/neutral-80
:pressed colors/neutral-60
:disabled colors/neutral-80}}
:dark-grey {:icon-color colors/white
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:default colors/neutral-70
:pressed colors/neutral-60
:disabled colors/neutral-70}}
:outline {:icon-color colors/neutral-40
:icon-secondary-color colors/neutral-40
:label-color colors/white
:border-color {:default colors/neutral-70
:pressed colors/neutral-60
:disabled colors/neutral-70}}
:ghost {:icon-color colors/neutral-40
:icon-secondary-color colors/neutral-40
:label-color colors/white
:background-color {:pressed colors/neutral-80}}
:danger {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/danger-60
:pressed colors/danger-50
:disabled colors/danger-60}}
:positive {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/success-60
:pressed colors/success-50
:disabled colors/success-60-opa-30}}
:photo-bg {:icon-color colors/white
:icon-secondary-color colors/neutral-30
:label-color colors/white
:background-color {:default colors/neutral-80-opa-40
:pressed colors/neutral-80-opa-50
:disabled colors/neutral-80-opa-40}}
:blur-bg {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
:background-color {:default colors/white-opa-5
:pressed colors/white-opa-10
:disabled colors/white-opa-5}}
:blur-bg-outline {:icon-color colors/white
:icon-secondary-color colors/white-opa-40
:label-color colors/white
:border-color {:default colors/white-opa-10
:pressed colors/white-opa-20
:disabled colors/white-opa-5}}
:shell {:icon-color colors/white
:label-color colors/white
:background-color {:default colors/neutral-95}}}})
(defn shape-style-container [type icon size]
(defn shape-style-container
[type icon size]
{:border-radius (if (and icon (#{:primary :secondary :danger} type))
24
(case size
@ -145,22 +146,43 @@
32 10
24 8))})
(defn style-container [type size disabled background-color border-color icon above width before after]
(defn style-container
[type size disabled background-color border-color icon above width before after]
(merge {:height size
:align-items :center
:justify-content :center
:flex-direction (if above :column :row)
:background-color background-color
:padding-horizontal (when-not (or icon before after)
(case size 56 16 40 16 32 12 24 8))
(case size
56 16
40 16
32 12
24 8))
:padding-left (when-not (or icon before)
(case size 56 16 40 16 32 12 24 8))
(case size
56 16
40 16
32 12
24 8))
:padding-right (when-not (or icon after)
(case size 56 16 40 16 32 12 24 8))
(case size
56 16
40 16
32 12
24 8))
:padding-top (when-not (or icon before after)
(case size 56 0 40 9 32 5 24 3))
(case size
56 0
40 9
32 5
24 3))
:padding-bottom (when-not (or icon before after)
(case size 56 0 40 9 32 5 24 4))}
(case size
56 0
40 9
32 5
24 4))}
(shape-style-container type icon size)
(when width
{:width width})
@ -172,7 +194,8 @@
(when disabled
{:opacity 0.3})))
(defn community-themed? [type community-color]
(defn community-themed?
[type community-color]
(and (= type :community) (string? community-color)))
(defn button
@ -192,85 +215,97 @@
[button {:icon true} :main-icons/close-circle]"
[_ _]
(let [pressed (reagent/atom false)]
(fn [{:keys [on-press disabled type size community-color community-text-color before after above width
override-theme override-background-color
on-long-press accessibility-label icon icon-no-color style test-ID]
:or {type :primary
size 40}}
children]
(fn
[{:keys [on-press disabled type size community-color community-text-color before after above
width
override-theme override-background-color
on-long-press accessibility-label icon icon-no-color style test-ID]
:or {type :primary
size 40}}
children]
(let [{:keys [icon-color icon-secondary-color background-color label-color border-color]}
(get-in themes [(or
override-theme
(theme/get-theme)) type])
state (cond disabled :disabled @pressed :pressed :else :default)
icon-size (when (= 24 size) 12)
(get-in themes
[(or
override-theme
(theme/get-theme)) type])
state (cond disabled :disabled
@pressed :pressed
:else :default)
icon-size (when (= 24 size) 12)
icon-secondary-color (or icon-secondary-color icon-color)]
[rn/touchable-without-feedback (merge {:test-ID test-ID
:disabled disabled
:accessibility-label accessibility-label}
(when on-press
{:on-press (fn []
(on-press))})
(when on-long-press
{:on-long-press (fn []
(on-long-press))})
{:on-press-in (fn []
(reset! pressed true))}
{:on-press-out (fn []
(reset! pressed nil))})
[rn/touchable-without-feedback
(merge {:test-ID test-ID
:disabled disabled
:accessibility-label accessibility-label}
(when on-press
{:on-press (fn []
(on-press))})
(when on-long-press
{:on-long-press (fn []
(on-long-press))})
{:on-press-in (fn []
(reset! pressed true))}
{:on-press-out (fn []
(reset! pressed nil))})
[rn/view {:style (merge
(shape-style-container type icon size)
{:background-color
(if (= state :pressed)
(colors/theme-colors colors/neutral-100 colors/white)
:transparent)}
style)}
[rn/view {:style (merge
(style-container
type
size
disabled
(or override-background-color (get background-color state))
(get border-color state)
icon
above
width
before
after)
(when
(community-themed? type community-color)
(merge
{:background-color community-color}
(when (= state :pressed) {:opacity 0.9}))))}
[rn/view
{:style (merge
(shape-style-container type icon size)
{:background-color
(if (= state :pressed)
(colors/theme-colors colors/neutral-100 colors/white)
:transparent)}
style)}
[rn/view
{:style (merge
(style-container
type
size
disabled
(or override-background-color (get background-color state))
(get border-color state)
icon
above
width
before
after)
(when
(community-themed? type community-color)
(merge
{:background-color community-color}
(when (= state :pressed) {:opacity 0.9}))))}
(when above
[rn/view
[quo2.icons/icon above {:container-style {:margin-bottom 2}
:color icon-secondary-color
:size icon-size}]])
[quo2.icons/icon above
{:container-style {:margin-bottom 2}
:color icon-secondary-color
:size icon-size}]])
(when before
[rn/view
[quo2.icons/icon before {:container-style {:margin-left (if (= size 40) 12 8)
:margin-right 4}
:color icon-secondary-color
:size icon-size}]])
[quo2.icons/icon before
{:container-style {:margin-left (if (= size 40) 12 8)
:margin-right 4}
:color icon-secondary-color
:size icon-size}]])
[rn/view
(cond
(or icon icon-no-color)
[quo2.icons/icon children {:color icon-color
:no-color icon-no-color
:size icon-size}]
[quo2.icons/icon children
{:color icon-color
:no-color icon-no-color
:size icon-size}]
(string? children)
[text/text {:size (when (#{56 24} size) :paragraph-2)
:weight :medium
:number-of-lines 1
:style {:color (if
(and
(community-themed? type community-color)
(string? community-text-color))
community-text-color
label-color)}}
[text/text
{:size (when (#{56 24} size) :paragraph-2)
:weight :medium
:number-of-lines 1
:style {:color (if
(and
(community-themed? type community-color)
(string? community-text-color))
community-text-color
label-color)}}
children]
@ -278,7 +313,8 @@
children)]
(when after
[rn/view
[quo2.icons/icon after {:container-style {:margin-left 4
:margin-right (if (= size 40) 12 8)}
:color icon-secondary-color
:size icon-size}]])]]]))))
[quo2.icons/icon after
{:container-style {:margin-left 4
:margin-right (if (= size 40) 12 8)}
:color icon-secondary-color
:size icon-size}]])]]]))))

View File

@ -1,11 +1,12 @@
(ns quo2.components.buttons.dynamic-button
(:require [react-native.core :as rn]
[reagent.core :as reagent]
[quo2.components.icon :as icon]
(:require [quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.components.markdown.text :as text]))
[react-native.core :as rn]
[reagent.core :as reagent]))
(defn- get-button-color [type pressed? customization-color]
(defn- get-button-color
[type pressed? customization-color]
(if (#{:jump-to :mention} type)
(if pressed?
(colors/custom-color-by-theme customization-color 60 50)
@ -14,12 +15,14 @@
(colors/theme-colors colors/neutral-80-opa-80 colors/white-opa-80)
(colors/theme-colors colors/neutral-80-opa-70 colors/white-opa-70))))
(defn- get-icon-and-text-color [type]
(defn- get-icon-and-text-color
[type]
(if (#{:jump-to :mention} type)
colors/white
(colors/theme-colors colors/white colors/neutral-100)))
(defn- icon-view [type]
(defn- icon-view
[type]
[icon/icon
(case type
:jump-to :i/jump-to
@ -67,32 +70,34 @@
:active-opacity 1
:style {:padding 5}
:accessibility-label type}
[rn/view {:style (merge
{:flex-direction :row
:height 24
:border-radius 12
:background-color (get-button-color type @pressed? (or customization-color :primary))}
style)}
[rn/view
{:style (merge
{:flex-direction :row
:height 24
:border-radius 12
:background-color (get-button-color type @pressed? (or customization-color :primary))}
style)}
(when (#{:mention :search :search-with-label :bottom} type)
[icon-view type])
(when (#{:jump-to :mention :notification-down :notification-up :search-with-label} type)
[text/text {:weight :medium
:size :paragraph-2
:style {:color (get-icon-and-text-color type)
:margin-top 2.5
:margin-bottom 3.5
:margin-left (case type
:jump-to 8
:mention 0
:notification-down 8
:notification-up 8
:search-with-label 0)
:margin-right (case type
:jump-to 0
:mention 8
:notification-down 0
:notification-up 0
:search-with-label 8)}}
[text/text
{:weight :medium
:size :paragraph-2
:style {:color (get-icon-and-text-color type)
:margin-top 2.5
:margin-bottom 3.5
:margin-left (case type
:jump-to 8
:mention 0
:notification-down 8
:notification-up 8
:search-with-label 0)
:margin-right (case type
:jump-to 0
:mention 8
:notification-down 0
:notification-up 0
:search-with-label 8)}}
(case type
:jump-to label
:search-with-label label

View File

@ -3,35 +3,36 @@
[cljs-bean.core :as bean]
[clojure.string :as string]
[oops.core :as oops]
[react-native.core :as rn]
[quo2.theme :as theme]
[quo2.components.buttons.button :as button]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[reagent.core :as reagent]
[quo2.theme :as theme]
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]
[react-native.masked-view :as masked-view]
[react-native.syntax-highlighter :as highlighter]))
[react-native.syntax-highlighter :as highlighter]
[reagent.core :as reagent]))
;; Example themes:
;; https://github.com/react-syntax-highlighter/react-syntax-highlighter/tree/master/src/styles/hljs
(def ^:private themes
{:light {:hljs-comment {:color colors/neutral-40},
:hljs-title {:color (colors/custom-color :blue 50)},
:hljs-keyword {:color (colors/custom-color :green 50)},
:hljs-string {:color (colors/custom-color :turquoise 50)},
{:light {:hljs-comment {:color colors/neutral-40}
:hljs-title {:color (colors/custom-color :blue 50)}
:hljs-keyword {:color (colors/custom-color :green 50)}
:hljs-string {:color (colors/custom-color :turquoise 50)}
:hljs-literal {:color (colors/custom-color :turquoise 50)}
:hljs-number {:color (colors/custom-color :turquoise 50)}
:line-number {:color colors/neutral-40}}
:dark {:hljs-comment {:color colors/neutral-60},
:hljs-title {:color (colors/custom-color :blue 60)},
:hljs-keyword {:color (colors/custom-color :green 60)},
:hljs-string {:color (colors/custom-color :turquoise 60)},
:dark {:hljs-comment {:color colors/neutral-60}
:hljs-title {:color (colors/custom-color :blue 60)}
:hljs-keyword {:color (colors/custom-color :green 60)}
:hljs-string {:color (colors/custom-color :turquoise 60)}
:hljs-literal {:color (colors/custom-color :turquoise 60)}
:hljs-number {:color (colors/custom-color :turquoise 60)}
:line-number {:color colors/neutral-40}}})
(defn- text-style [class-names]
(defn- text-style
[class-names]
(->> class-names
(map keyword)
(reduce #(merge %1 (get-in themes [(theme/get-theme) %2]))
@ -43,35 +44,41 @@
;; layout.
:line-height 18})))
(defn- render-nodes [nodes]
(defn- render-nodes
[nodes]
(map (fn [{:keys [children value] :as node}]
;; Node can have :children or a :value.
(if children
(into [text/text {:weight :code
:size :paragraph-2
:style (text-style (get-in node [:properties :className]))}]
(into [text/text
{:weight :code
:size :paragraph-2
:style (text-style (get-in node [:properties :className]))}]
(render-nodes children))
;; Remove newlines as we already render each line separately.
(-> value string/trim-newline)))
nodes))
(defn- code-block [{:keys [rows line-number-width]}]
(defn- code-block
[{:keys [rows line-number-width]}]
[into [:<>]
(->> rows
(render-nodes)
;; Line numbers
(map-indexed (fn [idx row]
(conj [rn/view {:style {:flex-direction :row}}
[rn/view {:style {:width line-number-width
;; 8+12 margin
:margin-right 20}}
[text/text {:weight :code
:size :paragraph-2
:style (text-style ["line-number"])}
[rn/view
{:style {:width line-number-width
;; 8+12 margin
:margin-right 20}}
[text/text
{:weight :code
:size :paragraph-2
:style (text-style ["line-number"])}
(inc idx)]]]
row))))])
(defn- native-renderer []
(defn- native-renderer
[]
(let [text-height (reagent/atom nil)]
(fn [{:keys [rows max-lines on-copy-press]}]
(let [background-color (colors/theme-colors
@ -89,67 +96,81 @@
max-line-digits (-> rows count (min max-rows) str count)
;; ~ 9 is char width, 18 is width used in Figma.
line-number-width (* font-scale (max 18 (* 9 max-line-digits)))
max-text-height (some-> max-lines (* font-scale 18)) ;; 18 is font's line height.
max-text-height (some-> max-lines
(* font-scale 18)) ;; 18 is font's line height.
truncated? (and max-text-height (< max-text-height @text-height))
maybe-mask-wrapper (if truncated?
[masked-view/masked-view
{:mask-element
(reagent/as-element
[linear-gradient/linear-gradient {:colors ["black" "transparent"]
:locations [0.75 1]
:style {:flex 1}}])}]
[linear-gradient/linear-gradient
{:colors ["black" "transparent"]
:locations [0.75 1]
:style {:flex 1}}])}]
[:<>])]
[rn/view {:style {:overflow :hidden
:padding 8
:background-color background-color
:border-color border-color
:border-width 1
:border-radius 8
;; Hide on intial render to avoid flicker when mask-wrapper is shown.
:opacity (if @text-height 1 0)}}
[rn/view
{:style {:overflow :hidden
:padding 8
:background-color background-color
:border-color border-color
:border-width 1
:border-radius 8
;; Hide on intial render to avoid flicker when mask-wrapper is shown.
:opacity (if @text-height 1 0)}}
;; Line number container
[rn/view {:style {:position :absolute
:bottom 0
:top 0
:left 0
:width (+ line-number-width 8 8)
:background-color background-color-left
:border-right-color border-color
:border-right-width 1}}]
(conj maybe-mask-wrapper [rn/view {:max-height max-text-height}
[rn/view {:on-layout (fn [evt]
(let [height (oops/oget evt "nativeEvent.layout.height")]
(reset! text-height height)))}
[code-block {:rows (take max-rows rows)
:line-number-width line-number-width}]]])
[rn/view
{:style {:position :absolute
:bottom 0
:top 0
:left 0
:width (+ line-number-width 8 8)
:background-color background-color-left
:border-right-color border-color
:border-right-width 1}}]
(conj maybe-mask-wrapper
[rn/view {:max-height max-text-height}
[rn/view
{:on-layout (fn [evt]
(let [height (oops/oget evt "nativeEvent.layout.height")]
(reset! text-height height)))}
[code-block
{:rows (take max-rows rows)
:line-number-width line-number-width}]]])
;; Copy button
[rn/view {:style {:position :absolute
:bottom 8
:right 8}}
[button/button {:icon true
:type :grey
:size 24
:on-press on-copy-press}
[rn/view
{:style {:position :absolute
:bottom 8
:right 8}}
[button/button
{:icon true
:type :grey
:size 24
:on-press on-copy-press}
:main-icons/copy]]]))))
(defn- wrap-renderer-fn [f {:keys [max-lines on-copy-press]}]
(defn- wrap-renderer-fn
[f {:keys [max-lines on-copy-press]}]
(fn [^js props]
(reagent/as-element [:f> f {:rows (.-rows props)
:max-lines max-lines
:on-copy-press on-copy-press}])))
(reagent/as-element [:f> f
{:rows (.-rows props)
:max-lines max-lines
:on-copy-press on-copy-press}])))
(defn snippet [{:keys [language max-lines on-copy-press]} children]
[highlighter/highlighter {:language language
:renderer (wrap-renderer-fn
native-renderer {:max-lines max-lines
:on-copy-press #(when on-copy-press
(on-copy-press children))})
;; Default props to adapt Highlighter for react-native.
:CodeTag react-native/View
:PreTag react-native/View
:show-line-numbers false
:style #js {}
:custom-style #js {:backgroundColor nil}}
(defn snippet
[{:keys [language max-lines on-copy-press]} children]
[highlighter/highlighter
{:language language
:renderer (wrap-renderer-fn
native-renderer
{:max-lines max-lines
:on-copy-press #(when on-copy-press
(on-copy-press children))})
;; Default props to adapt Highlighter for react-native.
:CodeTag react-native/View
:PreTag react-native/View
:show-line-numbers false
:style #js {}
:custom-style #js {:backgroundColor nil}}
children])

View File

@ -1,18 +1,18 @@
(ns quo2.components.community.community-card-view
(:require
[quo2.components.community.community-view :as community-view]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
(:require [quo2.components.community.community-view :as community-view]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
(defn community-card-view-item
[{:keys [name description locked
status tokens cover tags width]} on-press]
[rn/touchable-opacity {:on-press on-press}
[rn/view {:style (style/community-card 20)}
[rn/view {:style {:width width
:height 230
:border-radius 20}
:on-press on-press}
[rn/view
{:style {:width width
:height 230
:border-radius 20}
:on-press on-press}
[rn/view
{:flex 1}
[rn/view (style/community-cover-container 40)
@ -27,9 +27,10 @@
;[communities.icon/community-icon-redesign community 48]]
(when (= status :gated)
[rn/view (style/permission-tag-styles)
[community-view/permission-tag-container {:locked locked
:status status
:tokens tokens}]])
[community-view/permission-tag-container
{:locked locked
:status status
:tokens tokens}]])
[community-view/community-title
{:title name
:description description}]

View File

@ -1,112 +1,131 @@
(ns quo2.components.community.community-list-view
(:require
[quo2.components.community.community-view :as community-view]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.components.counter.counter :as counter]
[quo2.components.icon :as icons]
[quo2.components.community.style :as style]
[react-native.fast-image :as fast-image]
[react-native.core :as rn]))
(:require [quo2.components.community.community-view :as community-view]
[quo2.components.community.style :as style]
[quo2.components.counter.counter :as counter]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]))
(defn community-icon-view [community-icon]
[rn/view {:width 32
:height 32}
[fast-image/fast-image {:source {:uri community-icon}
:style {:height 32
:border-radius 16
:width 32}}]])
(defn community-icon-view
[community-icon]
[rn/view
{:width 32
:height 32}
[fast-image/fast-image
{:source {:uri community-icon}
:style {:height 32
:border-radius 16
:width 32}}]])
(defn notification-view [{:keys [muted?
unread-messages?
unread-mentions-count]}]
(defn notification-view
[{:keys [muted?
unread-messages?
unread-mentions-count]}]
(cond
muted?
[icons/icon :i/muted {:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 20
:color (colors/theme-colors
colors/neutral-40
colors/neutral-50)}]
[icons/icon :i/muted
{:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 20
:color (colors/theme-colors
colors/neutral-40
colors/neutral-50)}]
(pos? unread-mentions-count)
[counter/counter {:type :default} unread-mentions-count]
unread-messages?
[rn/view {:style {:width 8
:height 8
:border-radius 4
:background-color (colors/theme-colors
colors/neutral-40
colors/neutral-60)}}]))
[rn/view
{:style {:width 8
:height 8
:border-radius 4
:background-color (colors/theme-colors
colors/neutral-40
colors/neutral-60)}}]))
(defn communities-list-view-item [props {:keys [name
locked?
status
muted?
unread-messages?
unread-mentions-count
community-icon
tokens
background-color]}]
[rn/view {:style (merge (style/community-card 16)
{:margin-bottom 12
:margin-horizontal 20})}
[rn/touchable-highlight (merge {:style {:height 56
:border-radius 16}}
props)
(defn communities-list-view-item
[props
{:keys [name
locked?
status
muted?
unread-messages?
unread-mentions-count
community-icon
tokens
background-color]}]
[rn/view
{:style (merge (style/community-card 16)
{:margin-bottom 12
:margin-horizontal 20})}
[rn/touchable-highlight
(merge {:style {:height 56
:border-radius 16}}
props)
[rn/view {:flex 1}
[rn/view {:flex-direction :row
:border-radius 16
:padding-horizontal 12
:align-items :center
:padding-vertical 8
:background-color background-color}
[rn/view
{:flex-direction :row
:border-radius 16
:padding-horizontal 12
:align-items :center
:padding-vertical 8
:background-color background-color}
[rn/view]
(when community-icon
[community-icon-view community-icon])
[rn/view {:flex 1
:margin-horizontal 12}
[text/text {:weight :semi-bold
:size :paragraph-1
:accessibility-label :community-name-text
:number-of-lines 1
:ellipsize-mode :tail
:style {:color (when muted?
(colors/theme-colors
colors/neutral-40
colors/neutral-60))}}
[rn/view
{:flex 1
:margin-horizontal 12}
[text/text
{:weight :semi-bold
:size :paragraph-1
:accessibility-label :community-name-text
:number-of-lines 1
:ellipsize-mode :tail
:style {:color (when muted?
(colors/theme-colors
colors/neutral-40
colors/neutral-60))}}
name]
[community-view/community-stats-column :list-view]]
(if (= status :gated)
[community-view/permission-tag-container {:locked? locked?
:tokens tokens}]
[notification-view {:muted? muted?
:unread-mentions-count unread-mentions-count
:unread-messages? unread-messages?}])]]]])
[community-view/permission-tag-container
{:locked? locked?
:tokens tokens}]
[notification-view
{:muted? muted?
:unread-mentions-count unread-mentions-count
:unread-messages? unread-messages?}])]]]])
(defn communities-membership-list-item [props {:keys [name
muted?
unread-messages?
unread-mentions-count
status
community-icon
tokens
locked?]}]
(defn communities-membership-list-item
[props
{:keys [name
muted?
unread-messages?
unread-mentions-count
status
community-icon
tokens
locked?]}]
[rn/view {:margin-bottom 20}
[rn/touchable-highlight (merge {:underlay-color colors/primary-50-opa-5
:style {:border-radius 12}}
props)
[rn/touchable-highlight
(merge {:underlay-color colors/primary-50-opa-5
:style {:border-radius 12}}
props)
[rn/view {:flex 1}
[rn/view {:flex-direction :row
:border-radius 16
:align-items :center}
[rn/view
{:flex-direction :row
:border-radius 16
:align-items :center}
(when community-icon
[community-icon-view community-icon])
[rn/view {:flex 1
:margin-left 12
:justify-content :center}
[rn/view
{:flex 1
:margin-left 12
:justify-content :center}
[text/text
{:accessibility-label :chat-name-text
:number-of-lines 1
@ -115,11 +134,14 @@
:size :paragraph-1}
name]]
[rn/view {:justify-content :center
:margin-right 16}
[rn/view
{:justify-content :center
:margin-right 16}
(if (= status :gated)
[community-view/permission-tag-container {:locked? locked?
:tokens tokens}]
[notification-view {:muted? muted?
:unread-mentions-count unread-mentions-count
:unread-messages? unread-messages?}])]]]]])
[community-view/permission-tag-container
{:locked? locked?
:tokens tokens}]
[notification-view
{:muted? muted?
:unread-mentions-count unread-mentions-count
:unread-messages? unread-messages?}])]]]]])

View File

@ -1,38 +1,46 @@
(ns quo2.components.community.community-view
(:require
[quo2.components.markdown.text :as text]
[quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[quo2.components.tags.permission-tag :as permission]
[quo2.components.tags.tag :as tag]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
(:require [quo2.components.community.style :as style]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.tags.permission-tag :as permission]
[quo2.components.tags.tag :as tag]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn community-stats [{:keys [icon members-count icon-color]}]
(defn community-stats
[{:keys [icon members-count icon-color]}]
[rn/view (style/stats-count-container)
[rn/view {:margin-right 4}
[icons/icon icon {:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 16
:color icon-color}]]
[text/text {:weight :regular
:size :paragraph-1}
[icons/icon icon
{:container-style {:align-items :center
:justify-content :center}
:resize-mode :center
:size 16
:color icon-color}]]
[text/text
{:weight :regular
:size :paragraph-1}
members-count]])
(defn community-stats-column [type]
(defn community-stats-column
[type]
(let [icon-color (colors/theme-colors colors/neutral-50 colors/neutral-40)]
[rn/view (if (= type :card-view)
(style/card-stats-container)
(style/list-stats-container))
[community-stats {:icon :i/group
:members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from outside this component
:icon-color icon-color}]
[community-stats {:icon :i/lightning
:members-count "112.1K"
:icon-color icon-color}]]))
[rn/view
(if (= type :card-view)
(style/card-stats-container)
(style/list-stats-container))
[community-stats
{:icon :i/group
:members-count "629.2K" ;;TODO here should be formatted value, use money/format-members from
;;outside this component
:icon-color icon-color}]
[community-stats
{:icon :i/lightning
:members-count "112.1K"
:icon-color icon-color}]]))
(defn community-tags [tags]
(defn community-tags
[tags]
[rn/view (style/community-tags-container)
(for [{:keys [id tag-label resource]} tags]
^{:key id}
@ -45,7 +53,8 @@
:labelled true
:resource resource}]])])
(defn community-title [{:keys [title description size] :or {size :small}}]
(defn community-title
[{:keys [title description size] :or {size :small}}]
[rn/view (style/community-title-description-container (if (= size :large) 56 32))
(when title
[text/text
@ -65,12 +74,14 @@
:style {:margin-top (if (= size :large) 8 2)}}
description])])
(defn permission-tag-container [{:keys [locked? tokens on-press]}]
[permission/tag {:background-color (colors/theme-colors
colors/neutral-10
colors/neutral-80)
:locked? locked?
:tokens tokens
(defn permission-tag-container
[{:keys [locked? tokens on-press]}]
[permission/tag
{:background-color (colors/theme-colors
colors/neutral-10
colors/neutral-80)
:locked? locked?
:tokens tokens
:size 24
:on-press on-press}])
:size 24
:on-press on-press}])

View File

@ -1,94 +1,107 @@
(ns quo2.components.community.discover-card
(:require [quo2.components.markdown.text :as text]
(:require [quo2.components.community.style :as style]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.components.community.style :as style]
[react-native.core :as rn]))
;; Discover card placeholders images.
;; TODO replaced when real data is available
(def images
{:images [{:id 1 :column-images [{:id 1 :image ""}]}
{:images [{:id 1 :column-images [{:id 1 :image ""}]}
{:id 2 :column-images [{}]}]})
(defn card-title-and-description [title description]
(defn card-title-and-description
[title description]
[rn/view
{:flex 1
:padding-top 8
:padding-bottom 8
:border-radius 12}
[rn/view {:flex 1
:padding-horizontal 12}
[text/text {:accessibility-label :community-name-text
:ellipsize-mode :tail
:number-of-lines 1
:weight :semi-bold
:size :paragraph-1}
{:flex 1
:padding-top 8
:padding-bottom 8
:border-radius 12}
[rn/view
{:flex 1
:padding-horizontal 12}
[text/text
{:accessibility-label :community-name-text
:ellipsize-mode :tail
:number-of-lines 1
:weight :semi-bold
:size :paragraph-1}
title]
[text/text {:accessibility-label :community-name-text
:ellipsize-mode :tail
:number-of-lines 1
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)
:weight :regular
:size :paragraph-2}
[text/text
{:accessibility-label :community-name-text
:ellipsize-mode :tail
:number-of-lines 1
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)
:weight :regular
:size :paragraph-2}
description]]])
(defn placeholder-list-images [{:keys [images width height border-radius]}]
(defn placeholder-list-images
[{:keys [images width height border-radius]}]
[rn/view
[rn/view {:justify-content :center}
[rn/view {:justify-content :center}
(for [{:keys [id]} images]
^{:key id}
[rn/view {:border-radius border-radius
:margin-top 4
:margin-right 4
:width width
:height height
:background-color colors/neutral-10}])]])
[rn/view
{:border-radius border-radius
:margin-top 4
:margin-right 4
:width width
:height height
:background-color colors/neutral-10}])]])
(defn placeholder-row-images [{:keys [first-image last-image images width height
border-radius]}]
(defn placeholder-row-images
[{:keys [first-image last-image images width height
border-radius]}]
[rn/view
(when first-image
[rn/view {:border-bottom-right-radius 6
:border-bottom-left-radius 6
:margin-right 4
:width width
:height height
:background-color colors/neutral-10}])
[rn/view
{:border-bottom-right-radius 6
:border-bottom-left-radius 6
:margin-right 4
:width width
:height height
:background-color colors/neutral-10}])
(when images
[placeholder-list-images {:images images
:width 32
:height 32
:border-radius 6}])
[placeholder-list-images
{:images images
:width 32
:height 32
:border-radius 6}])
(when last-image
[rn/view {:border-top-left-radius border-radius
:border-top-right-radius 6
:margin-top 4
:width width
:height height
:background-color colors/neutral-10}])])
[rn/view
{:border-top-left-radius border-radius
:border-top-right-radius 6
:margin-top 4
:width width
:height height
:background-color colors/neutral-10}])])
(defn discover-card [{:keys [title description on-press accessibility-label]}]
(let [on-joined-images (get images :images)]
(defn discover-card
[{:keys [title description on-press accessibility-label]}]
(let [on-joined-images (get images :images)]
[rn/touchable-without-feedback
{:on-press on-press
:accessibility-label accessibility-label}
[rn/view (merge (style/community-card 16)
{:background-color (colors/theme-colors
colors/white
colors/neutral-90)}
{:flex-direction :row
:margin-horizontal 20
:height 56
:padding-right 12})
[rn/view
(merge (style/community-card 16)
{:background-color (colors/theme-colors
colors/white
colors/neutral-90)}
{:flex-direction :row
:margin-horizontal 20
:height 56
:padding-right 12})
[card-title-and-description title description]
(for [{:keys [id column-images]} on-joined-images]
^{:key id}
[placeholder-row-images {:images (when (= id 1)
column-images)
:width 32
:height (if (= id 1) 8 26)
:border-radius 6
:first-image "" ; TODO replace with real data
:last-image ""}])]])) ; TODO replace with real data
[placeholder-row-images
{:images (when (= id 1)
column-images)
:width 32
:height (if (= id 1) 8 26)
:border-radius 6
:first-image "" ; TODO replace with real data
:last-image ""}])]])) ; TODO replace with real data

View File

@ -8,57 +8,66 @@
:height 52
:padding-left 18})
(defn community-card [radius]
{:shadow-offset {:width 0
(defn community-card
[radius]
{:shadow-offset {:width 0
:height 2}
:shadow-radius radius
:shadow-opacity 1
:shadow-color colors/shadow
:border-radius radius
:justify-content :space-between
:elevation 2
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
(defn stats-count-container []
{:flex-direction :row
:align-items :center
:margin-right 16})
(defn card-stats-container []
{:flex-direction :row})
(defn list-stats-container []
{:flex-direction :row
:align-items :center})
(defn community-tags-container []
{:flex-direction :row})
(defn card-stats-position []
{:position :absolute
:top 116
:right 12
:left 12})
(defn community-tags-position []
{:position :absolute
:top 154
:right 12
:left 12})
(defn card-view-content-container [padding-horizontal]
{:flex 1
:height 20
:padding-left padding-horizontal
:padding-right padding-horizontal
:border-radius 16
:shadow-radius radius
:shadow-opacity 1
:shadow-color colors/shadow
:border-radius radius
:justify-content :space-between
:elevation 2
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
(defn card-view-chat-icon [icon-size]
(defn stats-count-container
[]
{:flex-direction :row
:align-items :center
:margin-right 16})
(defn card-stats-container
[]
{:flex-direction :row})
(defn list-stats-container
[]
{:flex-direction :row
:align-items :center})
(defn community-tags-container
[]
{:flex-direction :row})
(defn card-stats-position
[]
{:position :absolute
:top 116
:right 12
:left 12})
(defn community-tags-position
[]
{:position :absolute
:top 154
:right 12
:left 12})
(defn card-view-content-container
[padding-horizontal]
{:flex 1
:height 20
:padding-left padding-horizontal
:padding-right padding-horizontal
:border-radius 16
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
(defn card-view-chat-icon
[icon-size]
{:border-radius 48
:position :absolute
:top (- (/ icon-size 2))
@ -68,29 +77,34 @@
colors/white
colors/neutral-90)})
(defn list-view-content-container []
{:flex-direction :row
:border-radius 16
:align-items :center
(defn list-view-content-container
[]
{:flex-direction :row
:border-radius 16
:align-items :center
:background-color (colors/theme-colors
colors/white
colors/neutral-90)})
(defn list-view-chat-icon []
{:border-radius 32
:padding 12})
(defn list-view-chat-icon
[]
{:border-radius 32
:padding 12})
(defn community-title-description-container [margin-top]
(defn community-title-description-container
[margin-top]
{:margin-top margin-top})
(defn community-cover-container [height]
(defn community-cover-container
[height]
{:flex-direction :row
:height height
:border-top-right-radius 20
:border-top-left-radius 20
:background-color colors/primary-50-opa-20})
(defn permission-tag-styles []
{:position :absolute
:top 8
:right 8})
(defn permission-tag-styles
[]
{:position :absolute
:top 8
:right 8})

View File

@ -13,75 +13,83 @@
(def ^:private token-tag-horizontal-spacing 7)
(def token-tag-vertical-spacing 5)
(def styles {:container {:flex 1
:padding-horizontal 20}
:header-container {:flex-direction :row
:align-items :center}
:header-spacing-community {:margin-bottom 16}
:header-spacing-channel {:margin-bottom 24}
:header-avatar {:margin-right 8}
:header-community-avatar-image {:width 32 :height 32}
:header-title-container {:flex 1
:flex-direction :row
:align-items :center}
:header-title-lock {:margin-left 6}
:header-info-button {:margin-left 8}
:token-requirement-text-spacing {:margin-vertical 8}
:token-requirement-list-spacing {:padding-vertical (- 8 token-tag-vertical-spacing)}
:token-row {:flex-direction :row
:flex-wrap :wrap
:align-items :center}
:token-row-container {:flex-direction :row
:align-items :center
:margin-horizontal (- token-tag-horizontal-spacing)}
:token-row-container-no-shift {:margin-left 0}
:token-row-or-text {:margin-right 3}
:token-tag-spacing {:margin-horizontal token-tag-horizontal-spacing
:margin-vertical token-tag-vertical-spacing}
:divider {:height 1
:margin-horizontal -20
:margin-top (- 12 token-tag-vertical-spacing)
:margin-bottom 8}
:membership-request-denied {:margin-top 13}
:enter-button {:margin-top 16}
:info-text {:margin-top 12
:text-align :center
:padding-horizontal 20}})
(def styles
{:container {:flex 1
:padding-horizontal 20}
:header-container {:flex-direction :row
:align-items :center}
:header-spacing-community {:margin-bottom 16}
:header-spacing-channel {:margin-bottom 24}
:header-avatar {:margin-right 8}
:header-community-avatar-image {:width 32 :height 32}
:header-title-container {:flex 1
:flex-direction :row
:align-items :center}
:header-title-lock {:margin-left 6}
:header-info-button {:margin-left 8}
:token-requirement-text-spacing {:margin-vertical 8}
:token-requirement-list-spacing {:padding-vertical (- 8 token-tag-vertical-spacing)}
:token-row {:flex-direction :row
:flex-wrap :wrap
:align-items :center}
:token-row-container {:flex-direction :row
:align-items :center
:margin-horizontal (- token-tag-horizontal-spacing)}
:token-row-container-no-shift {:margin-left 0}
:token-row-or-text {:margin-right 3}
:token-tag-spacing {:margin-horizontal token-tag-horizontal-spacing
:margin-vertical token-tag-vertical-spacing}
:divider {:height 1
:margin-horizontal -20
:margin-top (- 12 token-tag-vertical-spacing)
:margin-bottom 8}
:membership-request-denied {:margin-top 13}
:enter-button {:margin-top 16}
:info-text {:margin-top 12
:text-align :center
:padding-horizontal 20}})
(defn multiple-token-requirements? [token-requirement-lists]
(defn multiple-token-requirements?
[token-requirement-lists]
(when (vector? (first token-requirement-lists))
(> (count token-requirement-lists) 1)))
(defn is-token-requirement-met? [token-requirement-list]
(defn is-token-requirement-met?
[token-requirement-list]
(and (seq token-requirement-list)
(every?
(fn [token]
(get token :is-sufficient?))
token-requirement-list)))
(defn are-multiple-token-requirements-met? [token-requirement-lists]
(defn are-multiple-token-requirements-met?
[token-requirement-lists]
(if (multiple-token-requirements? token-requirement-lists)
(some is-token-requirement-met? token-requirement-lists)
(is-token-requirement-met? token-requirement-lists)))
(defn token-requirement-list-row [tokens community-color]
[rn/view {:style (merge
(get styles :token-row)
(get styles :token-requirement-list-spacing))}
(defn token-requirement-list-row
[tokens community-color]
[rn/view
{:style (merge
(get styles :token-row)
(get styles :token-requirement-list-spacing))}
(map-indexed (fn [token-index token]
(let [{:keys [token-img-src token amount is-sufficient? is-purchasable?]} token]
^{:key token-index}
[rn/view {:style (get styles :token-tag-spacing)}
[token-tag/token-tag {:token token
:value amount
:size 24
:border-color community-color
:is-required is-sufficient?
:is-purchasable is-purchasable?
:token-img-src token-img-src}]]))
[token-tag/token-tag
{:token token
:value amount
:size 24
:border-color community-color
:is-required is-sufficient?
:is-purchasable is-purchasable?
:token-img-src token-img-src}]]))
tokens)])
(defn token-requirement-list [props community-color]
(defn token-requirement-list
[props community-color]
(let [{:keys [gate token-requirements-changed? required-tokens-lost?]} props
[gate-type token-requirement-lists] gate
multiple-token-requirements? (multiple-token-requirements? token-requirement-lists)
@ -89,15 +97,17 @@
you-must-hold-label (if (= gate-type :join)
(cond
token-requirements-changed? :t/you-must-now-hold
required-tokens-lost? :t/you-must-always-hold
:else :t/you-must-hold)
required-tokens-lost? :t/you-must-always-hold
:else :t/you-must-hold)
:t/you-must-hold)
message-label (cond
(= gate-type :join)
(cond
token-requirements-changed? :t/community-join-requirements-changed
required-tokens-lost? :t/community-join-requirements-tokens-lost
:else (if is-sufficient? :t/community-join-requirements-met :t/community-join-requirements-not-met))
required-tokens-lost? :t/community-join-requirements-tokens-lost
:else (if is-sufficient?
:t/community-join-requirements-met
:t/community-join-requirements-not-met))
(= gate-type :read)
(if is-sufficient?
:t/community-channel-read-requirements-met
@ -108,12 +118,14 @@
:t/community-channel-write-requirements-not-met))]
[rn/view
[rn/view {:style (get styles :token-requirement-text-spacing)}
[text/text {:weight :medium
:number-of-lines 1}
[text/text
{:weight :medium
:number-of-lines 1}
(i18n/label
message-label)]
[text/text {:size :paragraph-2
:number-of-lines 1}
[text/text
{:size :paragraph-2
:number-of-lines 1}
(i18n/label
you-must-hold-label)]]
@ -121,22 +133,26 @@
(map-indexed
(fn [token-requirement-index tokens]
^{:key token-requirement-index}
[rn/view {:style (merge
(get styles :token-row-container)
(when-not (= token-requirement-index 0)
(get styles :token-row-container-no-shift)))}
[rn/view
{:style (merge
(get styles :token-row-container)
(when-not (= token-requirement-index 0)
(get styles :token-row-container-no-shift)))}
(when-not (= token-requirement-index 0)
[text/text {:style (get styles :token-row-or-text)
:size :paragraph-2} "or"])
[text/text
{:style (get styles :token-row-or-text)
:size :paragraph-2} "or"])
[token-requirement-list-row tokens community-color]])
token-requirement-lists)
[rn/view {:style (get styles :token-row-container)}
[token-requirement-list-row token-requirement-lists community-color]])]))
(defn is-community-locked? [community]
(defn is-community-locked?
[community]
(not (are-multiple-token-requirements-met? (get-in community [:gates :join]))))
(defn is-channel-locked? [channel]
(defn is-channel-locked?
[channel]
(not (are-multiple-token-requirements-met?
(or (get-in channel [:gates :read])
(get-in channel [:gates :write])))))
@ -174,82 +190,98 @@
membership-request-denied?
token-requirements-changed?
required-tokens-lost?
gates]} (if (= type :community) community channel)
gates]}
(if (= type :community) community channel)
locked? (if
(= type :community)
(= type :community)
(is-community-locked? community)
(is-channel-locked? channel))]
[rn/view {:style (merge (get styles :container) {:background-color (colors/theme-colors colors/white colors/neutral-90)})}
[rn/view {:style (merge
(get styles :header-container)
(if
(= type :community)
(get styles :header-spacing-community)
(get styles :header-spacing-channel)))}
[rn/view
{:style (merge (get styles :container)
{:background-color (colors/theme-colors colors/white colors/neutral-90)})}
[rn/view
{:style (merge
(get styles :header-container)
(if
(= type :community)
(get styles :header-spacing-community)
(get styles :header-spacing-channel)))}
[rn/view {:style (get styles :header-avatar)}
(if (= type :community)
[fast-image/fast-image {:source community-avatar-img-src
:style (get styles :header-community-avatar-image)}]
[fast-image/fast-image
{:source community-avatar-img-src
:style (get styles :header-community-avatar-image)}]
[channel-avatar/channel-avatar {:big? true
:locked? locked?
:emoji emoji
:emoji-background-color emoji-background-color}])]
[channel-avatar/channel-avatar
{:big? true
:locked? locked?
:emoji emoji
:emoji-background-color emoji-background-color}])]
[rn/view {:style (get styles :header-title-container)}
[text/text {:weight :semi-bold
:number-of-lines 1
:size :heading-1
:style (get styles :header-text)} (if (= type :community) name (str "# " name))]
[text/text
{:weight :semi-bold
:number-of-lines 1
:size :heading-1
:style (get styles :header-text)} (if (= type :community) name (str "# " name))]
(when (= type :community)
[icon/icon (if locked?
:main-icons2/locked
:main-icons2/unlocked)
[icon/icon
(if locked?
:main-icons2/locked
:main-icons2/unlocked)
{:container-style (get styles :header-title-lock)
:color (colors/theme-colors
colors/neutral-50
colors/neutral-40)}])]
[button/button {:type :outline
:size 32
:icon true
:style (get styles :header-info-button)} :main-icons2/info]]
[button/button
{:type :outline
:size 32
:icon true
:style (get styles :header-info-button)} :main-icons2/info]]
(map-indexed (fn [gate-index gate]
^{:key gate-index}
[:<>
(when-not (= gate-index 0)
[rn/view {:style (merge
(get styles :divider)
{:background-color (colors/theme-colors
colors/neutral-10
colors/neutral-80)})}])
[token-requirement-list {:gate gate :token-requirements-changed? token-requirements-changed? :required-tokens-lost? required-tokens-lost?} community-color]])
[rn/view
{:style (merge
(get styles :divider)
{:background-color (colors/theme-colors
colors/neutral-10
colors/neutral-80)})}])
[token-requirement-list
{:gate gate
:token-requirements-changed? token-requirements-changed?
:required-tokens-lost? required-tokens-lost?} community-color]])
gates)
(when (= type :channel)
(if membership-request-denied?
[information-box/information-box {:type :error
:icon :main-icons2/untrustworthy
:no-icon-color? true
:style (get styles :membership-request-denied)}
[information-box/information-box
{:type :error
:icon :main-icons2/untrustworthy
:no-icon-color? true
:style (get styles :membership-request-denied)}
(i18n/label
:t/membership-request-denied)]
[:<>
[button/button {:type :community
:community-color community-color
:community-text-color community-text-color
:style (get styles :enter-button)
:disabled locked?
:on-press on-enter-channel}
(str "# " (i18n/label
:t/enter-channel))]
[button/button
{:type :community
:community-color community-color
:community-text-color community-text-color
:style (get styles :enter-button)
:disabled locked?
:on-press on-enter-channel}
(str "# "
(i18n/label
:t/enter-channel))]
[text/text
{:size :paragraph-2
{:size :paragraph-2
:style (merge
(get styles :info-text)
{:color (colors/theme-colors

View File

@ -7,28 +7,28 @@
([]
(render-counter {} nil))
([opts value]
(rtl/render (reagent/as-element [counter/counter opts value]))))
(rtl/render (reagent/as-element [counter/counter opts value]))))
(js/global.test "default render of counter component"
(fn []
(render-counter)
(-> (js/expect (rtl/screen.getByTestId "counter-component"))
(-> (js/expect (rtl/screen.getByTestId "counter-component"))
(.toBeTruthy))))
(js/global.test "renders counter with a string value"
(fn []
(render-counter {} "1")
(-> (js/expect (rtl/screen.getByText "1"))
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(js/global.test "renders counter with an integer value"
(fn []
(render-counter {} 1)
(-> (js/expect (rtl/screen.getByText "1"))
(-> (js/expect (rtl/screen.getByText "1"))
(.toBeTruthy))))
(js/global.test "renders counter with value 99+ when the value is greater than 99"
(fn []
(render-counter {} "100")
(-> (js/expect (rtl/screen.getByText "99+"))
(-> (js/expect (rtl/screen.getByText "99+"))
(.toBeTruthy))))

View File

@ -1,20 +1,21 @@
(ns quo2.components.counter.counter
(:require [quo2.theme :as theme]
[react-native.core :as rn]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]))
(:require [quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(def themes
{:light {:default colors/primary-50
:secondary colors/neutral-80-opa-5
:grey colors/neutral-10
:outline colors/neutral-20}
:dark {:default colors/primary-60
:secondary colors/white-opa-5
:grey colors/neutral-70
:outline colors/neutral-70}})
{:light {:default colors/primary-50
:secondary colors/neutral-80-opa-5
:grey colors/neutral-10
:outline colors/neutral-20}
:dark {:default colors/primary-60
:secondary colors/white-opa-5
:grey colors/neutral-70
:outline colors/neutral-70}})
(defn get-color [key]
(defn get-color
[key]
(get-in themes [(theme/get-theme) key]))
(defn counter
@ -35,30 +36,32 @@
"99+"
(str value))
width (case (count label)
1 16
2 20
1 16
2 20
28)]
[rn/view {:test-ID :counter-component
:accessible true
:accessibility-label accessibility-label
:style (cond-> (merge
{:align-items :center
:justify-content :center
:border-radius 6
:width width
:height 16}
style)
(= type :outline)
(merge {:border-width 1
:border-color (get-color type)})
[rn/view
{:test-ID :counter-component
:accessible true
:accessibility-label accessibility-label
:style (cond-> (merge
{:align-items :center
:justify-content :center
:border-radius 6
:width width
:height 16}
style)
(= type :outline)
(merge {:border-width 1
:border-color (get-color type)})
(not= type :outline)
(assoc :background-color
(or override-bg-color
(get-color type)))
(not= type :outline)
(assoc :background-color
(or override-bg-color
(get-color type)))
(> value 99)
(assoc :padding-left 0.5))}
[text/text {:weight :medium
:size :label
:style {:color text-color}} label]]))
(> value 99)
(assoc :padding-left 0.5))}
[text/text
{:weight :medium
:size :label
:style {:color text-color}} label]]))

View File

@ -1,18 +1,21 @@
(ns quo2.components.dividers.date
(:require [react-native.core :as rn]
(:require [quo2.components.markdown.text :as text]
[quo2.components.separator :as separator]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]))
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn date [value]
[rn/view {:margin-vertical 16
:padding-right 8
:padding-left 62}
[text/text {:weight :medium
:accessibility-label :divider-date-text
:size :label
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:text-transform :capitalize
:margin-bottom 4}}
(defn date
[value]
[rn/view
{:margin-vertical 16
:padding-right 8
:padding-left 62}
[text/text
{:weight :medium
:accessibility-label :divider-date-text
:size :label
:style {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)
:text-transform :capitalize
:margin-bottom 4}}
value]
[separator/separator]])

View File

@ -1,8 +1,8 @@
(ns quo2.components.dividers.divider-label
(:require [react-native.core :as rn]
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as markdown.text]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]))
[react-native.core :as rn]))
(def chevron-icon-container-width 20)
@ -15,17 +15,20 @@
increase-padding-top? -> boolean
blur? -> boolean"
[{:keys [label chevron-position counter-value increase-padding-top? blur?]}]
(let [dark? (colors/dark?)
border-and-counter-bg-color (if dark? (if blur? colors/white-opa-5 colors/neutral-70) colors/neutral-10)
padding-top (if increase-padding-top? 16 8)
text-and-icon-color (if dark? colors/neutral-40 colors/neutral-50)
counter-text-color (if dark? colors/white colors/neutral-100)]
[rn/view {:style {:border-top-width 1
:border-top-color border-and-counter-bg-color
:padding-top padding-top
:padding-horizontal 16
:align-items :center
:flex-direction :row}}
(let [dark? (colors/dark?)
border-and-counter-bg-color (if dark?
(if blur? colors/white-opa-5 colors/neutral-70)
colors/neutral-10)
padding-top (if increase-padding-top? 16 8)
text-and-icon-color (if dark? colors/neutral-40 colors/neutral-50)
counter-text-color (if dark? colors/white colors/neutral-100)]
[rn/view
{:style {:border-top-width 1
:border-top-color border-and-counter-bg-color
:padding-top padding-top
:padding-horizontal 16
:align-items :center
:flex-direction :row}}
(when (= chevron-position :left)
[rn/view {:style {:margin-right 4}}
[icons/icon
@ -33,27 +36,30 @@
{:color text-and-icon-color
:width chevron-icon-container-width
:height chevron-icon-container-height}]])
[markdown.text/text {:size :paragraph-2
:weight :medium
:style {:color text-and-icon-color
:flex 1}}
[markdown.text/text
{:size :paragraph-2
:weight :medium
:style {:color text-and-icon-color
:flex 1}}
label]
(when (= chevron-position :right)
[icons/icon
:main-icons/chevron-down
{:color text-and-icon-color
{:color text-and-icon-color
:size chevron-icon-container-width}])
(when (pos? counter-value)
[rn/view {:style {:border-radius 6
:height 16
:width (case (count counter-value)
1 16
2 20
28)
:background-color border-and-counter-bg-color
:align-items :center
:justify-content :center}}
[markdown.text/text {:size :label
:weight :medium
:style {:color counter-text-color}}
[rn/view
{:style {:border-radius 6
:height 16
:width (case (count counter-value)
1 16
2 20
28)
:background-color border-and-counter-bg-color
:align-items :center
:justify-content :center}}
[markdown.text/text
{:size :label
:weight :medium
:style {:color counter-text-color}}
counter-value]])]))

View File

@ -1,7 +1,7 @@
(ns quo2.components.dividers.new-messages
(:require [react-native.core :as rn]
[quo2.components.markdown.text :as text]
(:require [quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.linear-gradient :as linear-gradient]))
(defn new-messages
@ -9,11 +9,14 @@
[{:keys [label color] :or {color :primary}}]
(let [bg-color (colors/custom-color-by-theme color 50 60 5 5)
text-color (colors/custom-color-by-theme color 50 60)]
[linear-gradient/linear-gradient {:colors [bg-color "rgba(0,0,0,0)"]
:start {:x 0 :y 0} :end {:x 0 :y 1}}
[rn/view {:style {:padding-left 60
:padding-vertical 12
:padding-right 24}}
[linear-gradient/linear-gradient
{:colors [bg-color "rgba(0,0,0,0)"]
:start {:x 0 :y 0}
:end {:x 0 :y 1}}
[rn/view
{:style {:padding-left 60
:padding-vertical 12
:padding-right 24}}
[text/text
{:size :paragraph-2
:weight :medium

View File

@ -5,21 +5,21 @@
(defn render-action-drawer
([options]
(rtl/render (reagent/as-element [action-drawer/action-drawer options]))))
(rtl/render (reagent/as-element [action-drawer/action-drawer options]))))
(js/global.test "action-drawer renders with elements label displaying"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"}]])
(-> (js/expect (rtl/screen.getByText "a sample label"))
(render-action-drawer [[{:icon :i/friend
:label "a sample label"}]])
(-> (js/expect (rtl/screen.getByText "a sample label"))
(.toBeTruthy))))
(js/global.test "action-drawer renders with elements sub-label displaying"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:sub-label "a sample sub label"}]])
(-> (js/expect (rtl/screen.getByText "a sample sub label"))
(-> (js/expect (rtl/screen.getByText "a sample sub label"))
(.toBeTruthy))))
(js/global.test "action-drawer on click action works on element"
@ -29,14 +29,14 @@
:label "a sample label"
:on-press event}]])
(rtl/fireEvent.press (rtl/screen.getByText "a sample label"))
(-> (js/expect event)
(-> (js/expect event)
(.toHaveBeenCalled)))))
(js/global.test "action-drawer renders two icons when set"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:right-icon :i/friend
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:right-icon :i/friend
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getByLabelText "right-icon-for-action"))
(.toBeTruthy))
@ -45,19 +45,19 @@
(js/global.test "action-drawer does not render a divider when the add-divider? prop is false"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? false
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? false
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(.not)
(.toBeTruthy))))
(js/global.test "action-drawer renders a divider when the add-divider? prop is true"
(fn []
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? true
(render-action-drawer [[{:icon :i/friend
:label "a sample label"
:add-divider? true
:accessibility-label :first-element}]])
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(-> (js/expect (rtl/screen.getAllByLabelText "divider"))
(.toBeTruthy))))

View File

@ -1,54 +1,62 @@
(ns quo2.components.drawers.action-drawers
(:require [react-native.core :as rn]
(:require [quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.components.icon :as icon]
[quo2.foundations.colors :as colors]))
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn- get-icon-color [danger?]
(defn- get-icon-color
[danger?]
(if danger?
colors/danger-50
(colors/theme-colors colors/neutral-50 colors/neutral-40)))
(def divider
[rn/view {:style {:border-top-width 1
:border-top-color (colors/theme-colors
colors/neutral-10 colors/neutral-90)
:margin-top 8
:margin-bottom 7
:align-items :center
:flex-direction :row}
:accessible true
:accessibility-label :divider}])
[rn/view
{:style {:border-top-width 1
:border-top-color (colors/theme-colors
colors/neutral-10
colors/neutral-90)
:margin-top 8
:margin-bottom 7
:align-items :center
:flex-direction :row}
:accessible true
:accessibility-label :divider}])
(defn action [{:keys [icon
label
sub-label
right-icon
danger?
on-press
add-divider?
accessibility-label] :as action-props}]
(defn action
[{:keys [icon
label
sub-label
right-icon
danger?
on-press
add-divider?
accessibility-label]
:as action-props}]
(when action-props
[:<> {:key label}
(when add-divider? divider)
[rn/touchable-highlight {:accessibility-label accessibility-label
:style {:border-radius 12
:height (if sub-label 58 50)
:margin-horizontal 8}
:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90)
:on-press on-press}
[rn/view {:style
{:height (if sub-label 58 50)
:margin-horizontal 12
:flex-direction :row}}
[rn/view {:accessibility-label :left-icon-for-action
:accessible true
:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:margin-right 12
:width 20}}
[rn/touchable-highlight
{:accessibility-label accessibility-label
:style {:border-radius 12
:height (if sub-label 58 50)
:margin-horizontal 8}
:underlay-color (colors/theme-colors colors/neutral-5 colors/neutral-90)
:on-press on-press}
[rn/view
{:style
{:height (if sub-label 58 50)
:margin-horizontal 12
:flex-direction :row}}
[rn/view
{:accessibility-label :left-icon-for-action
:accessible true
:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:margin-right 12
:width 20}}
[icon/icon icon
{:color (get-icon-color danger?)
:size 20}]]
@ -70,18 +78,20 @@
(colors/theme-colors colors/neutral-50 colors/neutral-40)}}
sub-label])]
(when right-icon
[rn/view {:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:width 20}
:accessible true
:accessibility-label :right-icon-for-action}
[rn/view
{:style
{:height 20
:margin-top :auto
:margin-bottom :auto
:width 20}
:accessible true
:accessibility-label :right-icon-for-action}
[icon/icon right-icon
{:color (get-icon-color danger?)
:size 20}]])]]]))
(defn action-drawer [sections]
(defn action-drawer
[sections]
[:<>
(doall
(for [actions sections]

View File

@ -1,97 +1,108 @@
(ns quo2.components.dropdowns.dropdown
(:require [react-native.core :as rn]
[quo2.components.icon :as icons]
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]))
(defn apply-anim [dd-height val]
(defn apply-anim
[dd-height val]
(reanimated/animate-shared-value-with-delay dd-height
val
300
:easing1
0))
(def sizes {:big {:icon-size 20
:font {:font-size :paragraph-1}
:height 40
:padding {:padding-with-icon {:padding-vertical 9
:padding-horizontal 12}
:padding-with-no-icon {:padding-vertical 9
:padding-left 12
:padding-right 8}}}
:medium {:icon-size 20
:font {:font-size :paragraph-1}
:height 32
:padding {:padding-with-icon {:padding-vertical 5
:padding-horizontal 8}
:padding-with-no-icon {:padding-vertical 5
:padding-left 12
:padding-right 8}}}
:small {:icon-size 15
:font {:font-size :paragraph-2}
:height 24
:padding {:padding-with-icon {:padding-vertical 3
:padding-horizontal 6}
:padding-with-no-icon {:padding-vertical 3
:padding-horizontal 6}}}})
(def sizes
{:big {:icon-size 20
:font {:font-size :paragraph-1}
:height 40
:padding {:padding-with-icon {:padding-vertical 9
:padding-horizontal 12}
:padding-with-no-icon {:padding-vertical 9
:padding-left 12
:padding-right 8}}}
:medium {:icon-size 20
:font {:font-size :paragraph-1}
:height 32
:padding {:padding-with-icon {:padding-vertical 5
:padding-horizontal 8}
:padding-with-no-icon {:padding-vertical 5
:padding-left 12
:padding-right 8}}}
:small {:icon-size 15
:font {:font-size :paragraph-2}
:height 24
:padding {:padding-with-icon {:padding-vertical 3
:padding-horizontal 6}
:padding-with-no-icon {:padding-vertical 3
:padding-horizontal 6}}}})
(defn color-by-10
[color]
(colors/alpha color 0.6))
(defn dropdown-comp [{:keys [icon open? dd-height size disabled? dd-color use-border? border-color]}]
(defn dropdown-comp
[{:keys [icon open? dd-height size disabled? dd-color use-border? border-color]}]
(let [dark? (colors/dark?)
{:keys [width height width-with-icon padding font icon-size]} (size sizes)
{:keys [padding-with-icon padding-with-no-icon]} padding
font-size (:font-size font)
spacing (case size
:big 4
:big 4
:medium 2
:small 2)]
[rn/touchable-opacity (cond-> {:on-press (fn []
(if (swap! open? not)
(apply-anim dd-height 120)
(apply-anim dd-height 0)))
:style (cond-> (merge
(if icon
padding-with-icon
padding-with-no-icon)
{:width (if icon
width-with-icon
width)
:height height
:border-radius (case size
:big 12
:medium 10
:small 8)
:flex-direction :row
:align-items :center
:background-color (if @open?
dd-color
(color-by-10 dd-color))})
use-border? (assoc :border-width 1
:border-color (if @open? border-color (color-by-10 border-color))))}
disabled? (assoc-in [:style :opacity] 0.3)
disabled? (assoc :disabled true))
:small 2)]
[rn/touchable-opacity
(cond->
{:on-press (fn []
(if (swap! open? not)
(apply-anim dd-height 120)
(apply-anim dd-height 0)))
:style (cond->
(merge
(if icon
padding-with-icon
padding-with-no-icon)
{:width (if icon
width-with-icon
width)
:height height
:border-radius (case size
:big 12
:medium 10
:small 8)
:flex-direction :row
:align-items :center
:background-color (if @open?
dd-color
(color-by-10 dd-color))})
use-border? (assoc :border-width 1
:border-color (if @open?
border-color
(color-by-10 border-color))))}
disabled? (assoc-in [:style :opacity] 0.3)
disabled? (assoc :disabled true))
(when icon
[icons/icon icon {:no-color true
:size 20
:container-style {:margin-right spacing
:margin-top 1
:width icon-size
:height icon-size}}])
[text/text {:size font-size
:weight :medium
:font :font-medium
:color :main} "Dropdown"]
[icons/icon (if @open?
(if dark?
:main-icons/pullup-dark
:main-icons/pullup)
(if dark?
:main-icons/dropdown-dark
:main-icons/dropdown))
[icons/icon icon
{:no-color true
:size 20
:container-style {:margin-right spacing
:margin-top 1
:width icon-size
:height icon-size}}])
[text/text
{:size font-size
:weight :medium
:font :font-medium
:color :main} "Dropdown"]
[icons/icon
(if @open?
(if dark?
:main-icons/pullup-dark
:main-icons/pullup)
(if dark?
:main-icons/dropdown-dark
:main-icons/dropdown))
{:size 20
:no-color true
:container-style {:width (+ icon-size 3)
@ -105,9 +116,10 @@
(defn items-comp
[{:keys [items on-select]}]
(let [items-count (count items)]
[rn/scroll-view {:style {:height "100%"}
:horizontal false
:nestedScrollEnabled true}
[rn/scroll-view
{:style {:height "100%"}
:horizontal false
:nestedScrollEnabled true}
(doall
(map-indexed (fn [index item]
[rn/touchable-opacity
@ -121,27 +133,32 @@
colors/white)
:text-align :center}
:on-press #(on-select item)}
[text/text {:style {:text-align :center}} item]]) items))]))
[text/text {:style {:text-align :center}} item]])
items))]))
(defn dropdown [{:keys [items icon text default-item on-select size disabled? border-color use-border? dd-color]}]
(defn dropdown
[{:keys [items icon text default-item on-select size disabled? border-color use-border? dd-color]}]
[:f>
(fn []
(let [open? (reagent/atom false)
dd-height (reanimated/use-shared-value 0)]
[rn/view {:style {:flex-grow 1}}
[dropdown-comp {:items items
:icon icon
:disabled? disabled?
:size size
:dd-color dd-color
:text text
:border-color (colors/custom-color-by-theme border-color 50 60)
:use-border? use-border?
:default-item default-item
:open? open?
:dd-height dd-height}]
[reanimated/view {:style (reanimated/apply-animations-to-style
{:height dd-height}
{:height dd-height})}
[items-comp {:items items
:on-select on-select}]]]))])
[dropdown-comp
{:items items
:icon icon
:disabled? disabled?
:size size
:dd-color dd-color
:text text
:border-color (colors/custom-color-by-theme border-color 50 60)
:use-border? use-border?
:default-item default-item
:open? open?
:dd-height dd-height}]
[reanimated/view
{:style (reanimated/apply-animations-to-style
{:height dd-height}
{:height dd-height})}
[items-comp
{:items items
:on-select on-select}]]]))])

View File

@ -1,15 +1,16 @@
(ns quo2.components.header
(:require [oops.core :refer [oget]]
[react-native.reanimated :as reanimated]
[quo2.components.buttons.button :as button]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]))
(def header-height 56)
(defn header-wrapper-style [{:keys [height border-bottom background]}]
(defn header-wrapper-style
[{:keys [height border-bottom background]}]
(merge
{:background-color (colors/theme-colors
colors/neutral-5
@ -23,32 +24,37 @@
colors/neutral-5
colors/neutral-95)})))
(def absolute-fill {:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def absolute-fill
{:position :absolute
:top 0
:bottom 0
:left 0
:right 0})
(def content {:flex 1
:flex-direction :row
:align-items :center
:justify-content :center})
(def content
{:flex 1
:flex-direction :row
:align-items :center
:justify-content :center})
(def left {:position :absolute
:left 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-start})
(def left
{:position :absolute
:left 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-start})
(def right {:position :absolute
:right 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-end})
(def right
{:position :absolute
:right 0
:top 0
:bottom 0
:justify-content :center
:align-items :flex-end})
(defn title-style [{:keys [left right]} title-align]
(defn title-style
[{:keys [left right]} title-align]
(merge
absolute-fill
(case title-align
@ -69,57 +75,66 @@
(def header-action-placeholder
{:width 16})
(def element {:align-items :center
:justify-content :center
:flex 1})
(def element
{:align-items :center
:justify-content :center
:flex 1})
(defn header-action [{:keys [icon label on-press disabled accessibility-label]}]
[button/button (merge {:on-press on-press
:disabled disabled}
(cond
icon {:type :icon
:theme :icon}
label {:type :secondary})
(when accessibility-label
{:accessibility-label accessibility-label}))
(defn header-action
[{:keys [icon label on-press disabled accessibility-label]}]
[button/button
(merge {:on-press on-press
:disabled disabled}
(cond
icon {:type :icon
:theme :icon}
label {:type :secondary})
(when accessibility-label
{:accessibility-label accessibility-label}))
(cond
icon icon
label label)])
(defn header-actions [{:keys [accessories component]}]
(defn header-actions
[{:keys [accessories component]}]
[rn/view {:style element}
(cond
(seq accessories)
(into [rn/view {:style header-actions-style}]
(map header-action accessories))
component component
component component
:else
[rn/view {:style header-action-placeholder}])])
(defn header-title [{:keys [title subtitle component title-align]}]
(defn header-title
[{:keys [title subtitle component title-align]}]
[:<>
(cond
component component
component component
(and title subtitle)
[:<>
[text/text {:weight :medium
:number-of-lines 1}
[text/text
{:weight :medium
:number-of-lines 1}
title]
[text/text {:weight :regular
:color :secondary
:number-of-lines 1}
[text/text
{:weight :regular
:color :secondary
:number-of-lines 1}
subtitle]]
title [text/text {:weight :bold
:number-of-lines 0
:align title-align
:size :large}
title])])
title [text/text
{:weight :bold
:number-of-lines 0
:align title-align
:size :large}
title])])
(defn header [{:keys [left-width right-width]}]
(defn header
[{:keys [left-width right-width]}]
(let [layout (reagent/atom {:left {:width (or left-width 8)
:height header-height}
:right {:width (or right-width 8)
@ -131,46 +146,61 @@
(let [width (oget evt "nativeEvent" "layout" "width")
height (oget evt "nativeEvent" "layout" "height")]
(when get-layout
(get-layout el {:width width
:height height}))
(swap! layout assoc el {:width width
:height height}))))]
(fn [{:keys [left-accessories left-component border-bottom
right-accessories right-component insets get-layout
title subtitle title-component style title-align
background]
:or {title-align :center
border-bottom false}}]
(get-layout el
{:width width
:height height}))
(swap! layout assoc
el
{:width width
:height height}))))]
(fn
[{:keys [left-accessories left-component border-bottom
right-accessories right-component insets get-layout
title subtitle title-component style title-align
background]
:or {title-align :center
border-bottom false}}]
(let [status-bar-height (get insets :top 0)
height (+ header-height status-bar-height)]
[reanimated/view {:style (header-wrapper-style {:height height
:background background
:border-bottom border-bottom})}
[rn/view {:pointer-events :box-none
:height status-bar-height}]
[rn/view {:style (merge {:height header-height}
style)
:pointer-events :box-none}
[rn/view {:style absolute-fill
:pointer-events :box-none}
[rn/view {:style content
:pointer-events :box-none}
[rn/view {:style left
:on-layout (handle-layout :left get-layout)
:pointer-events :box-none}
[header-actions {:accessories left-accessories
:component left-component}]]
[reanimated/view
{:style (header-wrapper-style {:height height
:background background
:border-bottom border-bottom})}
[rn/view
{:pointer-events :box-none
:height status-bar-height}]
[rn/view
{:style (merge {:height header-height}
style)
:pointer-events :box-none}
[rn/view
{:style absolute-fill
:pointer-events :box-none}
[rn/view
{:style content
:pointer-events :box-none}
[rn/view
{:style left
:on-layout (handle-layout :left get-layout)
:pointer-events :box-none}
[header-actions
{:accessories left-accessories
:component left-component}]]
[rn/view {:style (title-style @layout title-align)
:on-layout (handle-layout :title get-layout)
:pointer-events :box-none}
[header-title {:title title
:subtitle subtitle
:title-align title-align
:component title-component}]]
[rn/view
{:style (title-style @layout title-align)
:on-layout (handle-layout :title get-layout)
:pointer-events :box-none}
[header-title
{:title title
:subtitle subtitle
:title-align title-align
:component title-component}]]
[rn/view {:style right
:on-layout (handle-layout :right get-layout)
:pointer-events :box-none}
[header-actions {:accessories right-accessories
:component right-component}]]]]]]))))
[rn/view
{:style right
:on-layout (handle-layout :right get-layout)
:pointer-events :box-none}
[header-actions
{:accessories right-accessories
:component right-component}]]]]]]))))

View File

@ -1,15 +1,15 @@
(ns quo2.components.icon
(:require
[clojure.string :as string]
[react-native.core :as rn]
[quo2.components.icons.icons :as icons]
[quo2.foundations.colors :as colors]))
(:require [clojure.string :as string]
[quo2.components.icons.icons :as icons]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn memo-icon-fn
([icon-name] (memo-icon-fn icon-name nil))
([icon-name {:keys [color container-style size
accessibility-label no-color]
:or {accessibility-label :icon}}]
([icon-name
{:keys [color container-style size
accessibility-label no-color]
:or {accessibility-label :icon}}]
(let [size (or size 20)]
^{:key icon-name}
[rn/image

View File

@ -4,12 +4,14 @@
(def icon-path "./resources/images/icons2/")
(defn combine-path [path el]
(defn combine-path
[path el]
(if (System/getenv "COMPONENT_TEST")
(str "." path el "@2x.png")
(str "." path el ".png")))
(defn require-icon [size path]
(defn require-icon
[size path]
(fn [el]
(let [s (combine-path path el)
k (-> el
@ -19,18 +21,21 @@
(str size))]
[k `(js/require ~s)])))
(defn get-files [path]
(defn get-files
[path]
(->> (io/file path)
file-seq
(filter #(string/ends-with? % "png"))
(map #(first (string/split (.getName %) #"@")))
distinct))
(defn get-icons [size]
(defn get-icons
[size]
(let [path (str icon-path size "x" size "/")]
(into {} (map (require-icon size path) (get-files path)))))
(defmacro resolve-icons []
(defmacro resolve-icons
[]
(merge
(get-icons 12)
(get-icons 16)

View File

@ -4,7 +4,8 @@
(def icons (icons/resolve-icons))
(defn icon-source [icon]
(defn icon-source
[icon]
(if-let [icon (get icons (name icon))]
icon
(do

View File

@ -1,19 +1,20 @@
(ns quo2.components.info.info-message
(:require [quo2.theme :as theme]
[react-native.core :as rn]
(:require [quo2.components.icon :as quo2.icons]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as quo2.icons]))
[quo2.theme :as theme]
[react-native.core :as rn]))
(def themes
{:light {:default colors/neutral-40
:success colors/success-50
:error colors/danger-50}
:dark {:default colors/neutral-60
:success colors/success-60
:error colors/danger-60}})
{:light {:default colors/neutral-40
:success colors/success-50
:error colors/danger-50}
:dark {:default colors/neutral-60
:success colors/success-60
:error colors/danger-60}})
(defn get-color [key]
(defn get-color
[key]
(get-in themes [(theme/get-theme) key]))
(defn info-message
@ -26,17 +27,20 @@
:icon-color colors/white ;; icon color override
:no-icon-color? false ;; disable tint color for icon"
[{:keys [type size icon text-color icon-color no-icon-color?]} message]
(let [weight (if (= size :default) :regular :medium)
size (if (= size :default) :paragraph-2 :label)
text-color (or text-color (get-color type))
icon-color (or icon-color text-color)]
[rn/view {:style {:flex-direction :row
:flex 1}}
[quo2.icons/icon icon {:color icon-color
:no-color no-icon-color?
:size 12
:container-style {:margin-top 3}}]
[text/text {:size size
:weight weight
:style {:color text-color
:margin-horizontal 8}} message]]))
(let [weight (if (= size :default) :regular :medium)
size (if (= size :default) :paragraph-2 :label)
text-color (or text-color (get-color type))
icon-color (or icon-color text-color)]
[rn/view
{:style {:flex-direction :row
:flex 1}}
[quo2.icons/icon icon
{:color icon-color
:no-color no-icon-color?
:size 12
:container-style {:margin-top 3}}]
[text/text
{:size size
:weight weight
:style {:color text-color
:margin-horizontal 8}} message]]))

View File

@ -1,44 +1,46 @@
(ns quo2.components.info.information-box
(:require [quo2.theme :as theme]
[react-native.core :as rn]
[clojure.string :as string]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as quo2.icons]
(:require [clojure.string :as string]
[quo2.components.buttons.button :as quo2.button]
[quo2.components.info.info-message :as info-message]))
[quo2.components.icon :as quo2.icons]
[quo2.components.info.info-message :as info-message]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(def themes
{:light {:default {:bg colors/white
:border colors/neutral-20
:icon colors/neutral-50
:text colors/neutral-100}
:informative {:bg colors/primary-50-opa-5
:border colors/primary-50-opa-10
:icon colors/primary-50
:text colors/neutral-100}
:error {:bg colors/danger-50-opa-5
:border colors/danger-50-opa-10
:icon colors/danger-50
:text colors/danger-50}
{:light {:default {:bg colors/white
:border colors/neutral-20
:icon colors/neutral-50
:text colors/neutral-100}
:informative {:bg colors/primary-50-opa-5
:border colors/primary-50-opa-10
:icon colors/primary-50
:text colors/neutral-100}
:error {:bg colors/danger-50-opa-5
:border colors/danger-50-opa-10
:icon colors/danger-50
:text colors/danger-50}
:close-button colors/neutral-100}
:dark {:default {:bg colors/neutral-90
:border colors/neutral-70
:icon colors/neutral-40
:text colors/white}
:informative {:bg colors/primary-50-opa-5
:border colors/primary-50-opa-10
:icon colors/white
:text colors/white}
:error {:bg colors/danger-50-opa-5
:border colors/danger-50-opa-10
:icon colors/danger-50
:text colors/danger-50}
:dark {:default {:bg colors/neutral-90
:border colors/neutral-70
:icon colors/neutral-40
:text colors/white}
:informative {:bg colors/primary-50-opa-5
:border colors/primary-50-opa-10
:icon colors/white
:text colors/white}
:error {:bg colors/danger-50-opa-5
:border colors/danger-50-opa-10
:icon colors/danger-50
:text colors/danger-50}
:close-button colors/white}})
(defn get-color [key]
(defn get-color
[key]
(get-in themes [(theme/get-theme) key]))
(defn get-color-by-type [type key]
(defn get-color-by-type
[type key]
(get-in themes [(theme/get-theme) type key]))
(defn information-box
@ -54,40 +56,47 @@
:button-label \"PressMe\" ;; add action button with label
:on-button-press action ;; (required for information box with button-label)
:on-close on-close ;; (optional on-close call)"
[{:keys [type closable? closed? id icon style button-label on-button-press on-close no-icon-color?]} message]
[{:keys [type closable? closed? id icon style button-label on-button-press on-close no-icon-color?]}
message]
(let [background-color (get-color-by-type type :bg)
border-color (get-color-by-type type :border)
icon-color (get-color-by-type type :icon)
text-color (get-color-by-type type :text)
include-button? (not (string/blank? button-label))]
(when-not closed?
[rn/view {:accessibility-label (or id :information-box)
:style (merge {:background-color background-color
:border-color border-color
:border-width 1
:border-radius 12
:padding-top (if include-button? 10 11)
:padding-bottom (if include-button? 12 11)
:padding-horizontal 16} style)}
[rn/view {:style {:flex-direction :row
:align-self :flex-start}}
[info-message/info-message {:size :default
:icon icon
:text-color text-color
:icon-color icon-color
:no-icon-color? no-icon-color?} message]
[rn/view
{:accessibility-label (or id :information-box)
:style (merge {:background-color background-color
:border-color border-color
:border-width 1
:border-radius 12
:padding-top (if include-button? 10 11)
:padding-bottom (if include-button? 12 11)
:padding-horizontal 16}
style)}
[rn/view
{:style {:flex-direction :row
:align-self :flex-start}}
[info-message/info-message
{:size :default
:icon icon
:text-color text-color
:icon-color icon-color
:no-icon-color? no-icon-color?} message]
(when closable?
[rn/touchable-opacity
{:on-press on-close
:accessibility-label (str (or id "information-box") "-close-button")}
[quo2.icons/icon :i/close {:size 12
:color (get-color :close-button)
:container-style {:margin-top 4
:margin-left 8}}]])]
[quo2.icons/icon :i/close
{:size 12
:color (get-color :close-button)
:container-style {:margin-top 4
:margin-left 8}}]])]
(when include-button?
[quo2.button/button {:type :primary
:size 24
:on-press on-button-press
:style {:margin-left 20
:margin-top 8
:align-self :flex-start}} button-label])])))
[quo2.button/button
{:type :primary
:size 24
:on-press on-button-press
:style {:margin-left 20
:margin-top 8
:align-self :flex-start}} button-label])])))

View File

@ -7,25 +7,28 @@
[quo2.theme :as theme]
[react-native.core :as rn]))
(defn list-item [{:keys [name locked? mentions-count unread-messages?
muted? is-active-channel? emoji channel-color on-press]
:or {channel-color colors/primary-50}}]
(defn list-item
[{:keys [name locked? mentions-count unread-messages?
muted? is-active-channel? emoji channel-color on-press]
:or {channel-color colors/primary-50}}]
[rn/touchable-opacity {:on-press on-press}
[rn/view {:style (merge {:height 48
:display :flex
:border-radius 12
:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:padding-left 12
:padding-right 12}
(when is-active-channel?
{:background-color (colors/theme-alpha channel-color 0.05 0.05)}))}
[rn/view {:display :flex
:flex-direction :row
:justify-content :flex-start
:align-items :center}
[rn/view
{:style (merge {:height 48
:display :flex
:border-radius 12
:flex-direction :row
:justify-content :space-between
:align-items :center
:width "100%"
:padding-left 12
:padding-right 12}
(when is-active-channel?
{:background-color (colors/theme-alpha channel-color 0.05 0.05)}))}
[rn/view
{:display :flex
:flex-direction :row
:justify-content :flex-start
:align-items :center}
[channel-avatar/channel-avatar
{:big? true
:locked? locked?
@ -35,7 +38,8 @@
{:style (merge {:margin-left 12}
(when (and (not locked?) muted?)
{:color (if (theme/dark?) colors/neutral-60 colors/neutral-40)}))
:weight :medium :size :paragraph-1} (str "# " name)]]
:weight :medium
:size :paragraph-1} (str "# " name)]]
[rn/view {:style {:height 20}}
(when (and (not locked?)
muted?)
@ -45,8 +49,9 @@
(when (and (not locked?)
(not muted?)
(pos? (int mentions-count)))
[rn/view {:style {:margin-right 2
:margin-top 2}}
[rn/view
{:style {:margin-right 2
:margin-top 2}}
[quo2.counter/counter {:override-bg-color channel-color} mentions-count]])
(when (and (not locked?)
(not muted?)

View File

@ -1,17 +1,18 @@
(ns quo2.components.list-items.menu-item
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors :refer [theme-colors]]
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.icon :as icons]))
[quo2.foundations.colors :as colors :refer [theme-colors]]
[react-native.core :as rn]))
(defn themes [type]
(defn themes
[type]
(case type
:main {:icon-color (theme-colors colors/neutral-50 colors/neutral-10)
:background (theme-colors colors/white colors/neutral-90)
:text-color (theme-colors colors/neutral-100 colors/white)}
:danger {:icon-color (theme-colors colors/danger-50 colors/danger-60)
:background (theme-colors colors/white colors/neutral-90)
:text-color (theme-colors colors/danger-50 colors/danger-60)}))
:main {:icon-color (theme-colors colors/neutral-50 colors/neutral-10)
:background (theme-colors colors/white colors/neutral-90)
:text-color (theme-colors colors/neutral-100 colors/white)}
:danger {:icon-color (theme-colors colors/danger-50 colors/danger-60)
:background (theme-colors colors/white colors/neutral-90)
:text-color (theme-colors colors/danger-50 colors/danger-60)}))
(defn menu-item
[{:keys [type title accessibility-label icon on-press]
@ -19,25 +20,28 @@
(let [{:keys [icon-color text-color background]} (themes type)]
[rn/touchable-opacity
(merge {:accessibility-label accessibility-label
:style {:background-color background
:height 48
:flex-direction :row
:align-items :center}}
:style {:background-color background
:height 48
:flex-direction :row
:align-items :center}}
(when on-press
{:on-press on-press}))
[rn/view {:style {:flex-direction :row
:flex-grow 0
:flex-shrink 1
:padding-horizontal 20}}
[rn/view {:style {:width 20
:height 20
:align-items :center
:justify-content :center
:margin-right 12}}
[rn/view
{:style {:flex-direction :row
:flex-grow 0
:flex-shrink 1
:padding-horizontal 20}}
[rn/view
{:style {:width 20
:height 20
:align-items :center
:justify-content :center
:margin-right 12}}
[icons/icon icon {:color icon-color}]]
[text/text {:weight :medium
:style {:color text-color}
:ellipsize-mode :tail
:number-of-lines 1
:size :paragraph-1}
[text/text
{:weight :medium
:style {:color text-color}
:ellipsize-mode :tail
:number-of-lines 1
:size :paragraph-1}
title]]]))

View File

@ -1,97 +1,112 @@
(ns quo2.components.list-items.preview-list
(:require [react-native.core :as rn]
[react-native.hole-view :as hole-view]
[react-native.fast-image :as fast-image]
[quo2.foundations.colors :as colors]
(:require [quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.icon :as quo2.icons]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.markdown.text :as quo2.text]))
[quo2.components.markdown.text :as quo2.text]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.fast-image :as fast-image]
[react-native.hole-view :as hole-view]))
(def params {32 {:border-radius {:circular 16 :rounded 10}
:hole-radius {:circular 18 :rounded 12}
:margin-left -8
:hole-size 36
:hole-x 22
:hole-y -2}
24 {:border-radius {:circular 12 :rounded 8}
:hole-radius {:circular 13 :rounded 9}
:margin-left -4
:hole-size 26
:hole-x 19
:hole-y -1}
16 {:border-radius {:circular 8 :rounded 8}
:hole-radius {:circular 9 :rounded 9}
:margin-left -4
:hole-size 18
:hole-x 11
:hole-y -1}})
(def params
{32 {:border-radius {:circular 16 :rounded 10}
:hole-radius {:circular 18 :rounded 12}
:margin-left -8
:hole-size 36
:hole-x 22
:hole-y -2}
24 {:border-radius {:circular 12 :rounded 8}
:hole-radius {:circular 13 :rounded 9}
:margin-left -4
:hole-size 26
:hole-x 19
:hole-y -1}
16 {:border-radius {:circular 8 :rounded 8}
:hole-radius {:circular 9 :rounded 9}
:margin-left -4
:hole-size 18
:hole-x 11
:hole-y -1}})
;; TODO - Add avatar components for other types once implemented
(defn avatar [item type size border-radius]
(defn avatar
[item type size border-radius]
(case type
:user [user-avatar/user-avatar
(merge {:ring? false
:status-indicator? false
:size (case size 32 :small 24 :xs 16 :xxxs)}
item)]
(:photo :collectible) [fast-image/fast-image {:source (:source item)
:style {:width size
:height size
:border-radius border-radius}}]))
:user [user-avatar/user-avatar
(merge {:ring? false
:status-indicator? false
:size (case size
32 :small
24 :xs
16 :xxxs)}
item)]
(:photo :collectible) [fast-image/fast-image
{:source (:source item)
:style {:width size
:height size
:border-radius border-radius}}]))
(defn list-item [index type size item list-size margin-left
hole-size hole-radius hole-x hole-y border-radius]
(defn list-item
[index type size item list-size margin-left
hole-size hole-radius hole-x hole-y border-radius]
(let [last-item? (= index (- list-size 1))]
[hole-view/hole-view {:style {:margin-left (if (= index 0) 0 margin-left)}
:holes (if last-item? []
[{:x hole-x
:y hole-y
:width hole-size
:height hole-size
:borderRadius hole-radius}])}
[hole-view/hole-view
{:style {:margin-left (if (= index 0) 0 margin-left)}
:holes (if last-item?
[]
[{:x hole-x
:y hole-y
:width hole-size
:height hole-size
:borderRadius hole-radius}])}
[avatar item type size border-radius]]))
(defn get-overflow-color [transparent? transparent-color light-color dark-color override-theme]
(defn get-overflow-color
[transparent? transparent-color light-color dark-color override-theme]
(if transparent?
transparent-color
(colors/theme-colors light-color dark-color override-theme)))
(defn overflow-label [label size transparent? border-radius margin-left override-theme more-than-99-label]
[rn/view {:style {:width size
:height size
:margin-left margin-left
:border-radius border-radius
:justify-content :center
:align-items :center
:background-color (get-overflow-color
transparent?
colors/white-opa-10
colors/neutral-20
colors/neutral-70
override-theme)}}
(defn overflow-label
[label size transparent? border-radius margin-left override-theme more-than-99-label]
[rn/view
{:style {:width size
:height size
:margin-left margin-left
:border-radius border-radius
:justify-content :center
:align-items :center
:background-color (get-overflow-color
transparent?
colors/white-opa-10
colors/neutral-20
colors/neutral-70
override-theme)}}
(if (= size 16)
[quo2.icons/icon :i/more {:size 12
:color (get-overflow-color
transparent?
colors/white-opa-70
colors/neutral-50
colors/neutral-40
override-theme)}]
[quo2.text/text {:size (if (= size 32) :paragraph-2 :label)
:weight :medium
:style {:color (get-overflow-color
transparent?
colors/white-opa-70
colors/neutral-60
colors/neutral-40
override-theme)
:margin-left -2}}
[quo2.icons/icon :i/more
{:size 12
:color (get-overflow-color
transparent?
colors/white-opa-70
colors/neutral-50
colors/neutral-40
override-theme)}]
[quo2.text/text
{:size (if (= size 32) :paragraph-2 :label)
:weight :medium
:style {:color (get-overflow-color
transparent?
colors/white-opa-70
colors/neutral-60
colors/neutral-40
override-theme)
:margin-left -2}}
;; If overflow label is below 100, show label as +label (ex. +30), else just show 99+
(if (< label 100)
(str "+" label)
more-than-99-label)])])
(defn border-type [type]
(defn border-type
[type]
(case type
(:account :collectible :photo) :rounded
:circular))
@ -120,4 +135,5 @@
[list-item index type size (get items-arr index) list-size
margin-left hole-size hole-radius hole-x hole-y border-radius])
(when (> list-size 4)
[overflow-label (- list-size 3) size transparent? border-radius margin-left override-theme more-than-99-label])]))
[overflow-label (- list-size 3) size transparent? border-radius margin-left override-theme
more-than-99-label])]))

View File

@ -1,25 +1,27 @@
(ns quo2.components.loaders.skeleton
(:require [react-native.core :as rn]
(:require [quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.masked-view :as masked-view]
[reagent.core :as reagent]
[react-native.reanimated :as reanimated]
[quo2.foundations.colors :as colors]))
[reagent.core :as reagent]))
(def message-skeleton-height 54)
(def avatar-skeleton-size 32)
(def message-content-width [{:author 80
:message 249}
{:author 124
:message 156}
{:author 96
:message 212}
{:author 112
:message 144}])
(def message-content-width
[{:author 80
:message 249}
{:author 124
:message 156}
{:author 96
:message 212}
{:author 112
:message 144}])
;; Standlone message skeleton
(defn message-skeleton []
(defn message-skeleton
[]
[:f>
(fn []
(let [color (colors/theme-colors colors/neutral-5 colors/neutral-70)
@ -31,47 +33,56 @@
translate-x (reanimated/use-shared-value (- window-width))
animated-gradient-style (reanimated/apply-animations-to-style
{:transform [{:translateX translate-x}]}
{:width window-width
:height "100%"})]
{:width window-width
:height "100%"})]
(reanimated/animate-shared-value-with-repeat translate-x window-width 1000 :linear (- 1) false)
[masked-view/masked-view
{:style {:height message-skeleton-height}
{:style {:height message-skeleton-height}
:maskElement (reagent/as-element
[rn/view {:style {:height message-skeleton-height
:flex-direction :row
:padding-vertical 11
:background-color :transparent
:padding-left 21}}
[rn/view {:style {:height avatar-skeleton-size
:width avatar-skeleton-size
:border-radius (/ avatar-skeleton-size 2)
:background-color color
:overflow :hidden}}]
[rn/view {:style {:padding-left 8
:background-color :transparent}}
[rn/view {:style {:height 8
:width author-width
:border-radius 6
:background-color color
:margin-bottom 8
:overflow :hidden}}]
[rn/view {:style {:height 16
:width message-width
:border-radius 6
:overflow :hidden
:background-color color}}]]])}
[rn/view {:style {:flex 1
:background-color color}}
[reanimated/linear-gradient {:colors [color color loading-color color color]
:start {:x 0 :y 0}
:end {:x 1 :y 0}
:style animated-gradient-style}]]]))])
[rn/view
{:style {:height message-skeleton-height
:flex-direction :row
:padding-vertical 11
:background-color :transparent
:padding-left 21}}
[rn/view
{:style {:height avatar-skeleton-size
:width avatar-skeleton-size
:border-radius (/ avatar-skeleton-size 2)
:background-color color
:overflow :hidden}}]
[rn/view
{:style {:padding-left 8
:background-color :transparent}}
[rn/view
{:style {:height 8
:width author-width
:border-radius 6
:background-color color
:margin-bottom 8
:overflow :hidden}}]
[rn/view
{:style {:height 16
:width message-width
:border-radius 6
:overflow :hidden
:background-color color}}]]])}
[rn/view
{:style {:flex 1
:background-color color}}
[reanimated/linear-gradient
{:colors [color color loading-color color color]
:start {:x 0 :y 0}
:end {:x 1 :y 0}
:style animated-gradient-style}]]]))])
(defn skeleton [parent-height]
(defn skeleton
[parent-height]
(let [number-of-skeletons (int (Math/floor (/ parent-height message-skeleton-height)))]
[rn/view {:style {:background-color (colors/theme-colors
colors/white
colors/neutral-90)
:flex 1}}
[rn/view
{:style {:background-color (colors/theme-colors
colors/white
colors/neutral-90)
:flex 1}}
(for [n (range number-of-skeletons)]
[message-skeleton {:key n}])]))

View File

@ -1,11 +1,12 @@
(ns quo2.components.markdown.text
(:require [react-native.core :as rn]
[quo2.theme :as theme]
[quo2.foundations.colors :as colors]
(:require [quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]
[quo2.theme :as theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
(defn text-style [{:keys [size align weight style]}]
(defn text-style
[{:keys [size align weight style]}]
(merge (case (or weight :regular)
:regular typography/font-regular
:medium typography/font-medium
@ -26,10 +27,12 @@
style
(assoc style :color (if (= (theme/get-theme) :dark) colors/white colors/neutral-100)))))
(defn text []
(defn text
[]
(let [this (reagent/current-component)
props (reagent/props this)
style (text-style props)]
(into [rn/text (merge {:style style}
(dissoc props :style :size :align :weight :color))]
(into [rn/text
(merge {:style style}
(dissoc props :style :size :align :weight :color))]
(reagent/children this))))

View File

@ -8,10 +8,12 @@
:flex-direction :row
:align-items :center})
(defn ens-text []
(defn ens-text
[]
{:color (colors/theme-colors colors/neutral-100 colors/white)})
(defn nickname-text []
(defn nickname-text
[]
{:color (colors/theme-colors colors/neutral-100 colors/white)})
(def middle-dot-nickname
@ -26,7 +28,8 @@
{:color colors/neutral-50
:margin-left 4})
(defn profile-name-text [nickname?]
(defn profile-name-text
[nickname?]
{:color (if nickname?
(colors/theme-colors colors/neutral-60 colors/neutral-40)
(colors/theme-colors colors/neutral-100 colors/white))})
@ -34,6 +37,7 @@
(def icon-container
{:margin-left 4})
(defn time-text [ens?]
(defn time-text
[ens?]
{:color colors/neutral-50
:margin-left (if ens? 8 4)})

View File

@ -1,60 +1,68 @@
(ns quo2.components.messages.author.view
(:require
[quo2.components.messages.author.style :as style]
[react-native.core :as rn]
[quo2.components.markdown.text :as text]
[quo2.components.icon :as icons]
[status-im.utils.utils :as utils]
[clojure.string :as string]))
(:require [clojure.string :as string]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.messages.author.style :as style]
[react-native.core :as rn]
[status-im.utils.utils :as utils]))
(def middle-dot "·")
(defn display-name [{:keys [profile-name nickname ens-name text-style]}]
(defn display-name
[{:keys [profile-name nickname ens-name text-style]}]
(let [ens? (-> ens-name string/blank? not)
nickname? (-> nickname string/blank? not)]
(if ens?
[text/text (merge {:weight :semi-bold
:size :paragraph-2
:style (style/ens-text)}
text-style)
[text/text
(merge {:weight :semi-bold
:size :paragraph-2
:style (style/ens-text)}
text-style)
ens-name]
[:<>
(if nickname?
[text/text (merge {:weight :semi-bold
:size :paragraph-2
:style (style/nickname-text)}
text-style)
[text/text
(merge {:weight :semi-bold
:size :paragraph-2
:style (style/nickname-text)}
text-style)
nickname]
[text/text (merge {:weight :semi-bold
:size :paragraph-2
:style (style/profile-name-text nickname?)}
text-style)
[text/text
(merge {:weight :semi-bold
:size :paragraph-2
:style (style/profile-name-text nickname?)}
text-style)
profile-name])])))
(defn author [{:keys [profile-name nickname chat-key ens-name time-str contact? verified? untrustworthy?]}]
(defn author
[{:keys [profile-name nickname chat-key ens-name time-str contact? verified? untrustworthy?]}]
[:f>
(fn []
(let [ens? (-> ens-name string/blank? not)
(let [ens? (-> ens-name string/blank? not)
nickname? (-> nickname string/blank? not)]
[rn/view {:style style/container}
(if ens?
[text/text {:weight :semi-bold
:size :paragraph-2
:style (style/ens-text)}
[text/text
{:weight :semi-bold
:size :paragraph-2
:style (style/ens-text)}
ens-name]
[:<>
(when nickname?
[:<>
[text/text {:weight :semi-bold
:size :paragraph-2
:style (style/nickname-text)}
[text/text
{:weight :semi-bold
:size :paragraph-2
:style (style/nickname-text)}
nickname]
[text/text {:size :paragraph-2
:style style/middle-dot-nickname}
[text/text
{:size :paragraph-2
:style style/middle-dot-nickname}
middle-dot]])
[text/text {:weight (if nickname? :medium :semi-bold)
:size :paragraph-2
:style (style/profile-name-text nickname?)}
[text/text
{:weight (if nickname? :medium :semi-bold)
:size :paragraph-2
:style (style/profile-name-text nickname?)}
profile-name]])
(when contact?
[icons/icon :main-icons2/contact
@ -73,16 +81,19 @@
:no-color true
:container-style style/icon-container}])
(when-not ens?
[text/text {:monospace true
:size :paragraph-2
:style style/chat-key-text}
[text/text
{:monospace true
:size :paragraph-2
:style style/chat-key-text}
(utils/get-shortened-address chat-key)])
(when-not ens?
[text/text {:monospace true
:size :paragraph-2
:style style/middle-dot-chat-key}
[text/text
{:monospace true
:size :paragraph-2
:style style/middle-dot-chat-key}
middle-dot])
[text/text {:monospace true
:size :paragraph-2
:style (style/time-text ens?)}
[text/text
{:monospace true
:size :paragraph-2
:style (style/time-text ens?)}
time-str]]))])

View File

@ -1,12 +1,11 @@
(ns quo2.components.messages.gap
(:require
[oops.core :refer [oget]]
[react-native.core :as rn]
[quo2.theme :as theme]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[reagent.core :as reagent]))
(:require [oops.core :refer [oget]]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
;;; helpers
(def themes
@ -17,7 +16,8 @@
:time colors/neutral-40
:background colors/neutral-95}})
(defn get-color [key]
(defn get-color
[key]
(get-in themes [(theme/get-theme) key]))
(def ui-images
@ -27,45 +27,51 @@
:dark {:horizontal (js/require "../resources/images/ui/message-gap-hborder-dark.png")
:circles (js/require "../resources/images/ui/message-gap-circle-bg-dark.png")}})
(defn get-image [key]
(defn get-image
[key]
(get-in ui-images [(theme/get-theme) key]))
;;; components
;;;; borders
(defn hborder [{:keys [type style]}]
[rn/image {:source (get-image :horizontal)
:resize-mode :repeat
:style (merge {:position :absolute
:left 0
:padding-horizontal 4
:overflow :hidden
:width "110%"
:height 8
:margin-left -4}
(if (= type :top)
{:top 0}
{:transform [{:rotateZ "180deg"}]
:bottom 0})
style)}])
(defn hborder
[{:keys [type style]}]
[rn/image
{:source (get-image :horizontal)
:resize-mode :repeat
:style (merge {:position :absolute
:left 0
:padding-horizontal 4
:overflow :hidden
:width "110%"
:height 8
:margin-left -4}
(if (= type :top)
{:top 0}
{:transform [{:rotateZ "180deg"}]
:bottom 0})
style)}])
(defn vborder [type body-height]
(defn vborder
[type body-height]
(let [height @body-height
img (get-image :vertical)]
(when (and img height)
[rn/image {:source img
:resize-mode :repeat
:style (merge
{:position :absolute
:top 4
:height (- height 8)
:width 4}
(if (= type :left)
{:left 0}
{:transform [{:rotate "180deg"}]
:right 0}))}])))
[rn/image
{:source img
:resize-mode :repeat
:style (merge
{:position :absolute
:top 4
:height (- height 8)
:width 4}
(if (= type :left)
{:left 0}
{:transform [{:rotate "180deg"}]
:right 0}))}])))
;;;; others
(defn circle []
(defn circle
[]
[rn/view
{:width 9
:height 9
@ -75,34 +81,40 @@
:border-color (get-color :icon)
:border-radius 50}])
(defn timestamp [str]
[text/text {:size :label
:style {:text-transform :none
:color (get-color :time)}} str])
(defn timestamp
[str]
[text/text
{:size :label
:style {:text-transform :none
:color (get-color :time)}} str])
(defn info-button [on-press]
(defn info-button
[on-press]
[rn/touchable-without-feedback
{:on-press on-press}
[icon/icon "message-gap-info" {:size 12 :no-color true :container-style {:padding 4}}]])
;;;; timeline/body
(defn timeline []
[rn/view {:flex 0
:margin-right 20
:align-items :center
:width 9
:justify-content :space-between}
(defn timeline
[]
[rn/view
{:flex 0
:margin-right 20
:align-items :center
:width 9
:justify-content :space-between}
[circle]
[rn/image {:style {:flex 1} :source (get-image :circles) :resize-mode :repeat}]
[circle]])
(defn body [timestamp-far timestamp-near on-info-button-pressed on-press warning-label]
(defn body
[timestamp-far timestamp-near on-info-button-pressed on-press warning-label]
[rn/view {:flex 1}
[rn/view
{:flex-direction :row
:align-items :center
:justify-content :space-between
:margin-right 2}
{:flex-direction :row
:align-items :center
:justify-content :space-between
:margin-right 2}
[timestamp timestamp-far]
(when on-info-button-pressed [info-button on-info-button-pressed])]
@ -131,13 +143,14 @@
:flex 1}
[hborder {:type :top}]
[hborder {:type :bottom}]
[rn/view (merge {:width "100%"
:background-color (get-color :background)
:flex-direction :row
:padding 20
:padding-left 31
:margin-vertical 4}
style)
[rn/view
(merge {:width "100%"
:background-color (get-color :background)
:flex-direction :row
:padding 20
:padding-left 31
:margin-vertical 4}
style)
[timeline]
[body timestamp-far timestamp-near on-info-button-pressed on-press warning-label]]

View File

@ -8,9 +8,10 @@
[react-native.reanimated :as reanimated]
[utils.string :as utils]))
(def themes-landed {:pinned colors/primary-50-opa-5
:added colors/primary-50-opa-5
:deleted colors/danger-50-opa-5})
(def themes-landed
{:pinned colors/primary-50-opa-5
:added colors/primary-50-opa-5
:deleted colors/danger-50-opa-5})
(def themes
{:light {:text colors/neutral-100
@ -24,115 +25,149 @@
:pressed colors/neutral-80
:landed themes-landed}}})
(defn get-color [& keys]
(defn get-color
[& keys]
(reduce (fn [acc k] (get acc k (reduced acc)))
((theme/get-theme) themes) (vec keys)))
((theme/get-theme) themes)
(vec keys)))
(defn sm-timestamp [timestamp-str]
(defn sm-timestamp
[timestamp-str]
[rn/view {:margin-left 6}
[text/text {:size :label
:style {:color (get-color :time)
:text-transform :none}}
[text/text
{:size :label
:style {:color (get-color :time)
:text-transform :none}}
timestamp-str]])
(defn sm-icon [{:keys [icon color opacity]}]
[rn/view {:align-items :center
:margin-right 8}
[icon-avatar/icon-avatar {:size :medium
:icon icon
:color color
:opacity opacity}]])
(defn sm-icon
[{:keys [icon color opacity]}]
[rn/view
{:align-items :center
:margin-right 8}
[icon-avatar/icon-avatar
{:size :medium
:icon icon
:color color
:opacity opacity}]])
(defn sm-user-avatar [image]
(defn sm-user-avatar
[image]
[rn/view {:margin-right 4}
[user-avatar/user-avatar {:status-indicator? false
:online? false
:size :xxxs
:profile-picture image
:ring? false}]])
[user-avatar/user-avatar
{:status-indicator? false
:online? false
:size :xxxs
:profile-picture image
:ring? false}]])
(defmulti sm-render :type)
(defmethod sm-render :deleted [{:keys [label timestamp-str labels]}]
[rn/view {:align-items :center
:justify-content :space-between
:flex 1
:flex-direction :row}
[rn/view {:align-items :center
:flex-direction :row}
[sm-icon {:icon :main-icons/delete
:color :danger
:opacity 5}]
[text/text {:size :paragraph-2
:style {:color (get-color :text)
:margin-right 5}}
(defmethod sm-render :deleted
[{:keys [label timestamp-str labels]}]
[rn/view
{:align-items :center
:justify-content :space-between
:flex 1
:flex-direction :row}
[rn/view
{:align-items :center
:flex-direction :row}
[sm-icon
{:icon :main-icons/delete
:color :danger
:opacity 5}]
[text/text
{:size :paragraph-2
:style {:color (get-color :text)
:margin-right 5}}
(or (get labels label) label (:message-deleted labels))]
[sm-timestamp timestamp-str]]])
(defmethod sm-render :added [{:keys [state mentions timestamp-str labels]}]
[rn/view {:align-items :center
:flex-direction :row}
[sm-icon {:icon :main-icons/add-user
:color :primary
:opacity (if (= state :landed) 0 5)}]
(defmethod sm-render :added
[{:keys [state mentions timestamp-str labels]}]
[rn/view
{:align-items :center
:flex-direction :row}
[sm-icon
{:icon :main-icons/add-user
:color :primary
:opacity (if (= state :landed) 0 5)}]
[sm-user-avatar (:image (first mentions))]
[text/text {:weight :semi-bold
:size :paragraph-2}
[text/text
{:weight :semi-bold
:size :paragraph-2}
(:name (first mentions))]
[text/text {:size :paragraph-2
:style {:color (get-color :text)
:margin-left 3
:margin-right 3}}
[text/text
{:size :paragraph-2
:style {:color (get-color :text)
:margin-left 3
:margin-right 3}}
(:added labels)]
[sm-user-avatar (:image (second mentions))]
[text/text {:weight :semi-bold
:size :paragraph-2}
[text/text
{:weight :semi-bold
:size :paragraph-2}
(:name (second mentions))]
[sm-timestamp timestamp-str]])
(defmethod sm-render :pinned [{:keys [state pinned-by content timestamp-str labels]}]
[rn/view {:flex-direction :row
:flex 1
:align-items :center}
[sm-icon {:icon :main-icons/pin
:color :primary
:opacity (if (= state :landed) 0 5)}]
[rn/view {:flex-direction :column
:flex 1}
[rn/view {:align-items :baseline
:flex-direction :row}
[text/text {:size :paragraph-2
:weight :semi-bold
:style {:color (get-color :text)}}
(defmethod sm-render :pinned
[{:keys [state pinned-by content timestamp-str labels]}]
[rn/view
{:flex-direction :row
:flex 1
:align-items :center}
[sm-icon
{:icon :main-icons/pin
:color :primary
:opacity (if (= state :landed) 0 5)}]
[rn/view
{:flex-direction :column
:flex 1}
[rn/view
{:align-items :baseline
:flex-direction :row}
[text/text
{:size :paragraph-2
:weight :semi-bold
:style {:color (get-color :text)}}
(utils/truncate-str pinned-by 18)]
[rn/view {:margin-left 4
:margin-right 2}
[text/text {:size :paragraph-2
:style {:color (get-color :text)}}
[rn/view
{:margin-left 4
:margin-right 2}
[text/text
{:size :paragraph-2
:style {:color (get-color :text)}}
(:pinned-a-message labels)]]
[sm-timestamp timestamp-str]]
[rn/view {:flex-direction :row}
[rn/view {:flex-direction :row
:margin-right 4}
[rn/view
{:flex-direction :row
:margin-right 4}
[sm-user-avatar (:image (:mentions content))]
[text/text {:weight :semi-bold
:size :label}
[text/text
{:weight :semi-bold
:size :label}
(:name (:mentions content))]]
(when (seq (:text content))
[rn/view {:margin-right 20
:flex-direction :row
:flex 1}
[text/text {:size :label
:style {:color (get-color :text)}
:number-of-lines 1
:ellipsize-mode :tail}
[rn/view
{:margin-right 20
:flex-direction :row
:flex 1}
[text/text
{:size :label
:style {:color (get-color :text)}
:number-of-lines 1
:ellipsize-mode :tail}
(:text content)]])
[rn/view {:justify-content :flex-end
:flex-direction :row
:min-width 10}
[rn/view
{:justify-content :flex-end
:flex-direction :row
:min-width 10}
(when (seq (:info content))
[text/text {:size :label
:style {:color (get-color :time)}}
[text/text
{:size :label
:style {:color (get-color :time)}}
(utils/truncate-str (:info content) 24)])]]]])
(defn system-message
@ -143,11 +178,16 @@
(get-color :bg (if animate-landing? :landed :default) type))]
(when animate-landing?
(reanimated/animate-shared-value-with-delay
sv-color (get-color :bg :default type) 0 :linear 1000))
sv-color
(get-color :bg :default type)
0
:linear
1000))
[reanimated/touchable-opacity
{:on-press #(when-not non-pressable?
(reanimated/set-shared-value
sv-color (get-color :bg :pressed type)))
sv-color
(get-color :bg :pressed type)))
:style (reanimated/apply-animations-to-style
{:background-color sv-color}
(merge

View File

@ -1,12 +1,13 @@
(ns quo2.components.navigation.bottom-nav-tab
(:require [react-native.core :as rn]
[react-native.reanimated :as reanimated]
[react-native.hole-view :as hole-view]
[quo2.foundations.colors :as colors]
(:require [quo2.components.counter.counter :as counter]
[quo2.components.icons.icons :as icons]
[quo2.components.counter.counter :as counter]))
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.hole-view :as hole-view]
[react-native.reanimated :as reanimated]))
(defn toggle-background-color [background-color press-out? pass-through?]
(defn toggle-background-color
[background-color press-out? pass-through?]
(reanimated/set-shared-value
background-color
(cond
@ -36,44 +37,47 @@
background-color (reanimated/use-shared-value "transparent")
background-animated-style (reanimated/apply-animations-to-style
{:background-color background-color}
{:width 90
:height 40
:border-radius 10})]
{:width 90
:height 40
:border-radius 10})]
[rn/touchable-without-feedback
{:test-ID test-ID
{:test-ID test-ID
:on-press on-press
:on-press-in #(toggle-background-color background-color false pass-through?)
:on-press-out #(toggle-background-color background-color true pass-through?)
:accessibility-label accessibility-label}
[reanimated/view {:style background-animated-style}
[hole-view/hole-view {:style {:padding-left 33
:padding-top 8}
:key new-notifications? ;; Key is required to force removal of holes
:holes (cond
(not new-notifications?) ;; No new notifications, remove holes
[]
[hole-view/hole-view
{:style {:padding-left 33
:padding-top 8}
:key new-notifications? ;; Key is required to force removal of holes
:holes (cond
(not new-notifications?) ;; No new notifications, remove holes
[]
(= notification-indicator :unread-dot)
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
(= notification-indicator :unread-dot)
[{:x 50 :y 5 :width 10 :height 10 :borderRadius 5}]
:else
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
:else
[{:x 47 :y 1 :width 18 :height 18 :borderRadius 7}])}
[reanimated/image
{:style icon-animated-style
{:style icon-animated-style
:source (icons/icon-source (keyword (str icon 24)))}]]
(when new-notifications?
(if (= notification-indicator :counter)
[counter/counter {:outline false
:override-text-color colors/white
:override-bg-color colors/primary-50
:style {:position :absolute
:left 48
:top 2}}
[counter/counter
{:outline false
:override-text-color colors/white
:override-bg-color colors/primary-50
:style {:position :absolute
:left 48
:top 2}}
counter-label]
[rn/view {:style {:width 8
:height 8
:border-radius 4
:top 6
:left 51
:position :absolute
:background-color colors/primary-50}}]))]]))])
[rn/view
{:style {:width 8
:height 8
:border-radius 4
:top 6
:left 51
:position :absolute
:background-color colors/primary-50}}]))]]))])

View File

@ -1,9 +1,10 @@
(ns quo2.components.navigation.floating-shell-button
(:require [react-native.core :as rn]
[react-native.reanimated :as reanimated]
[quo2.components.buttons.dynamic-button :as dynamic-button]))
(:require [quo2.components.buttons.dynamic-button :as dynamic-button]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]))
(defn dynamic-button-view [type dynamic-buttons style]
(defn dynamic-button-view
[type dynamic-buttons style]
(when-let [{:keys [count on-press customization-color label]} (get dynamic-buttons type)]
[dynamic-button/dynamic-button
{:type type
@ -25,23 +26,27 @@
(fn []
(let [original-style (merge {:flex-direction :row
:margin-horizontal 12
:pointer-events :box-none} style)
:pointer-events :box-none}
style)
animated-style (reanimated/apply-animations-to-style
(if opacity-anim
{:opacity opacity-anim} {})
{:opacity opacity-anim}
{})
original-style)]
[reanimated/view {:style animated-style}
;; Left Section
[rn/view {:style {:flex 1}}
[dynamic-button-view :search dynamic-buttons {:position :absolute
:right 8}]]
[dynamic-button-view :search dynamic-buttons
{:position :absolute
:right 8}]]
;; Mid Section (jump-to)
[dynamic-button-view :jump-to dynamic-buttons nil]
;; Right Section
;; Right Section
[rn/view {:style {:flex 1}}
[rn/view {:style {:position :absolute
:flex-direction :row
:right 0}}
[rn/view
{:style {:position :absolute
:flex-direction :row
:right 0}}
[dynamic-button-view :mention dynamic-buttons {:margin-left 8}]
[dynamic-button-view :notification-down dynamic-buttons {:margin-left 8}]
[dynamic-button-view :notification-up dynamic-buttons {:margin-left 8}]

View File

@ -1,11 +1,11 @@
(ns quo2.components.navigation.page-nav
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
(:require [clojure.string :as string]
[quo2.components.avatars.user-avatar :as user-avatar]
[quo2.components.buttons.button :as button]
[quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[clojure.string :as string]
[quo2.components.buttons.button :as button]
[quo2.components.avatars.user-avatar :as user-avatar]))
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(def ^:private centrify-style
{:display :flex
@ -16,7 +16,8 @@
(defn- big? [size] (= size :big))
(defn- icon-props [color size]
(defn- icon-props
[color size]
(merge {:size 20
:container-style {:width (if (big? size)
20
@ -28,22 +29,25 @@
{:color color}
{:no-color true})))
(defn left-section-view [{:keys [on-press icon accessibility-label type] :or {type :grey}}
put-middle-section-on-left?]
(defn left-section-view
[{:keys [on-press icon accessibility-label type] :or {type :grey}}
put-middle-section-on-left?]
[rn/view {:style (when put-middle-section-on-left? {:margin-right 5})}
[button/button {:on-press on-press
:icon true
:type type
:size 32
:accessibility-label accessibility-label}
[button/button
{:on-press on-press
:icon true
:type type
:size 32
:accessibility-label accessibility-label}
icon]])
(defn- mid-section-comp
[{:keys [description-img description-user-icon horizontal-description?
text-secondary-color align-mid? text-color icon main-text type description]}]
[rn/view {:style (assoc centrify-style
:flex-direction :row
:margin-horizontal 2)}
[rn/view
{:style (assoc centrify-style
:flex-direction :row
:margin-horizontal 2)}
(when (or (and (not horizontal-description?)
align-mid?
(not= :text-with-description type))
@ -52,85 +56,98 @@
(if description-img
[rn/view {:margin-right 8}
[description-img]]
[rn/image {:source {:uri description-user-icon}
:style {:width 32
:height 32
:border-radius 32
:margin-right 8}}]))
[rn/view {:style {:flex-direction (if horizontal-description?
:row
:column)}}
[text/text {:size :paragraph-1
:weight :semi-bold
:style {:color text-color
:line-height 21}}
[rn/image
{:source {:uri description-user-icon}
:style {:width 32
:height 32
:border-radius 32
:margin-right 8}}]))
[rn/view
{:style {:flex-direction (if horizontal-description?
:row
:column)}}
[text/text
{:size :paragraph-1
:weight :semi-bold
:style {:color text-color
:line-height 21}}
main-text]
(when description
[text/text {:size :paragraph-2
:weight :medium
:style (cond-> {:padding-right 4
:color text-secondary-color
:line-height 18}
horizontal-description? (assoc :margin-left 4 :margin-top 2))}
[text/text
{:size :paragraph-2
:weight :medium
:style (cond-> {:padding-right 4
:color text-secondary-color
:line-height 18}
horizontal-description? (assoc :margin-left 4 :margin-top 2))}
description])]])
(defn- mid-section-view
[{:keys [horizontal-description? one-icon-align-left? type left-align?
main-text right-icon main-text-icon-color left-icon on-press avatar] :as props}]
(let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95)
main-text right-icon main-text-icon-color left-icon on-press avatar]
:as props}]
(let [text-color (if (colors/dark?) colors/neutral-5 colors/neutral-95)
text-secondary-color (if (colors/dark?) colors/neutral-40 colors/neutral-50)
component-instance [mid-section-comp (assoc props :text-secondary-color text-secondary-color)]]
component-instance [mid-section-comp (assoc props :text-secondary-color text-secondary-color)]]
[rn/touchable-opacity {:on-press on-press}
[rn/view {:style (merge
(if left-align?
align-left
centrify-style)
{:flex 1
:margin-left 4
:text-align-vertical :center})}
[rn/view
{:style (merge
(if left-align?
align-left
centrify-style)
{:flex 1
:margin-left 4
:text-align-vertical :center})}
(case type
:text-only [text/text {:size :paragraph-1
:weight :semi-bold
:style {:color text-color}}
main-text]
:user-avatar [rn/view {:style (assoc centrify-style :flex-direction :row)}
[user-avatar/user-avatar avatar]
[text/text {:size :paragraph-1
:text-only [text/text
{:size :paragraph-1
:weight :semi-bold
:style {:color text-color}}
main-text]
:user-avatar [rn/view {:style (assoc centrify-style :flex-direction :row)}
[user-avatar/user-avatar avatar]
[text/text
{:size :paragraph-1
:weight :semi-bold
:style {:padding-horizontal 4
:color text-color}}
main-text]]
:text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)}
[icons/icon left-icon
(icon-props main-text-icon-color :big)]
[text/text {:size :paragraph-1
:weight :semi-bold
:style {:padding-horizontal 4
:color text-color}}
main-text]
[icons/icon right-icon
(icon-props main-text-icon-color :big)]]
:text-with-one-icon [rn/view {:style {:flex-direction :row}}
(if one-icon-align-left?
[rn/view {:style {:flex-direction :row
:align-items :center}}
(when horizontal-description?
[icons/icon left-icon
(icon-props main-text-icon-color :big)])
component-instance]
[rn/view {:style {:flex-direction :row
:align-items :center}}
component-instance
(when horizontal-description?
[icons/icon left-icon
(icon-props main-text-icon-color :big)])])]
main-text]]
:text-with-two-icons [rn/view {:style (assoc centrify-style :flex-direction :row)}
[icons/icon left-icon
(icon-props main-text-icon-color :big)]
[text/text
{:size :paragraph-1
:weight :semi-bold
:style {:padding-horizontal 4
:color text-color}}
main-text]
[icons/icon right-icon
(icon-props main-text-icon-color :big)]]
:text-with-one-icon [rn/view {:style {:flex-direction :row}}
(if one-icon-align-left?
[rn/view
{:style {:flex-direction :row
:align-items :center}}
(when horizontal-description?
[icons/icon left-icon
(icon-props main-text-icon-color :big)])
component-instance]
[rn/view
{:style {:flex-direction :row
:align-items :center}}
component-instance
(when horizontal-description?
[icons/icon left-icon
(icon-props main-text-icon-color :big)])])]
:text-with-description component-instance)]]))
(defn- right-section-view [right-section-buttons]
[rn/view {:style (assoc centrify-style
:flex-direction :row
:flex 1
:justify-content :flex-end)}
(defn- right-section-view
[right-section-buttons]
[rn/view
{:style (assoc centrify-style
:flex-direction :row
:flex 1
:justify-content :flex-end)}
(let [last-icon-index (-> right-section-buttons count dec)]
(map-indexed (fn [index {:keys [icon on-press type] :or {type :grey}}]
^{:key index}
@ -194,27 +211,30 @@
:icon (:icon mid-section)
:left-icon (:left-icon mid-section)
:avatar (:avatar mid-section)}]
[rn/view {:style (cond-> {:display :flex
:flex-direction :row
;; iPhone 11 Pro's height in Figma divided by Component height 56/1125
:align-items :center
:padding-horizontal 20
:height 56
:justify-content :space-between}
page-nav-background-uri (assoc :background-color page-nav-color)
page-nav-color (assoc :background page-nav-background-uri))}
[rn/view {:style {:flex 1
:flex-direction :row
:align-items :center}}
[rn/view
{:style (cond-> {:display :flex
:flex-direction :row
;; iPhone 11 Pro's height in Figma divided by Component height 56/1125
:align-items :center
:padding-horizontal 20
:height 56
:justify-content :space-between}
page-nav-background-uri (assoc :background-color page-nav-color)
page-nav-color (assoc :background page-nav-background-uri))}
[rn/view
{:style {:flex 1
:flex-direction :row
:align-items :center}}
[left-section-view left-section put-middle-section-on-left?]
(when put-middle-section-on-left?
[mid-section-view (assoc mid-section-props
:left-align? true
:description (:description mid-section)
:description-color (:description-color mid-section)
:description-icon (:description-icon mid-section)
:align-mid? align-mid?
:description-user-icon (:description-user-icon mid-section))])]
[mid-section-view
(assoc mid-section-props
:left-align? true
:description (:description mid-section)
:description-color (:description-color mid-section)
:description-icon (:description-icon mid-section)
:align-mid? align-mid?
:description-user-icon (:description-user-icon mid-section))])]
(when-not put-middle-section-on-left?
[mid-section-view mid-section-props])
[right-section-view right-section-buttons]]))

View File

@ -43,7 +43,8 @@
:align-items :flex-start
:flex 1})
(defn title [replying?]
(defn title
[replying?]
{:color colors/white
:flex-shrink 1
:max-width (when-not replying? "60%")})

View File

@ -4,9 +4,9 @@
[quo2.components.buttons.button :as button]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.components.notifications.activity-log.style :as style]
[quo2.components.tags.status-tags :as status-tags]
[quo2.foundations.colors :as colors]
[quo2.components.notifications.activity-log.style :as style]
[react-native.core :as rn]
[reagent.core :as reagent]
[status-im.i18n.i18n :as i18n]))
@ -21,17 +21,20 @@
(defn- activity-reply-text-input
[reply-input on-update-reply]
[rn/view
[rn/view {:style {:margin-top 16
:margin-bottom 8
:flex-direction :row}}
[text/text {:weight :medium
:style {:flex-grow 1
:color colors/neutral-40}}
[rn/view
{:style {:margin-top 16
:margin-bottom 8
:flex-direction :row}}
[text/text
{:weight :medium
:style {:flex-grow 1
:color colors/neutral-40}}
(i18n/label :t/your-answer)]
[text/text {:style {:flex-shrink 1
:color (if (valid-reply? @reply-input)
colors/neutral-40
colors/danger-60)}}
[text/text
{:style {:flex-shrink 1
:color (if (valid-reply? @reply-input)
colors/neutral-40
colors/danger-60)}}
(str (count @reply-input) "/" max-reply-length)]]
[rn/view
;; TODO(@ilmotta): Replace with quo2 component when available.
@ -58,33 +61,39 @@
(let [first-line-offset (if replying? 4 0)
gap-between-lines 4]
(into [rn/view {:style (assoc style/context-container :margin-top first-line-offset)}]
(mapcat (fn [detail]
^{:key (hash detail)}
(if (string? detail)
(map (fn [s]
[rn/view {:style {:margin-right 4
:margin-top 0}}
[text/text {:size :paragraph-2
:style {:color colors/white}}
s]])
(string/split detail #"\s+"))
[[rn/view {:margin-right 4
:margin-top gap-between-lines}
detail]]))
context))))
(mapcat
(fn [detail]
^{:key (hash detail)}
(if (string? detail)
(map (fn [s]
[rn/view
{:style {:margin-right 4
:margin-top 0}}
[text/text
{:size :paragraph-2
:style {:color colors/white}}
s]])
(string/split detail #"\s+"))
[[rn/view
{:margin-right 4
:margin-top gap-between-lines}
detail]]))
context))))
(defn- activity-message
[{:keys [title body]}]
[rn/view {:style style/message-container}
(when title
[text/text {:size :paragraph-2
:accessibility-label :activity-message-title
:style style/message-title}
[text/text
{:size :paragraph-2
:accessibility-label :activity-message-title
:style style/message-title}
title])
(if (string? body)
[text/text {:style style/message-body
:accessibility-label :activity-message-body
:size :paragraph-1}
[text/text
{:style style/message-body
:accessibility-label :activity-message-body
:size :paragraph-1}
body]
body)])
@ -97,44 +106,51 @@
:flex-basis 0})]
[rn/view style/buttons-container
(when button-1
[button/button (-> button-1
(assoc :size size)
(update :style merge common-style {:margin-right 8}))
[button/button
(-> button-1
(assoc :size size)
(update :style merge common-style {:margin-right 8}))
(:label button-1)])
(when button-2
[button/button (-> button-2
(assoc :size size)
(assoc :disabled (and replying? (not (valid-reply? @reply-input))))
(update :style merge common-style))
[button/button
(-> button-2
(assoc :size size)
(assoc :disabled (and replying? (not (valid-reply? @reply-input))))
(update :style merge common-style))
(:label button-2)])]))
(defn- activity-status
[status]
[rn/view {:style style/status
:accessibility-label :activity-status}
[status-tags/status-tag {:size :small
:label (:label status)
:status status}]])
[rn/view
{:style style/status
:accessibility-label :activity-status}
[status-tags/status-tag
{:size :small
:label (:label status)
:status status}]])
(defn- activity-title
[title replying?]
[text/text {:weight :semi-bold
:accessibility-label :activity-title
:style (style/title replying?)
:size (if replying? :heading-2 :paragraph-1)}
[text/text
{:weight :semi-bold
:accessibility-label :activity-title
:style (style/title replying?)
:size (if replying? :heading-2 :paragraph-1)}
title])
(defn- activity-timestamp
[timestamp]
[text/text {:size :label
:accessibility-label :activity-timestamp
:style style/timestamp}
[text/text
{:size :label
:accessibility-label :activity-timestamp
:style style/timestamp}
timestamp])
(defn- activity-unread-dot
[]
[rn/view {:accessibility-label :activity-unread-indicator
:style style/unread-dot-container}
[rn/view
{:accessibility-label :activity-unread-indicator
:style style/unread-dot-container}
[rn/view {:style style/unread-dot}]])
(defn- footer
@ -159,12 +175,14 @@
replying?
unread?]
:as props}]
[rn/view {:accessibility-label :activity
:style style/container}
[rn/view
{:accessibility-label :activity
:style style/container}
(when-not replying?
[activity-icon icon])
[rn/view {:style {:padding-left (when-not replying? 8)
:flex 1}}
[rn/view
{:style {:padding-left (when-not replying? 8)
:flex 1}}
[rn/view
[rn/view {:style style/top-section-container}
[activity-title title replying?]

View File

@ -1,11 +1,10 @@
(ns quo2.components.notifications.count-down-circle
(:require
[goog.string :as gstring]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[react-native.svg :as svg]
[reagent.core :as reagent]))
(:require [goog.string :as gstring]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]
[react-native.svg :as svg]
[reagent.core :as reagent]))
(defn- get-path-props
[size stroke-width rotation]

View File

@ -1,17 +1,21 @@
(ns quo2.components.notifications.info-count
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]))
(:require [quo2.foundations.colors :as colors]
[quo2.foundations.typography :as typography]
[react-native.core :as rn]))
(defn info-count [count style]
(defn info-count
[count style]
(when (> count 0)
[rn/view {:style (merge {:width 16
:height 16
:position :absolute
:right 22
:border-radius 6
:justify-content :center
:align-items :center
:background-color (colors/theme-colors colors/primary-50 colors/primary-60)}
style)}
[rn/text {:style (merge typography/font-medium typography/label {:color colors/white :text-align :center})} count]]))
[rn/view
{:style (merge {:width 16
:height 16
:position :absolute
:right 22
:border-radius 6
:justify-content :center
:align-items :center
:background-color (colors/theme-colors colors/primary-50 colors/primary-60)}
style)}
[rn/text
{:style (merge typography/font-medium typography/label {:color colors/white :text-align :center})}
count]]))

View File

@ -1,12 +1,14 @@
(ns quo2.components.notifications.notification-dot
(:require [react-native.core :as rn]
[quo2.foundations.colors :as colors]))
(:require [quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn notification-dot [style]
[rn/view {:style (merge {:background-color (colors/theme-colors colors/primary-50 colors/primary-60)
:width 8
:height 8
:border-radius 4
:position :absolute
:z-index 1}
style)}])
(defn notification-dot
[style]
[rn/view
{:style (merge {:background-color (colors/theme-colors colors/primary-50 colors/primary-60)
:width 8
:height 8
:border-radius 4
:position :absolute
:z-index 1}
style)}])

View File

@ -1,12 +1,11 @@
(ns quo2.components.notifications.toast
(:require
[i18n.i18n :as i18n]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.components.notifications.count-down-circle :as count-down-circle]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(:require [i18n.i18n :as i18n]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo2.components.notifications.count-down-circle :as count-down-circle]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(def ^:private themes
{:container {:light {:background-color colors/white-opa-70}

View File

@ -1,33 +1,33 @@
(ns quo2.components.reactions.reaction
(:require [quo2.components.markdown.text :as quo2.text]
[react-native.core :as rn]
[quo2.theme :as theme]
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as quo2.text]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]))
[quo2.theme :as theme]
[react-native.core :as rn]))
(def reaction-styling
{:flex-direction :row
:justify-content :center
:align-items :center
{:flex-direction :row
:justify-content :center
:align-items :center
:padding-horizontal 8
:border-radius 8
:height 24})
:border-radius 8
:height 24})
(defn open-reactions-menu
[{:keys [on-press]}]
(let [dark? (theme/dark?)]
[rn/touchable-opacity
{:on-press on-press
{:on-press on-press
:accessibility-label :emoji-reaction-add
:style (merge reaction-styling
{:padding-horizontal 9
:border-width 1
:margin-top 5
:border-color (if dark?
colors/neutral-70
colors/neutral-30)})}
:style (merge reaction-styling
{:padding-horizontal 9
:border-width 1
:margin-top 5
:border-color (if dark?
colors/neutral-70
colors/neutral-30)})}
[icons/icon :i/add
{:size 20
{:size 20
:color (if dark?
colors/white
colors/neutral-100)}]]))
@ -35,37 +35,41 @@
(defn reaction
"Add your emoji as a param here"
[{:keys [emoji clicks neutral? on-press accessibility-label]}]
(let [dark? (theme/dark?)
text-color (if dark? colors/white
colors/neutral-100)
numeric-value (int clicks)
(let [dark? (theme/dark?)
text-color (if dark?
colors/white
colors/neutral-100)
numeric-value (int clicks)
clicks-positive? (pos? numeric-value)]
[rn/touchable-opacity
{:on-press on-press
:accessibility-label accessibility-label
:style (merge reaction-styling
(cond-> {:background-color
(if dark?
(if neutral?
colors/neutral-70
:transparent)
(if neutral?
colors/neutral-30
:transparent))}
(cond->
{:background-color
(if dark?
(if neutral?
colors/neutral-70
:transparent)
(if neutral?
colors/neutral-30
:transparent))}
(and dark? (not neutral?))
(assoc :border-color colors/neutral-70
:border-width 1)
(and (not dark?) (not neutral?))
(assoc :border-color colors/neutral-30
:border-width 1)))}
[icons/icon emoji {:no-color true
:size 16}]
[quo2.text/text {:size :paragraph-2
:weight :semi-bold
:color text-color
:flex-direction :row
:align-items :center
:justify-content :center}
[icons/icon emoji
{:no-color true
:size 16}]
[quo2.text/text
{:size :paragraph-2
:weight :semi-bold
:color text-color
:flex-direction :row
:align-items :center
:justify-content :center}
(if clicks-positive?
(str " " numeric-value)
"")]]))

View File

@ -2,7 +2,8 @@
(:require [quo2.foundations.colors :as colors]
[react-native.reanimated :as reanimated]))
(defn animated-circle [scale opacity color]
(defn animated-circle
[scale opacity color]
(reanimated/apply-animations-to-style
{:transform [{:scale scale}]
:opacity opacity}
@ -16,11 +17,12 @@
:align-items :center
:z-index 0}))
(defn record-button-big-container [translate-x translate-y opacity]
(defn record-button-big-container
[translate-x translate-y opacity]
(reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}
{:translateX translate-x}]
:opacity opacity}
:opacity opacity}
{:position :absolute
:bottom 0
:right 0
@ -30,7 +32,8 @@
:justify-content :center
:z-index 0}))
(defn record-button-big-body [button-color]
(defn record-button-big-body
[button-color]
{:width 56
:height 56
:border-radius 28
@ -39,7 +42,8 @@
:background-color button-color
:overflow :hidden})
(defn record-button-big-red-overlay [red-overlay-opacity]
(defn record-button-big-red-overlay
[red-overlay-opacity]
(reanimated/apply-animations-to-style
{:opacity red-overlay-opacity}
{:position :absolute
@ -49,7 +53,8 @@
:bottom 0
:background-color colors/danger-50}))
(defn record-button-big-gray-overlay [gray-overlay-opacity]
(defn record-button-big-gray-overlay
[gray-overlay-opacity]
(reanimated/apply-animations-to-style
{:opacity gray-overlay-opacity}
{:position :absolute
@ -59,7 +64,8 @@
:bottom 0
:background-color (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80)}))
(defn record-button-big-icon-container [icon-opacity]
(defn record-button-big-icon-container
[icon-opacity]
(reanimated/apply-animations-to-style
{:opacity icon-opacity}
{}))
@ -70,7 +76,8 @@
:border-radius 4
:background-color colors/white})
(defn send-button-container [opacity]
(defn send-button-container
[opacity]
(reanimated/apply-animations-to-style
{:opacity opacity}
{:justify-content :center
@ -81,7 +88,8 @@
:top 0
:right 20}))
(defn send-button-connector [opacity width height border-radius-first-half border-radius-second-half]
(defn send-button-connector
[opacity width height border-radius-first-half border-radius-second-half]
(reanimated/apply-animations-to-style
{:opacity opacity
:width width
@ -96,7 +104,8 @@
:background-color colors/primary-50
:z-index 0}))
(defn send-button [translate-y opacity]
(defn send-button
[translate-y opacity]
(reanimated/apply-animations-to-style
{:transform [{:translateY translate-y}]
:opacity opacity}
@ -114,7 +123,8 @@
(def send-icon-container
{:z-index 10})
(defn lock-button-container [opacity]
(defn lock-button-container
[opacity]
(reanimated/apply-animations-to-style
{:opacity opacity}
{:transform [{:rotate "45deg"}]
@ -126,7 +136,8 @@
:top 20
:left 20}))
(defn lock-button-connector [opacity width height border-radius-first-half border-radius-second-half]
(defn lock-button-connector
[opacity width height border-radius-first-half border-radius-second-half]
(reanimated/apply-animations-to-style
{:opacity opacity
:width width
@ -141,7 +152,8 @@
:background-color (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80)
:overflow :hidden}))
(defn lock-button [translate-x-y opacity]
(defn lock-button
[translate-x-y opacity]
(reanimated/apply-animations-to-style
{:transform [{:translateX translate-x-y}
{:translateY translate-x-y}]
@ -158,7 +170,8 @@
:overflow :hidden
:z-index 12}))
(defn delete-button-container [opacity]
(defn delete-button-container
[opacity]
(reanimated/apply-animations-to-style
{:opacity opacity}
{:justify-content :center
@ -169,7 +182,8 @@
:bottom 20
:left 0}))
(defn delete-button-connector [opacity width height border-radius-first-half border-radius-second-half]
(defn delete-button-connector
[opacity width height border-radius-first-half border-radius-second-half]
(reanimated/apply-animations-to-style
{:opacity opacity
:width width
@ -184,7 +198,8 @@
:background-color colors/danger-50
:z-index 0}))
(defn delete-button [translate-x opacity]
(defn delete-button
[translate-x opacity]
(reanimated/apply-animations-to-style
{:transform [{:translateX translate-x}]
:opacity opacity}
@ -199,9 +214,10 @@
:left 0
:z-index 11}))
(defn record-button-container [opacity]
(defn record-button-container
[opacity]
(reanimated/apply-animations-to-style
{:opacity opacity}
{:opacity opacity}
{:margin-bottom 32
:margin-right 32}))

View File

@ -1,15 +1,15 @@
(ns quo2.components.record-audio.record-audio.view
(:require [quo.react :refer [memo effect!]]
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.components.icon :as icons]
(:require [cljs-bean.core :as bean]
[oops.core :as oops]
[quo.react :refer [effect! memo]]
[quo2.components.buttons.button :as button]
[quo2.components.icon :as icons]
[quo2.components.record-audio.record-audio.style :as style]
[quo2.foundations.colors :as colors]
[react-native.core :as rn]
[react-native.reanimated :as reanimated]
[reagent.core :as reagent]
[cljs-bean.core :as bean]
[quo2.components.record-audio.record-audio.style :as style]
[status-im.utils.utils :as utils]
[oops.core :as oops]))
[status-im.utils.utils :as utils]))
(def ^:private scale-to-each 1.8)
(def ^:private scale-to-total 2.6)
@ -39,26 +39,30 @@
:x 68
:y 68})
(defn- delete-button-area [active?]
(defn- delete-button-area
[active?]
{:width (if active? 72 82)
:height 56
:x (if active? -16 -32)
:y (if active? 64 70)})
(defn- lock-button-area [active?]
(defn- lock-button-area
[active?]
{:width (if active? 72 100)
:height (if active? 72 102)
:x -32
:y -32})
(defn- send-button-area [active?]
(defn- send-button-area
[active?]
{:width 56
:height (if active? 72 92)
:x 68
:y (if active? -16 -32)})
(defn- touch-inside-area? [{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]}
{:keys [width height x y]}]
(defn- touch-inside-area?
[{:keys [location-x location-y ignore-min-y? ignore-max-y? ignore-min-x? ignore-max-x?]}
{:keys [width height x y]}]
(let [max-x (+ x width)
max-y (+ y height)]
(and
@ -71,12 +75,16 @@
(def record-audio-worklets (js/require "../src/js/record_audio_worklets.js"))
(defn- ring-scale [scale substract]
(defn- ring-scale
[scale substract]
(.ringScale ^js record-audio-worklets
scale
substract))
(defn- record-button-big [recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating? record-button-at-initial-position? locked? reviewing-audio? recording-timer recording-length-ms clear-timeout touch-active?]
(defn- record-button-big
[recording? ready-to-send? ready-to-lock? ready-to-delete? record-button-is-animating?
record-button-at-initial-position? locked? reviewing-audio? recording-timer recording-length-ms
clear-timeout touch-active?]
[:f>
(fn []
(let [scale (reanimated/use-shared-value 1)
@ -85,13 +93,16 @@
animations (map
(fn [index]
(let [ring-scale (ring-scale scale (* scale-padding index))]
{:scale ring-scale
:opacity (reanimated/interpolate ring-scale [1 scale-to-each] [opacity-from 0])}))
{:scale ring-scale
:opacity (reanimated/interpolate ring-scale
[1 scale-to-each]
[opacity-from 0])}))
(range 0 5))
rings-color (cond
@ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque colors/neutral-80)
@ready-to-lock? (colors/theme-colors colors/neutral-80-opa-5-opaque
colors/neutral-80)
@ready-to-delete? colors/danger-50
:else colors/primary-50)
:else colors/primary-50)
translate-y (reanimated/use-shared-value 0)
translate-x (reanimated/use-shared-value 0)
button-color colors/primary-50
@ -115,12 +126,25 @@
(reset! recording-length-ms 0))))
start-animation (fn []
(reanimated/set-shared-value opacity 1)
(reanimated/animate-shared-value-with-timing scale 2.6 signal-anim-duration :linear)
;; TODO: Research if we can implement this with withSequence method from Reanimated 2
;; GitHub issue [#14561]: https://github.com/status-im/status-mobile/issues/14561
(reset! clear-timeout (utils/set-timeout #(do (reanimated/set-shared-value scale scale-to-each)
(reanimated/animate-shared-value-with-delay-repeat scale scale-to-total signal-anim-duration-2 :linear 0 -1))
signal-anim-duration)))
(reanimated/animate-shared-value-with-timing scale
2.6
signal-anim-duration
:linear)
;; TODO: Research if we can implement this with withSequence method
;; from Reanimated 2
;; GitHub issue [#14561]:
;; https://github.com/status-im/status-mobile/issues/14561
(reset! clear-timeout
(utils/set-timeout
#(do (reanimated/set-shared-value scale scale-to-each)
(reanimated/animate-shared-value-with-delay-repeat
scale
scale-to-total
signal-anim-duration-2
:linear
0
-1))
signal-anim-duration)))
stop-animation (fn []
(reanimated/set-shared-value opacity 0)
(reanimated/cancel-animation scale)
@ -129,48 +153,105 @@
start-y-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(reanimated/animate-shared-value-with-timing translate-y -64 250 :easing1)
(reanimated/animate-shared-value-with-delay icon-opacity 0 33.33 :linear 76.66)
(reanimated/animate-shared-value-with-timing translate-y
-64
250
:easing1)
(reanimated/animate-shared-value-with-delay icon-opacity
0 33.33
:linear 76.66)
(utils/set-timeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
250))
reset-y-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y 0 300 :easing1)
(reanimated/animate-shared-value-with-timing icon-opacity 1 500 :linear)
(utils/set-timeout (fn [] (reset! record-button-at-initial-position? true)) 500))
(reanimated/animate-shared-value-with-timing translate-y
0
300
:easing1)
(reanimated/animate-shared-value-with-timing icon-opacity
1
500
:linear)
(utils/set-timeout (fn []
(reset! record-button-at-initial-position? true))
500))
start-x-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(reanimated/animate-shared-value-with-timing translate-x -64 250 :easing1)
(reanimated/animate-shared-value-with-delay icon-opacity 0 33.33 :linear 76.66)
(reanimated/animate-shared-value-with-timing red-overlay-opacity 1 33.33 :linear)
(reanimated/animate-shared-value-with-timing translate-x
-64
250
:easing1)
(reanimated/animate-shared-value-with-delay icon-opacity
0 33.33
:linear 76.66)
(reanimated/animate-shared-value-with-timing red-overlay-opacity
1
33.33
:linear)
(utils/set-timeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
250))
reset-x-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x 0 300 :easing1)
(reanimated/animate-shared-value-with-timing icon-opacity 1 500 :linear)
(reanimated/animate-shared-value-with-timing red-overlay-opacity 0 100 :linear)
(utils/set-timeout (fn [] (reset! record-button-at-initial-position? true)) 500))
(reanimated/animate-shared-value-with-timing translate-x
0
300
:easing1)
(reanimated/animate-shared-value-with-timing icon-opacity
1
500
:linear)
(reanimated/animate-shared-value-with-timing red-overlay-opacity
0
100
:linear)
(utils/set-timeout (fn []
(reset! record-button-at-initial-position? true))
500))
start-x-y-animation (fn []
(reset! record-button-at-initial-position? false)
(reset! record-button-is-animating? true)
(reanimated/animate-shared-value-with-timing translate-y -44 200 :easing1)
(reanimated/animate-shared-value-with-timing translate-x -44 200 :easing1)
(reanimated/animate-shared-value-with-delay icon-opacity 0 33.33 :linear 33.33)
(reanimated/animate-shared-value-with-timing gray-overlay-opacity 1 33.33 :linear)
(reanimated/animate-shared-value-with-timing translate-y
-44
200
:easing1)
(reanimated/animate-shared-value-with-timing translate-x
-44
200
:easing1)
(reanimated/animate-shared-value-with-delay icon-opacity
0 33.33
:linear 33.33)
(reanimated/animate-shared-value-with-timing gray-overlay-opacity
1
33.33
:linear)
(utils/set-timeout (fn []
(reset! record-button-is-animating? false)
(when-not @touch-active? (complete-animation)))
200))
reset-x-y-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y 0 300 :easing1)
(reanimated/animate-shared-value-with-timing translate-x 0 300 :easing1)
(reanimated/animate-shared-value-with-timing icon-opacity 1 500 :linear)
(reanimated/animate-shared-value-with-timing gray-overlay-opacity 0 800 :linear)
(utils/set-timeout (fn [] (reset! record-button-at-initial-position? true)) 800))]
(reanimated/animate-shared-value-with-timing translate-y
0
300
:easing1)
(reanimated/animate-shared-value-with-timing translate-x
0
300
:easing1)
(reanimated/animate-shared-value-with-timing icon-opacity
1
500
:linear)
(reanimated/animate-shared-value-with-timing gray-overlay-opacity
0
800
:linear)
(utils/set-timeout (fn []
(reset! record-button-at-initial-position? true))
800))]
(effect! #(cond
@recording?
(start-animation)
@ -189,15 +270,17 @@
(start-x-animation)
(reset-x-animation))
[@ready-to-delete?])
[reanimated/view {:style (style/record-button-big-container translate-x translate-y opacity)
:pointer-events :none}
[reanimated/view
{:style (style/record-button-big-container translate-x translate-y opacity)
:pointer-events :none}
[:<>
(map-indexed
(fn [id animation]
^{:key id}
[animated-ring {:scale (:scale animation)
:opacity (:opacity animation)
:color rings-color}])
[animated-ring
{:scale (:scale animation)
:opacity (:opacity animation)
:color rings-color}])
animations)]
[rn/view {:style (style/record-button-big-body button-color)}
[reanimated/view {:style (style/record-button-big-red-overlay red-overlay-opacity)}]
@ -207,7 +290,8 @@
[rn/view {:style style/stop-icon}]
[icons/icon :i/audio {:color icon-color}])]]]))])
(defn- send-button [recording? ready-to-send? reviewing-audio?]
(defn- send-button
[recording? ready-to-send? reviewing-audio?]
[:f>
(fn []
(let [opacity (reanimated/use-shared-value 0)
@ -218,33 +302,70 @@
border-radius-first-half (reanimated/use-shared-value 16)
border-radius-second-half (reanimated/use-shared-value 8)
start-y-animation (fn []
(reanimated/animate-shared-value-with-delay translate-y 12 50 :linear 133.33)
(reanimated/animate-shared-value-with-delay connector-opacity 1 0 :easing1 93.33)
(reanimated/animate-shared-value-with-delay width 56 83.33 :easing1 80)
(reanimated/animate-shared-value-with-delay height 56 83.33 :easing1 80)
(reanimated/animate-shared-value-with-delay border-radius-first-half 28 83.33 :easing1 80)
(reanimated/animate-shared-value-with-delay border-radius-second-half 28 83.33 :easing1 80))
(reanimated/animate-shared-value-with-delay translate-y
12 50
:linear 133.33)
(reanimated/animate-shared-value-with-delay connector-opacity
1 0
:easing1 93.33)
(reanimated/animate-shared-value-with-delay width
56 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay height
56 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay
border-radius-first-half
28 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay
border-radius-second-half
28 83.33
:easing1 80))
reset-y-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y 0 100 :linear)
(reanimated/animate-shared-value-with-timing translate-y
0
100
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 12)
(reanimated/set-shared-value height 24)
(reanimated/set-shared-value border-radius-first-half 16)
(reanimated/set-shared-value border-radius-second-half 8))
fade-in-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y 0 200 :linear)
(reanimated/animate-shared-value-with-timing opacity 1 200 :linear))
(reanimated/animate-shared-value-with-timing translate-y
0
200
:linear)
(reanimated/animate-shared-value-with-timing opacity
1
200
:linear))
fade-out-animation (fn []
(reanimated/animate-shared-value-with-timing translate-y (if @reviewing-audio? 76 20) 200 :linear)
(when-not @reviewing-audio? (reanimated/animate-shared-value-with-timing opacity 0 200 :linear))
(reanimated/animate-shared-value-with-timing translate-y
(if @reviewing-audio?
76
20)
200
:linear)
(when-not @reviewing-audio?
(reanimated/animate-shared-value-with-timing opacity
0
200
:linear))
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))
fade-out-reset-animation (fn []
(reanimated/animate-shared-value-with-timing opacity 0 200 :linear)
(reanimated/animate-shared-value-with-delay translate-y 20 0 :linear 200)
(reanimated/animate-shared-value-with-timing opacity
0
200
:linear)
(reanimated/animate-shared-value-with-delay translate-y
20 0
:linear 200)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
@ -260,18 +381,26 @@
(effect! #(cond
@ready-to-send?
(start-y-animation)
@recording? (reset-y-animation))
@recording? (reset-y-animation))
[@ready-to-send?])
[:<>
[reanimated/view {:style (style/send-button-container opacity)}
[reanimated/view {:style (style/send-button-connector connector-opacity width height border-radius-first-half border-radius-second-half)}]]
[reanimated/view {:style (style/send-button translate-y opacity)
:pointer-events :none}
[icons/icon :i/arrow-up {:color colors/white
:size 20
:container-style style/send-icon-container}]]]))])
[reanimated/view
{:style (style/send-button-connector connector-opacity
width
height
border-radius-first-half
border-radius-second-half)}]]
[reanimated/view
{:style (style/send-button translate-y opacity)
:pointer-events :none}
[icons/icon :i/arrow-up
{:color colors/white
:size 20
:container-style style/send-icon-container}]]]))])
(defn- lock-button [recording? ready-to-lock? locked?]
(defn- lock-button
[recording? ready-to-lock? locked?]
[:f>
(fn []
(let [translate-x-y (reanimated/use-shared-value 20)
@ -282,25 +411,54 @@
border-radius-first-half (reanimated/use-shared-value 8)
border-radius-second-half (reanimated/use-shared-value 8)
start-x-y-animation (fn []
(reanimated/animate-shared-value-with-delay translate-x-y 8 50 :linear 116.66)
(reanimated/animate-shared-value-with-delay connector-opacity 1 0 :easing1 80)
(reanimated/animate-shared-value-with-delay width 56 83.33 :easing1 63.33)
(reanimated/animate-shared-value-with-delay height 56 83.33 :easing1 63.33)
(reanimated/animate-shared-value-with-delay border-radius-first-half 28 83.33 :easing1 63.33)
(reanimated/animate-shared-value-with-delay border-radius-second-half 28 83.33 :easing1 63.33))
(reanimated/animate-shared-value-with-delay translate-x-y
8 50
:linear 116.66)
(reanimated/animate-shared-value-with-delay connector-opacity
1 0
:easing1 80)
(reanimated/animate-shared-value-with-delay width
56 83.33
:easing1 63.33)
(reanimated/animate-shared-value-with-delay height
56 83.33
:easing1 63.33)
(reanimated/animate-shared-value-with-delay
border-radius-first-half
28 83.33
:easing1 63.33)
(reanimated/animate-shared-value-with-delay
border-radius-second-half
28 83.33
:easing1 63.33))
reset-x-y-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x-y 0 100 :linear)
(reanimated/animate-shared-value-with-timing translate-x-y
0
100
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))
fade-in-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x-y 0 220 :linear)
(reanimated/animate-shared-value-with-timing opacity 1 220 :linear))
(reanimated/animate-shared-value-with-timing translate-x-y
0
220
:linear)
(reanimated/animate-shared-value-with-timing opacity
1
220
:linear))
fade-out-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x-y 20 200 :linear)
(reanimated/animate-shared-value-with-timing opacity 0 200 :linear)
(reanimated/animate-shared-value-with-timing translate-x-y
20
200
:linear)
(reanimated/animate-shared-value-with-timing opacity
0
200
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
@ -322,14 +480,21 @@
[@locked?])
[:<>
[reanimated/view {:style (style/lock-button-container opacity)}
[reanimated/view {:style (style/lock-button-connector connector-opacity width height border-radius-first-half border-radius-second-half)}]]
[reanimated/view {:style (style/lock-button translate-x-y opacity)
:pointer-events :none}
[reanimated/view
{:style (style/lock-button-connector connector-opacity
width
height
border-radius-first-half
border-radius-second-half)}]]
[reanimated/view
{:style (style/lock-button translate-x-y opacity)
:pointer-events :none}
[icons/icon (if @ready-to-lock? :i/locked :i/unlocked)
{:color (colors/theme-colors colors/black colors/white)
:size 20}]]]))])
(defn- delete-button [recording? ready-to-delete?]
(defn- delete-button
[recording? ready-to-delete?]
[:f>
(fn []
(let [opacity (reanimated/use-shared-value 0)
@ -340,25 +505,54 @@
border-radius-first-half (reanimated/use-shared-value 8)
border-radius-second-half (reanimated/use-shared-value 8)
start-x-animation (fn []
(reanimated/animate-shared-value-with-delay translate-x 12 50 :linear 133.33)
(reanimated/animate-shared-value-with-delay connector-opacity 1 0 :easing1 93.33)
(reanimated/animate-shared-value-with-delay width 56 83.33 :easing1 80)
(reanimated/animate-shared-value-with-delay height 56 83.33 :easing1 80)
(reanimated/animate-shared-value-with-delay border-radius-first-half 28 83.33 :easing1 80)
(reanimated/animate-shared-value-with-delay border-radius-second-half 28 83.33 :easing1 80))
(reanimated/animate-shared-value-with-delay translate-x
12 50
:linear 133.33)
(reanimated/animate-shared-value-with-delay connector-opacity
1 0
:easing1 93.33)
(reanimated/animate-shared-value-with-delay width
56 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay height
56 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay
border-radius-first-half
28 83.33
:easing1 80)
(reanimated/animate-shared-value-with-delay
border-radius-second-half
28 83.33
:easing1 80))
reset-x-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x 0 100 :linear)
(reanimated/animate-shared-value-with-timing translate-x
0
100
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
(reanimated/set-shared-value border-radius-first-half 8)
(reanimated/set-shared-value border-radius-second-half 16))
fade-in-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x 0 200 :linear)
(reanimated/animate-shared-value-with-timing opacity 1 200 :linear))
(reanimated/animate-shared-value-with-timing translate-x
0
200
:linear)
(reanimated/animate-shared-value-with-timing opacity
1
200
:linear))
fade-out-animation (fn []
(reanimated/animate-shared-value-with-timing translate-x 20 200 :linear)
(reanimated/animate-shared-value-with-timing opacity 0 200 :linear)
(reanimated/animate-shared-value-with-timing translate-x
20
200
:linear)
(reanimated/animate-shared-value-with-timing opacity
0
200
:linear)
(reanimated/set-shared-value connector-opacity 0)
(reanimated/set-shared-value width 24)
(reanimated/set-shared-value height 12)
@ -376,13 +570,21 @@
[@ready-to-delete?])
[:<>
[reanimated/view {:style (style/delete-button-container opacity)}
[reanimated/view {:style (style/delete-button-connector connector-opacity width height border-radius-first-half border-radius-second-half)}]]
[reanimated/view {:style (style/delete-button translate-x opacity)
:pointer-events :none}
[icons/icon :i/delete {:color colors/white
:size 20}]]]))])
[reanimated/view
{:style (style/delete-button-connector connector-opacity
width
height
border-radius-first-half
border-radius-second-half)}]]
[reanimated/view
{:style (style/delete-button translate-x opacity)
:pointer-events :none}
[icons/icon :i/delete
{:color colors/white
:size 20}]]]))])
(defn- record-button [recording? reviewing-audio?]
(defn- record-button
[recording? reviewing-audio?]
[:f>
(fn []
(let [opacity (reanimated/use-shared-value 1)
@ -393,13 +595,15 @@
(show-animation))
[@recording? @reviewing-audio?])
[reanimated/view {:style (style/record-button-container opacity)}
[button/button {:type :outline
:size 32
:width 32
:accessibility-label :mic-button}
[button/button
{:type :outline
:size 32
:width 32
:accessibility-label :mic-button}
[icons/icon :i/audio {:color (colors/theme-colors colors/neutral-100 colors/white)}]]]))])
(defn input-view []
(defn input-view
[]
[:f>
(fn []
(let [recording? (reagent/atom false)
@ -414,126 +618,136 @@
touch-active? (atom false)
recording-timer (atom nil)
recording-length-ms (atom 0)
on-start-should-set-responder (fn [^js e]
(when-not @locked?
(let [pressed-record-button? (touch-inside-area?
{:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-button-area)]
(when-not @reviewing-audio?
(reset! recording? pressed-record-button?)
(when pressed-record-button?
;; TODO: By now we just track recording length, we need to add actual audio recording logic
;; GitHub issue [#14558]: https://github.com/status-im/status-mobile/issues/14558
(reset! recording-timer (utils/set-interval #(reset! recording-length-ms (+ @recording-length-ms 500)) 500))))
(reset! touch-active? true)))
true)
on-responder-move (fn [^js e]
(when-not @locked?
(let [location-x (oops/oget e "nativeEvent.locationX")
location-y (oops/oget e "nativeEvent.locationY")
page-x (oops/oget e "nativeEvent.pageX")
page-y (oops/oget e "nativeEvent.pageY")
moved-to-send-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? true
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? true}
(send-button-area @ready-to-send?))
moved-to-delete-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? true
:ignore-min-x? true
:ignore-max-x? false}
(delete-button-area @ready-to-delete?))
moved-to-lock-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
(lock-button-area @ready-to-lock?))
moved-to-record-button? (and
(touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-button-area-big)
(not= location-x page-x)
(not= location-y page-y))]
(cond
(and
(or
(and moved-to-record-button? @ready-to-lock?)
(and (not @locked?) moved-to-lock-button? @record-button-at-initial-position?))
(not @ready-to-delete?)
(not @ready-to-send?)
@recording?) (reset! ready-to-lock? moved-to-lock-button?)
(and
(or
(and moved-to-record-button? @ready-to-delete?)
(and moved-to-delete-button? @record-button-at-initial-position?))
(not @ready-to-lock?)
(not @ready-to-send?)
@recording?) (reset! ready-to-delete? moved-to-delete-button?)
(and
(or
(and moved-to-record-button? @ready-to-send?)
(and moved-to-send-button? @record-button-at-initial-position?))
(not @ready-to-lock?)
(not @ready-to-delete?)
@recording?) (reset! ready-to-send? moved-to-send-button?)))))
on-responder-release (fn [^js e]
(let [on-record-button? (touch-inside-area?
{:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
(if @reviewing-audio? record-button-area record-button-area-big))]
(cond
(and @reviewing-audio? on-record-button?)
(reset! reviewing-audio? false)
(and @ready-to-lock? (not @record-button-is-animating?))
(do
(reset! locked? true)
(reset! ready-to-lock? false))
(and (not @reviewing-audio?) on-record-button?)
(do
(when (>= @recording-length-ms 500) (reset! reviewing-audio? true))
(reset! locked? false)
(reset! recording? false)
(reset! ready-to-lock? false)
(utils/clear-interval @recording-timer)
(reset! recording-length-ms 0))
(and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?))
(do
(reset! recording? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(reset! ready-to-lock? false)
(utils/clear-interval @recording-timer)
(reset! recording-length-ms 0))))
(reset! touch-active? false))]
on-start-should-set-responder
(fn [^js e]
(when-not @locked?
(let [pressed-record-button? (touch-inside-area?
{:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-button-area)]
(when-not @reviewing-audio?
(reset! recording? pressed-record-button?)
(when pressed-record-button?
;; TODO: By now we just track recording length, we need to add actual audio
;; recording logic
;; GitHub issue [#14558]: https://github.com/status-im/status-mobile/issues/14558
(reset! recording-timer (utils/set-interval #(reset! recording-length-ms
(+ @recording-length-ms 500))
500))))
(reset! touch-active? true)))
true)
on-responder-move
(fn [^js e]
(when-not @locked?
(let [location-x (oops/oget e "nativeEvent.locationX")
location-y (oops/oget e "nativeEvent.locationY")
page-x (oops/oget e "nativeEvent.pageX")
page-y (oops/oget e "nativeEvent.pageY")
moved-to-send-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? true
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? true}
(send-button-area @ready-to-send?))
moved-to-delete-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? true
:ignore-min-x? true
:ignore-max-x? false}
(delete-button-area @ready-to-delete?))
moved-to-lock-button? (touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
(lock-button-area @ready-to-lock?))
moved-to-record-button? (and
(touch-inside-area?
{:location-x location-x
:location-y location-y
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
record-button-area-big)
(not= location-x page-x)
(not= location-y page-y))]
(cond
(and
(or
(and moved-to-record-button? @ready-to-lock?)
(and (not @locked?) moved-to-lock-button? @record-button-at-initial-position?))
(not @ready-to-delete?)
(not @ready-to-send?)
@recording?)
(reset! ready-to-lock? moved-to-lock-button?)
(and
(or
(and moved-to-record-button? @ready-to-delete?)
(and moved-to-delete-button? @record-button-at-initial-position?))
(not @ready-to-lock?)
(not @ready-to-send?)
@recording?)
(reset! ready-to-delete? moved-to-delete-button?)
(and
(or
(and moved-to-record-button? @ready-to-send?)
(and moved-to-send-button? @record-button-at-initial-position?))
(not @ready-to-lock?)
(not @ready-to-delete?)
@recording?)
(reset! ready-to-send? moved-to-send-button?)))))
on-responder-release
(fn [^js e]
(let [on-record-button? (touch-inside-area?
{:location-x (oops/oget e "nativeEvent.locationX")
:location-y (oops/oget e "nativeEvent.locationY")
:ignore-min-y? false
:ignore-max-y? false
:ignore-min-x? false
:ignore-max-x? false}
(if @reviewing-audio? record-button-area record-button-area-big))]
(cond
(and @reviewing-audio? on-record-button?)
(reset! reviewing-audio? false)
(and @ready-to-lock? (not @record-button-is-animating?))
(do
(reset! locked? true)
(reset! ready-to-lock? false))
(and (not @reviewing-audio?) on-record-button?)
(do
(when (>= @recording-length-ms 500) (reset! reviewing-audio? true))
(reset! locked? false)
(reset! recording? false)
(reset! ready-to-lock? false)
(utils/clear-interval @recording-timer)
(reset! recording-length-ms 0))
(and (not @locked?) (not @reviewing-audio?) (not @record-button-is-animating?))
(do
(reset! recording? false)
(reset! ready-to-send? false)
(reset! ready-to-delete? false)
(reset! ready-to-lock? false)
(utils/clear-interval @recording-timer)
(reset! recording-length-ms 0))))
(reset! touch-active? false))]
(fn []
[rn/view {:style style/input-container
:pointer-events :box-only
:on-start-should-set-responder on-start-should-set-responder
:on-responder-move on-responder-move
:on-responder-release on-responder-release}
[rn/view
{:style style/input-container
:pointer-events :box-only
:on-start-should-set-responder on-start-should-set-responder
:on-responder-move on-responder-move
:on-responder-release on-responder-release}
[delete-button recording? ready-to-delete?]
[lock-button recording? ready-to-lock? locked?]
[send-button recording? ready-to-send? reviewing-audio?]

View File

@ -7,30 +7,30 @@
([]
(render-toggle {}))
([opts]
(rtl/render (reagent/as-element [selectors/toggle opts]))))
(rtl/render (reagent/as-element [selectors/toggle opts]))))
(defn render-checkbox
([]
(render-checkbox {}))
([opts]
(rtl/render (reagent/as-element [selectors/checkbox opts]))))
(rtl/render (reagent/as-element [selectors/checkbox opts]))))
(defn render-checkbox-prefill
([]
(render-checkbox-prefill {}))
([opts]
(rtl/render (reagent/as-element [selectors/checkbox-prefill opts]))))
(rtl/render (reagent/as-element [selectors/checkbox-prefill opts]))))
(defn render-radio
([]
(render-radio {}))
([opts]
(rtl/render (reagent/as-element [selectors/radio opts]))))
(rtl/render (reagent/as-element [selectors/radio opts]))))
(js/global.test "default render of toggle component"
(fn []
(render-toggle)
(-> (js/expect (rtl/screen.getByTestId "toggle-component"))
(-> (js/expect (rtl/screen.getByTestId "toggle-component"))
(.toBeTruthy))))
(js/global.test "toggle component on change is working"
@ -44,7 +44,7 @@
(js/global.test "default render of radio component"
(fn []
(render-radio)
(-> (js/expect (rtl/screen.getByTestId "radio-component"))
(-> (js/expect (rtl/screen.getByTestId "radio-component"))
(.toBeTruthy))))
(js/global.test "radio component on change is working"
@ -58,7 +58,7 @@
(js/global.test "default render of checkbox component"
(fn []
(render-checkbox)
(-> (js/expect (rtl/screen.getByTestId "checkbox-component"))
(-> (js/expect (rtl/screen.getByTestId "checkbox-component"))
(.toBeTruthy))))
(js/global.test "checkbox component on change is working"
@ -72,7 +72,7 @@
(js/global.test "default render of checkbox-prefill component"
(fn []
(render-checkbox-prefill)
(-> (js/expect (rtl/screen.getByTestId "checkbox-prefill-component"))
(-> (js/expect (rtl/screen.getByTestId "checkbox-prefill-component"))
(.toBeTruthy))))
(js/global.test "checkbox-prefill component on change is working"

View File

@ -1,31 +1,32 @@
(ns quo2.components.selectors.disclaimer
(:require [react-native.core :as rn]
(:require [quo2.components.markdown.text :as text]
[quo2.components.selectors.selectors :as selectors]
[quo2.foundations.colors :as colors]
[quo2.components.markdown.text :as text]
[quo2.components.selectors.selectors :as selectors]))
[react-native.core :as rn]))
(defn disclaimer [{:keys [on-change accessibility-label container-style]} label]
(defn disclaimer
[{:keys [on-change accessibility-label container-style]} label]
[rn/view
{:style
(merge
container-style
{:flex 1
:flex-direction :row
{:flex 1
:flex-direction :row
:background-color (colors/theme-colors
colors/neutral-5
colors/neutral-80-opa-40)
:padding 11
:align-self :stretch
:border-radius 12
:border-width 1
:border-color (colors/theme-colors
colors/neutral-20
colors/neutral-70)})}
:padding 11
:align-self :stretch
:border-radius 12
:border-width 1
:border-color (colors/theme-colors
colors/neutral-20
colors/neutral-70)})}
[selectors/checkbox
{:on-change on-change}]
[text/text
{:accessibility-label accessibility-label
:size :paragraph-2
:style {:margin-left 8}}
:style {:margin-left 8}}
label]])

View File

@ -1,14 +1,19 @@
(ns quo2.components.selectors.selectors
(:require [react-native.core :as rn]
(:require [quo2.components.icon :as icons]
[quo2.foundations.colors :as colors]
[reagent.core :as reagent]
[quo2.components.icon :as icons]))
[react-native.core :as rn]
[reagent.core :as reagent]))
(defn- get-color [checked? disabled? blurred-background?]
(defn- get-color
[checked? disabled? blurred-background?]
(cond
checked?
(colors/custom-color-by-theme
:primary 50 60 (when disabled? 30) (when disabled? 30))
:primary
50
60
(when disabled? 30)
(when disabled? 30))
blurred-background?
(colors/theme-colors
(colors/alpha colors/neutral-80 (if disabled? 0.05 0.1))
@ -18,138 +23,152 @@
(colors/alpha colors/neutral-20 (if disabled? 0.4 1))
(colors/alpha colors/neutral-70 (if disabled? 0.3 1)))))
(defn- handle-press [disabled? on-change checked?]
(defn- handle-press
[disabled? on-change checked?]
(when (not disabled?)
(fn []
(swap! checked? not)
(when on-change (on-change @checked?)))))
(defn checkbox-prefill [{:keys [default-checked?]}]
(defn checkbox-prefill
[{:keys [default-checked?]}]
(let [checked? (reagent/atom (or default-checked? false))]
(fn [{:keys [on-change disabled? blurred-background? container-style]}]
[rn/touchable-without-feedback
{:on-press (handle-press disabled? on-change checked?)}
[rn/view
{:style (merge
container-style
{:height 21
:width 21
:border-radius 6
:background-color (if blurred-background?
(colors/theme-colors
(colors/alpha colors/neutral-80 (if disabled? 0.05 0.1))
(colors/alpha colors/white (if disabled? 0.05 0.1)))
(colors/theme-colors
(colors/alpha colors/neutral-20 (if disabled? 0.3 1))
(colors/alpha colors/neutral-70 (if disabled? 0.3 1))))})
{:style (merge
container-style
{:height 21
:width 21
:border-radius 6
:background-color (if blurred-background?
(colors/theme-colors
(colors/alpha colors/neutral-80
(if disabled? 0.05 0.1))
(colors/alpha colors/white (if disabled? 0.05 0.1)))
(colors/theme-colors
(colors/alpha colors/neutral-20
(if disabled? 0.3 1))
(colors/alpha colors/neutral-70
(if disabled? 0.3 1))))})
:accessibility-label (str "checkbox-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "checkbox-prefill-component"}
:testID "checkbox-prefill-component"}
(when @checked?
[rn/view {:style
{:height 20
:width 20}}
[rn/view
{:style
{:height 20
:width 20}}
[icons/icon :i/check-small
{:size 20
{:size 20
:color (colors/theme-colors
(colors/alpha colors/neutral-100 (if disabled? 0.3 1))
(colors/alpha colors/white (if disabled? 0.3 1)))}]])]])))
(defn checkbox [{:keys [default-checked?]}]
(defn checkbox
[{:keys [default-checked?]}]
(let [checked? (reagent/atom (or default-checked? false))]
(fn [{:keys [on-change disabled? blurred-background? container-style]}]
[rn/touchable-without-feedback
{:on-press (handle-press disabled? on-change checked?)}
[rn/view
{:style (merge
container-style
{:height 20
:width 20})}
{:style (merge
container-style
{:height 20
:width 20})}
[rn/view
{:style {:flex 1
:border-radius 6
:border-width (if @checked? 0 1)
:background-color (cond
@checked?
(get-color @checked? disabled? blurred-background?)
blurred-background?
(colors/theme-colors
colors/white-opa-5
colors/white-opa-10)
:else
(colors/theme-colors
colors/white
colors/neutral-80-opa-40))
:border-color (if @checked? :none
(get-color @checked? disabled? blurred-background?))}
{:style {:flex 1
:border-radius 6
:border-width (if @checked? 0 1)
:background-color (cond
@checked?
(get-color @checked? disabled? blurred-background?)
blurred-background?
(colors/theme-colors
colors/white-opa-5
colors/white-opa-10)
:else
(colors/theme-colors
colors/white
colors/neutral-80-opa-40))
:border-color (if @checked?
:none
(get-color @checked? disabled? blurred-background?))}
:accessibility-label (str "checkbox-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "checkbox-component"}
:testID "checkbox-component"}
(when @checked?
[rn/view {:style
{:height 20
:width 20}}
[rn/view
{:style
{:height 20
:width 20}}
[icons/icon :i/check-small
{:size 20
{:size 20
:color colors/white}]])]]])))
(defn radio [{:keys [default-checked?]}]
(defn radio
[{:keys [default-checked?]}]
(let [checked? (reagent/atom (or default-checked? false))]
(fn [{:keys [on-change disabled? blurred-background? container-style]}]
[rn/touchable-without-feedback
{:on-press (handle-press disabled? on-change checked?)}
[rn/view
{:style (merge
container-style
{:height 20
:width 20
:border-radius 20
:border-width 1
:border-color (get-color @checked? disabled? blurred-background?)
:background-color (when-not blurred-background?
(colors/theme-colors colors/white
(colors/alpha colors/neutral-80 0.4)))})
{:style (merge
container-style
{:height 20
:width 20
:border-radius 20
:border-width 1
:border-color (get-color @checked? disabled? blurred-background?)
:background-color (when-not blurred-background?
(colors/theme-colors colors/white
(colors/alpha colors/neutral-80
0.4)))})
:accessibility-label (str "radio-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "radio-component"}
:testID "radio-component"}
[rn/view {:style
{:margin-left :auto
:height 14
:width 14
:background-color (when @checked? (get-color @checked? disabled? blurred-background?))
:border-radius 20
:margin-right :auto
:margin-top :auto
:margin-bottom :auto}}]]])))
[rn/view
{:style
{:margin-left :auto
:height 14
:width 14
:background-color (when @checked? (get-color @checked? disabled? blurred-background?))
:border-radius 20
:margin-right :auto
:margin-top :auto
:margin-bottom :auto}}]]])))
(defn toggle [{:keys [default-checked?]}]
(defn toggle
[{:keys [default-checked?]}]
(let [checked? (reagent/atom (or default-checked? false))]
(fn [{:keys [on-change disabled? blurred-background? container-style]}]
[rn/touchable-without-feedback
{:on-press (handle-press disabled? on-change checked?)}
[rn/view
{:style (merge
container-style
{:height 20
:width 30
:border-radius 20
:background-color (get-color @checked? disabled? blurred-background?)})
{:style (merge
container-style
{:height 20
:width 30
:border-radius 20
:background-color (get-color @checked? disabled? blurred-background?)})
:accessibility-label (str "toggle-" (if @checked? "on" "off"))
:accessibility-role :checkbox
:testID "toggle-component"}
[rn/view {:style
{:margin-left (if @checked? 12 2)
:height 16
:width 16
:background-color (if blurred-background?
(colors/theme-colors
(colors/alpha colors/white (if disabled? 0.4 1))
(colors/alpha colors/white (if disabled? 0.3 1)))
(colors/theme-colors
(colors/alpha colors/white 1)
(colors/alpha colors/white (if disabled? 0.4 1))))
:border-radius 20
:margin-right :auto
:margin-top :auto
:margin-bottom :auto}}]]])))
:testID "toggle-component"}
[rn/view
{:style
{:margin-left (if @checked? 12 2)
:height 16
:width 16
:background-color (if blurred-background?
(colors/theme-colors
(colors/alpha colors/white (if disabled? 0.4 1))
(colors/alpha colors/white (if disabled? 0.3 1)))
(colors/theme-colors
(colors/alpha colors/white 1)
(colors/alpha colors/white (if disabled? 0.4 1))))
:border-radius 20
:margin-right :auto
:margin-top :auto
:margin-bottom :auto}}]]])))

View File

@ -1,8 +1,9 @@
(ns quo2.components.separator
(:require [react-native.core :as rn]
[quo2.foundations.colors :as quo2.colors]))
(:require [quo2.foundations.colors :as quo2.colors]
[react-native.core :as rn]))
(defn separator [{:keys [style]}]
(defn separator
[{:keys [style]}]
[rn/view
{:style
(merge

View File

@ -1,10 +1,10 @@
(ns quo2.components.settings.privacy-option
(:require [react-native.core :as rn]
[quo2.components.icon :as icons]
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.selectors.selectors :as selectors]
[quo2.components.settings.style :as style]
[quo2.components.markdown.text :as text]
[quo2.foundations.colors :as colors]))
[quo2.foundations.colors :as colors]
[react-native.core :as rn]))
(defn- bullet
[]
@ -27,8 +27,9 @@
[rn/view {:style style/card-footer-label-container}
[text/text {:size :paragraph-2} label]]
[rn/view {:style style/card-footer-toggle-container}
[selectors/toggle {:disabled? (not active?)
:on-change on-toggle}]]]])
[selectors/toggle
{:disabled? (not active?)
:on-change on-toggle}]]]])
(defn- selection-indicator
[active?]
@ -38,8 +39,9 @@
(defn- card-header
[{:keys [icon label active?]}]
[rn/view {:style style/card-header-container}
[icons/icon icon {:size 20
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]
[icons/icon icon
{:size 20
:color (colors/theme-colors colors/neutral-50 colors/neutral-40)}]
[rn/view {:style style/card-header-label-container}
[text/text {:weight :semi-bold} label]]
[selection-indicator active?]])
@ -53,11 +55,13 @@
:accessibility-label :privacy-option-card
:testID :privacy-option-card}
[rn/view (style/privacy-option-card active?)
[card-header {:active? active?
:icon icon
:label header}]
[card-header
{:active? active?
:icon icon
:label header}]
[unordered-list (when-not footer {:margin-bottom 8}) list-items]
(when footer
[card-footer {:active? active?
:label footer
:on-toggle on-toggle}])]])
[card-footer
{:active? active?
:label footer
:on-toggle on-toggle}])]])

View File

@ -1,46 +1,47 @@
(ns quo2.components.tabs.account-selector
(:require
[quo2.theme :as theme]
[react-native.core :as rn]
[quo2.foundations.colors :as colors]
[quo2.components.markdown.text :as quo2]))
(:require [quo2.components.markdown.text :as quo2]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(def themes
{:light {:default {:bg colors/neutral-10
:account-text colors/neutral-100
:label-text colors/neutral-50}
:transparent {:bg colors/neutral-80-opa-5
:account-text colors/neutral-100
:label-text colors/neutral-80-opa-40}}
{:light {:default {:bg colors/neutral-10
:account-text colors/neutral-100
:label-text colors/neutral-50}
:transparent {:bg colors/neutral-80-opa-5
:account-text colors/neutral-100
:label-text colors/neutral-80-opa-40}}
:dark {:default {:bg colors/neutral-80-opa-80
:account-text colors/white
:label-text colors/neutral-40}
:transparent {:bg colors/white-opa-5
:account-text colors/white
:label-text colors/neutral-40}}})
:dark {:default {:bg colors/neutral-80-opa-80
:account-text colors/white
:label-text colors/neutral-40}
:transparent {:bg colors/white-opa-5
:account-text colors/white
:label-text colors/neutral-40}}})
(defn account-container-row [background-color]
{:padding-vertical 4
:flex-direction :row
:align-items :center
:background-color background-color
:border-radius 12})
(defn account-container-row
[background-color]
{:padding-vertical 4
:flex-direction :row
:align-items :center
:background-color background-color
:border-radius 12})
(def account-emoji
{:height 16
:width 16})
{:height 16
:width 16})
(def account-emoji-container
{:background-color (colors/custom-color :purple 50)
:padding 8
:justify-content :center
:align-items :center
:border-radius 10
:margin-left 4
:margin-right 8})
{:background-color (colors/custom-color :purple 50)
:padding 8
:justify-content :center
:align-items :center
:border-radius 10
:margin-left 4
:margin-right 8})
(defn get-color-by-type [type key]
(defn get-color-by-type
[type key]
(get-in themes [(theme/get-theme) type key]))
(defn account-selector
@ -57,16 +58,19 @@
account-text-color (get-color-by-type (if transparent? :transparent :default) :account-text)
label-text-color (get-color-by-type (if transparent? :transparent :default) :label-text)]
[rn/view {:style style}
(when show-label? [quo2/text {:weight :medium
:size :paragraph-2
:style {:color label-text-color
:margin-bottom 8}}
label-text])
(when show-label?
[quo2/text
{:weight :medium
:size :paragraph-2
:style {:color label-text-color
:margin-bottom 8}}
label-text])
[rn/view {:style (account-container-row background-color)}
[rn/view {:style account-emoji-container}
[quo2/text account-emoji]]
[quo2/text {:weight :medium
:size :paragraph-1
:style {:color account-text-color}}
[quo2/text
{:weight :medium
:size :paragraph-1
:style {:color account-text-color}}
account-text]]]))

View File

@ -1,29 +1,33 @@
(ns quo2.components.tabs.segmented-tab
(:require [reagent.core :as reagent]
[react-native.core :as rn]
[quo2.components.tabs.tab :as tab]
(:require [quo2.components.tabs.tab :as tab]
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]))
[quo2.theme :as theme]
[react-native.core :as rn]
[reagent.core :as reagent]))
(def themes {:light {:background-color colors/neutral-20}
:dark {:background-color colors/neutral-80}})
(def themes
{:light {:background-color colors/neutral-20}
:dark {:background-color colors/neutral-80}})
(defn segmented-control [{:keys [default-active on-change]}]
(defn segmented-control
[{:keys [default-active on-change]}]
(let [active-tab-id (reagent/atom default-active)]
(fn [{:keys [data size]}]
(let [active-id @active-tab-id]
[rn/view {:flex-direction :row
:background-color (get-in themes [(theme/get-theme) :background-color])
:border-radius (case size
32 10
28 8
24 8
20 6)
:padding 2}
[rn/view
{:flex-direction :row
:background-color (get-in themes [(theme/get-theme) :background-color])
:border-radius (case size
32 10
28 8
24 8
20 6)
:padding 2}
(for [[indx {:keys [label id]}] (map-indexed vector data)]
^{:key id}
[rn/view {:margin-left (if (= 0 indx) 0 2)
:flex 1}
[rn/view
{:margin-left (if (= 0 indx) 0 2)
:flex 1}
[tab/tab
{:id id
:segmented true

View File

@ -1,49 +1,52 @@
(ns quo2.components.tabs.tab
(:require [quo2.foundations.colors :as colors]
[react-native.core :as rn]
[quo2.theme :as theme]
(:require [quo2.components.icon :as icons]
[quo2.components.markdown.text :as text]
[quo2.components.icon :as icons]))
[quo2.foundations.colors :as colors]
[quo2.theme :as theme]
[react-native.core :as rn]))
(def themes {:light {:default {:background-color colors/neutral-20
:icon-color colors/neutral-50
:label {:style {:color colors/neutral-100}}}
:active {:background-color colors/neutral-50
:icon-color colors/white
:label {:style {:color colors/white}}}
:disabled {:background-color colors/neutral-20
:icon-color colors/neutral-50
:label {:style {:color colors/neutral-100}}}}
:dark {:default {:background-color colors/neutral-80
:icon-color colors/neutral-40
:label {:style {:color colors/white}}}
:active {:background-color colors/neutral-60
:icon-color colors/white
:label {:style {:color colors/white}}}
:disabled {:background-color colors/neutral-80
:icon-color colors/neutral-40
:label {:style {:color colors/white}}}}})
(def themes
{:light {:default {:background-color colors/neutral-20
:icon-color colors/neutral-50
:label {:style {:color colors/neutral-100}}}
:active {:background-color colors/neutral-50
:icon-color colors/white
:label {:style {:color colors/white}}}
:disabled {:background-color colors/neutral-20
:icon-color colors/neutral-50
:label {:style {:color colors/neutral-100}}}}
:dark {:default {:background-color colors/neutral-80
:icon-color colors/neutral-40
:label {:style {:color colors/white}}}
:active {:background-color colors/neutral-60
:icon-color colors/white
:label {:style {:color colors/white}}}
:disabled {:background-color colors/neutral-80
:icon-color colors/neutral-40
:label {:style {:color colors/white}}}}})
(def themes-for-blur-background {:light {:default {:background-color colors/neutral-80-opa-5
:icon-color colors/neutral-80-opa-40
:label {:style {:color colors/neutral-100}}}
:active {:background-color colors/neutral-80-opa-60
:icon-color colors/white
:label {:style {:color colors/white}}}
:disabled {:background-color colors/neutral-80-opa-5
:icon-color colors/neutral-80-opa-40
:label {:style {:color colors/neutral-100}}}}
:dark {:default {:background-color colors/white-opa-5
:icon-color colors/white
:label {:style {:color colors/white}}}
:active {:background-color colors/white-opa-20
:icon-color colors/white
:label {:style {:color colors/white}}}
:disabled {:background-color colors/white-opa-5
:icon-color colors/neutral-40
:label {:style {:color colors/white}}}}})
(def themes-for-blur-background
{:light {:default {:background-color colors/neutral-80-opa-5
:icon-color colors/neutral-80-opa-40
:label {:style {:color colors/neutral-100}}}
:active {:background-color colors/neutral-80-opa-60
:icon-color colors/white
:label {:style {:color colors/white}}}
:disabled {:background-color colors/neutral-80-opa-5
:icon-color colors/neutral-80-opa-40
:label {:style {:color colors/neutral-100}}}}
:dark {:default {:background-color colors/white-opa-5
:icon-color colors/white
:label {:style {:color colors/white}}}
:active {:background-color colors/white-opa-20
:icon-color colors/white
:label {:style {:color colors/white}}}
:disabled {:background-color colors/white-opa-5
:icon-color colors/neutral-40
:label {:style {:color colors/white}}}}})
(defn style-container [size disabled background-color]
(defn style-container
[size disabled background-color]
(merge {:height size
:align-items :center
:justify-content :center
@ -54,7 +57,11 @@
24 8
20 6)
:background-color background-color
:padding-horizontal (case size 32 12 28 12 24 8 20 8)}
:padding-horizontal (case size
32 12
28 12
24 8
20 8)}
(when disabled
{:opacity 0.3})))
@ -70,14 +77,18 @@
(fn [{:keys [id on-press disabled size before active accessibility-label blur? override-theme]
:or {size 32}}
children]
(let [state (cond disabled :disabled active :active :else :default)
(let [state (cond disabled :disabled
active :active
:else :default)
{:keys [icon-color background-color label]}
(get-in (if blur? themes-for-blur-background themes) [(or override-theme (theme/get-theme)) state])]
[rn/touchable-without-feedback (merge {:disabled disabled
:accessibility-label accessibility-label}
(when on-press
{:on-press (fn []
(on-press id))}))
(get-in (if blur? themes-for-blur-background themes)
[(or override-theme (theme/get-theme)) state])]
[rn/touchable-without-feedback
(merge {:disabled disabled
:accessibility-label accessibility-label}
(when on-press
{:on-press (fn []
(on-press id))}))
[rn/view {:style (style-container size disabled background-color)}
(when before
[rn/view
@ -85,10 +96,14 @@
[rn/view
(cond
(string? children)
[text/text (merge {:size (case size 24 :paragraph-2 20 :label nil)
:weight :medium
:number-of-lines 1}
label)
[text/text
(merge {:size (case size
24 :paragraph-2
20 :label
nil)
:weight :medium
:number-of-lines 1}
label)
children]
(vector? children)
children)]]])))

Some files were not shown because too many files have changed in this diff Show More