[#13964 #13962] Display contact requests (#14014)

This commit is contained in:
Icaro Motta 2022-09-19 11:08:21 -03:00 committed by GitHub
parent 528d05eec1
commit afbdb4c0b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 321 additions and 109 deletions

View File

@ -285,7 +285,7 @@ endif
lint: export TARGET := clojure
lint: ##@test Run code style checks
yarn clj-kondo --confg .clj-kondo/config.edn --lint src && \
yarn clj-kondo --config .clj-kondo/config.edn --cache false --lint src && \
TARGETS=$$(git diff --diff-filter=d --cached --name-only src && echo src) && \
clojure -Scp "$$CLASS_PATH" -m cljfmt.main check --indents indentation.edn $$TARGETS

View File

@ -83,7 +83,7 @@
"@babel/preset-env": "7.1.0",
"@babel/register": "7.0.0",
"@mapbox/node-pre-gyp": "^1.0.9",
"clj-kondo": "^2020.1.13",
"clj-kondo": "^2022.9.8",
"jest": "^25.1.0",
"nodemon": "^2.0.16",
"nyc": "^14.1.1",

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 622 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 613 B

View File

@ -1,10 +1,10 @@
(ns quo2.components.tags.status-tags
(:require [status-im.i18n.i18n :as i18n]
[quo2.foundations.colors :as colors]
(:require [quo.react-native :as rn]
[quo.theme :as quo.theme]
[quo2.components.icon :as icon]
[quo2.components.markdown.text :as text]
[quo.react-native :as rn]))
[quo2.foundations.colors :as colors]
[status-im.i18n.i18n :as i18n]))
(def default-container-style
{:border-radius 20
@ -50,42 +50,48 @@
:style {:padding-left 5
:color text-color}} label]]])))
(defn positive [size theme label]
[base-tag {:size size
(defn- positive
[size theme label]
[base-tag {:size size
:background-color colors/success-50-opa-10
:icon :verified
:border-color colors/success-50-opa-20
:text-color (if (= theme :light) colors/success-50
colors/success-60)
:label (or label (i18n/label :positive))}])
:icon :verified
:border-color colors/success-50-opa-20
:label (or label (i18n/label :positive))
:text-color (if (= theme :light) colors/success-50
colors/success-60)}])
(defn negative [size theme label]
[base-tag {:size size
:icon :untrustworthy
(defn- negative
[size theme label]
[base-tag {:size size
:icon :untrustworthy
:background-color colors/danger-50-opa-10
:border-color colors/danger-50-opa-20
:text-color (if (= theme :light)
colors/danger-50
colors/danger-60)
:label (or label (i18n/label :negative))}])
:border-color colors/danger-50-opa-20
:label (or label (i18n/label :negative))
:text-color (if (= theme :light)
colors/danger-50
colors/danger-60)}])
(defn pending [size theme label]
[base-tag {:size size
:icon :pending
(defn- pending
[size theme label]
[base-tag {:size size
:icon :pending
:label (or label (i18n/label :pending))
:background-color (if (= theme :light)
colors/neutral-10
colors/neutral-80)
:border-color (if (= theme :light)
colors/neutral-20
colors/neutral-70)
:text-color colors/neutral-50
:label (or label (i18n/label :pending))}])
:border-color (if (= theme :light)
colors/neutral-20
colors/neutral-70)
:text-color colors/neutral-50}])
(defn status-tag [_]
(fn [{:keys [status size override-theme label]}]
(let [theme (or override-theme (quo.theme/get-theme))]
[(case status
:positive positive
:negative negative
:pending pending
nil) size theme label])))
(defn status-tag [{:keys [status size override-theme label]}]
(when status
(when-let [status-component (case (:type status)
:positive positive
:negative negative
:pending pending
nil)]
[status-component
size
(or override-theme (quo.theme/get-theme))
label])))

View File

@ -6,15 +6,15 @@
[quo2.components.tags.status-tags :as quo2]))
(def status-tags-options
{:label "Status"
:key :status
:type :select
{:label "Status"
:key :status
:type :select
:options [{:value "Positive"
:key :positive}
:key :positive}
{:value "Negative"
:key :negative}
:key :negative}
{:value "Pending"
:key :pending}]})
:key :pending}]})
(def descriptor [status-tags-options
{:label "Size"
@ -27,16 +27,20 @@
(defn cool-preview []
(let [state (reagent/atom {:status :positive
:size :small})]
:size :small})]
(fn []
[rn/view {:margin-bottom 50
:padding 16}
[rn/view {:flex 1}
[preview/customizer state descriptor]]
[rn/view {:padding-vertical 60
:flex-direction :row
:justify-content :center}
[quo2/status-tag @state]]])))
(let [props (cond-> @state
(= :positive (:status @state)) (assoc :status {:label "Positive" :type :positive})
(= :negative (:status @state)) (assoc :status {:label "Negative" :type :negative})
(= :pending (:status @state)) (assoc :status {:label "Pending" :type :pending}))]
[rn/view {:margin-bottom 50
:padding 16}
[rn/view {:flex 1}
[preview/customizer state descriptor]]
[rn/view {:padding-vertical 60
:flex-direction :row
:justify-content :center}
[quo2/status-tag props]]]))))
(defn preview-status-tags []
[rn/view {:background-color (colors/theme-colors colors/white

View File

@ -5,26 +5,88 @@
[quo2.components.tags.context-tags :as context-tags]
[quo2.foundations.colors :as colors]
[reagent.core :as reagent]
[status-im.constants :as constants]
[status-im.i18n.i18n :as i18n]
[status-im.multiaccounts.core :as multiaccounts]
[status-im.ui.components.topbar :as topbar]
[status-im.utils.datetime :as datetime]
[status-im.utils.handlers :refer [<sub >evt]]))
(defn activity-title
[{:keys [type]}]
(case type
constants/activity-center-notification-type-contact-request
(i18n/label :t/contact-request)
constants/activity-center-notification-type-one-to-one-chat
"Dummy 1:1 chat title"
"Dummy default title"))
(defn activity-icon
[{:keys [type]}]
(case type
constants/activity-center-notification-type-contact-request
:add-user
:placeholder))
(defn activity-context
[{:keys [message last-message type]}]
(case type
constants/activity-center-notification-type-contact-request
(let [message (or message last-message)
contact (<sub [:contacts/contact-by-identity (:from message)])
sender-name (or (get-in contact [:names :nickname])
(get-in contact [:names :three-words-name]))]
[[context-tags/user-avatar-tag
{:color :purple
:override-theme :dark
:size :small
:style {:background-color colors/white-opa-10}
:text-style {:color colors/white}}
sender-name
(multiaccounts/displayed-photo contact)]
[rn/text {:style {:color colors/white}}
(i18n/label :t/contact-request-sent)]])
nil))
(defn activity-message
[{:keys [message last-message]}]
{:body (get-in (or message last-message) [:content :text])})
(defn activity-status
[notification]
(case (get-in notification [:message :contact-request-state])
constants/contact-request-message-state-accepted
{:type :positive :label (i18n/label :t/accepted)}
constants/contact-request-message-state-declined
{:type :negative :label (i18n/label :t/declined)}
nil))
(defn activity-buttons
[{:keys [type]}]
(case type
constants/activity-center-notification-type-contact-request
{:button-1 {:label (i18n/label :t/decline)
:type :danger}
:button-2 {:label (i18n/label :t/accept)
:type :success}}
nil))
(defn render-notification
[notification index]
[rn/view {:flex 1
:flex-direction :column
:margin-top (if (= 0 index) 0 4)}
[activity-logs/activity-log {:context [[context-tags/group-avatar-tag "Name" {:color :purple
:override-theme :dark
:size :small
:style {:background-color colors/white-opa-10}
:text-style {:color colors/white}}]
[rn/text {:style {:color colors/white}} "did something here."]]
:icon :placeholder
:message {:body (get-in notification [:message :content :text])}
:timestamp (:timestamp notification)
:title "Activity Title"
:unread? (not (:read notification))}]])
[activity-logs/activity-log
(merge {:context (activity-context notification)
:icon (activity-icon notification)
:message (activity-message notification)
:status (activity-status notification)
:timestamp (datetime/timestamp->relative (:timestamp notification))
:title (activity-title notification)
:unread? (not (:read notification))}
(activity-buttons notification))]])
(defn notifications-list
[]

View File

@ -1,7 +1,7 @@
(ns status-im.utils.datetime
(:require [re-frame.core :as re-frame]
[cljs-time.core :as t :refer [plus minus days hours before?]]
[cljs-time.coerce :refer [from-long from-date]]
[cljs-time.coerce :refer [from-long]]
[cljs-time.format :refer [formatters
formatter
unparse]]
@ -10,6 +10,8 @@
[clojure.string :as s]
[status-im.goog.i18n :as goog.18n]))
;;;; Datetime constants
(defn now []
(t/now))
@ -29,28 +31,54 @@
(def time-zone-offset (hours (- (/ (.getTimezoneOffset ^js (js/Date.)) 60))))
;; detects if given locale symbols timeformat generates AM/PM ("a")
(defn- is24Hour-locsym [^js locsym]
;;;; Utilities
(defn- is24Hour-locsym
"Detects if given locale symbols timeformat generates AM/PM ('a')."
[^js locsym]
(not (s/includes?
(nth (.-TIMEFORMATS locsym) 2)
"a")))
;; returns is24Hour from device or from given locale symbols
;; whenever we get non-nil value use it, else calculate it from the given locale symbol
(defn- is24Hour [locsym]
(defn- is24Hour
"Returns is24Hour from device or from given locale symbols. Whenever we get
non-nil value use it, else calculate it from the given locale symbol."
[^js locsym]
(if-some [fromdev (status/is24Hour)]
fromdev
(is24Hour-locsym locsym)))
;; time formats
(defn- short-time-format [locsym] (if (is24Hour locsym) "HH:mm" "h:mm a"))
(defn- time-format [locsym] (if (is24Hour locsym) "HH:mm:ss" "h:mm:ss a"))
;;;; Time formats
(defn- short-time-format
[^js locsym]
(if (is24Hour locsym)
"HH:mm"
"h:mm a"))
(defn- time-format
[^js locsym]
(if (is24Hour locsym)
"HH:mm:ss"
"h:mm:ss a"))
;;;; Date formats
;; date formats
(defn- short-date-format [_] "dd MMM")
(defn- medium-date-format [^js locsym] (nth (.-DATEFORMATS locsym) 2)) ; get medium format from current locale symbols
;; date-time formats
(defn- datetime-within-one-week-format
[^js locsym]
(if (is24Hour locsym)
"E HH:mm"
"E h:mm a"))
(defn- medium-date-format
"Get medium format from current locale symbols."
[^js locsym]
(nth (.-DATEFORMATS locsym) 2))
;;;; Datetime formats
(defn- medium-date-time-format [locsym]
(str (medium-date-format locsym) ", " (time-format locsym)))
@ -61,19 +89,38 @@
(reset! formatter
(goog.18n/mk-fmt status-im.i18n.i18n/locale format))))))
;; generate formatters for different formats
(def date-time-fmt
(get-formatter-fn medium-date-time-format))
(def date-fmt
(get-formatter-fn medium-date-format))
(def time-fmt
(get-formatter-fn short-time-format))
(def short-date-fmt
(get-formatter-fn short-date-format))
(def date-time-fmt (get-formatter-fn medium-date-time-format))
(def date-fmt (get-formatter-fn medium-date-format))
(def time-fmt (get-formatter-fn short-time-format))
(def short-date-fmt (get-formatter-fn short-date-format))
(def datetime-within-one-week-fmt (get-formatter-fn datetime-within-one-week-format))
;;
;; functions which apply formats for the given timestamp
;;
;;;; Utilities
(defn previous-years?
[datetime]
(< (t/year datetime) (t/year (t/now))))
(defn current-year?
[datetime]
(= (t/year datetime) (t/year (t/now))))
(defn today?
[datetime]
(let [now (t/now)]
(and (= (t/year now) (t/year datetime))
(= (t/month now) (t/month datetime))
(= (t/day now) (t/day datetime)))))
(defn within-last-n-days?
"Returns true if `datetime` is within last `n` days (inclusive on both ends)."
[datetime n]
(let [now (t/now)
start (t/at-midnight (t/minus now (t/days n)))
end (t/plus now (t/millis 1))]
(t/within? start end datetime)))
;;;; Timestamp formatters
(defn- to-str [ms old-fmt-fn yesterday-fmt-fn today-fmt-fn]
(let [date (from-long ms)
@ -97,6 +144,26 @@
#(label :t/datetime-yesterday)
#(label :t/datetime-today)))
(defn timestamp->relative [ms]
(let [datetime (from-long ms)]
(cond
(today? datetime)
(.format ^js (time-fmt) datetime)
(within-last-n-days? datetime 1)
(str (s/capitalize (label :t/datetime-yesterday))
" "
(.format ^js (time-fmt) datetime))
(within-last-n-days? datetime 6)
(.format ^js (datetime-within-one-week-fmt) datetime)
(current-year? datetime)
(.format ^js (short-date-fmt) datetime)
(previous-years? datetime)
(.format ^js (date-fmt) datetime))))
(defn timestamp->mini-date [ms]
(.format ^js (short-date-fmt) (-> ms
from-long
@ -171,21 +238,5 @@
(fn [coeffects _]
(assoc coeffects :now (timestamp))))
(defn format-date [format date]
(let [local (plus (from-date date) time-zone-offset)]
(unparse (formatter format) local)))
(defn get-ordinal-date [date]
(let [local (plus (from-date date) time-zone-offset)
day (js/parseInt (unparse (formatter "d") local))
s {0 "th"
1 "st"
2 "nd"
3 "rd"}
m (mod day 100)]
(str day (or (s (mod (- m 20) 10))
(s m)
(s 0)))))
(defn to-ms [sec]
(* 1000 sec))

View File

@ -1,8 +1,9 @@
(ns status-im.utils.datetime-test
(:require [cljs.test :refer-macros [deftest is]]
[status-im.utils.datetime :as d]
(:require [cljs-time.coerce :as time-coerce]
[cljs-time.core :as t]
[cljs.test :refer-macros [deftest testing is]]
[status-im.goog.i18n :as i18n]
[cljs-time.core :as t]))
[status-im.utils.datetime :as d]))
(defn match [name symbols]
(is (identical? (.-dateTimeSymbols_ (i18n/mk-fmt name #'status-im.utils.datetime/medium-date-format))
@ -75,6 +76,93 @@
d/date-fmt (fn [] (i18n/mk-fmt "nb-NO" #'status-im.utils.datetime/medium-date-time-format))]
(is (= (d/day-relative epoch) "1. jan. 1970, 00:00:00"))))
(deftest current-year?-test
;; Today is Monday, 1975-03-10 15:15:45Z
(with-redefs [t/*ms-fn* (constantly 163696545000)
d/time-zone-offset (t/period :hours 0)]
(is (d/current-year? (t/now)))
(testing "returns false for future years"
(is (not (d/current-year? (t/plus (t/now) (t/years 1))))))
(testing "returns true at 1975-01-01 00:00:00"
(is (d/current-year? (time-coerce/from-long 157766400000))))
(testing "returns false at 1974-12-31 23:59:59"
(is (not (d/current-year? (time-coerce/from-long 157766399000)))))))
(deftest previous-years?-test
;; Today is Monday, 1975-03-10 15:15:45Z
(with-redefs [t/*ms-fn* (constantly 163696545000)
d/time-zone-offset (t/period :hours 0)]
(is (not (d/previous-years? (t/now))))
(testing "returns false for future years"
(is (not (d/current-year? (t/plus (t/now) (t/years 1))))))
(testing "returns false at 1975-01-01 00:00:00"
(is (not (d/previous-years? (time-coerce/from-long 1640995200000)))))
(testing "returns true at 1974-12-31 23:59:59"
(is (not (d/previous-years? (time-coerce/from-long 1640995199000)))))))
(deftest within-last-n-days?-test
;; Today is Monday, 1975-03-10 15:15:45Z
(let [now 163696545000]
(with-redefs [t/*ms-fn* (constantly now)
d/time-zone-offset (t/period :hours 0)]
(testing "start of the period, 6 days ago (inclusive)"
;; Tuesday, 1975-03-03 23:59:59Z
(is (not (d/within-last-n-days? (time-coerce/from-long 163123199000) 6)))
;; Tuesday, 1975-03-04 00:00:00Z
(is (d/within-last-n-days? (time-coerce/from-long 163123200000) 6))
;; Tuesday, 1975-03-04 00:00:01Z
(is (d/within-last-n-days? (time-coerce/from-long 163123201000) 6)))
(testing "end of the period (inclusive)"
;; Monday, 1975-03-10 15:15:44Z
(is (d/within-last-n-days? (time-coerce/from-long 163696544000) 6))
;; Monday, 1975-03-10 15:15:45Z
(is (d/within-last-n-days? (time-coerce/from-long now) 6))
;; Monday, 1975-03-10 15:15:46Z
(is (not (d/within-last-n-days? (time-coerce/from-long 163696546000) 6)))))))
(deftest timestamp->relative-test
;; Today is Monday, 1975-03-10 15:15:45Z
(with-redefs [t/*ms-fn* (constantly 163696545000)
d/time-zone-offset (t/period :hours 0)
d/is24Hour (constantly false)]
(testing "formats previous years"
;; 1974-12-31 23:59:59Z
(is (= "Dec 31, 1974" (d/timestamp->relative 157766399000)))
;; 1973-01-01 00:00:00Z
(is (= "Jan 1, 1973" (d/timestamp->relative 94694400000))))
(testing "formats 7 days ago or older, but in the current year"
(is (= "03 Mar" (d/timestamp->relative 163091745000)))
(is (= "02 Mar" (d/timestamp->relative 163004400000)))
(is (= "01 Jan" (d/timestamp->relative 157820400000))))
(testing "formats dates within the last 6 days"
(is (= "Sat 3:15 PM" (d/timestamp->relative 163523745000)))
(is (= "Fri 3:15 PM" (d/timestamp->relative 163437345000)))
(is (= "Thu 3:15 PM" (d/timestamp->relative 163350945000)))
(is (= "Wed 3:15 PM" (d/timestamp->relative 163264545000)))
(is (= "Tue 3:15 PM" (d/timestamp->relative 163178145000))))
(testing "formats within yesterday window"
(is (= "Yesterday 3:15 PM" (d/timestamp->relative 163610145000)))
(is (= "Yesterday 11:59 PM" (d/timestamp->relative 163641599000))))
(testing "formats today, at various timestamps"
(is (= "3:15 PM" (d/timestamp->relative 163696545000)))
(is (= "12:00 PM" (d/timestamp->relative 163684800000)))
(is (= "12:00 AM" (d/timestamp->relative 163641600000))))))
#_((deftest day-relative-before-yesterday-force-24H-test
(with-redefs [t/*ms-fn* (constantly epoch-plus-3d)
d/is24Hour (constantly true)

View File

@ -1766,6 +1766,7 @@
"opened" : "Opened",
"accepted": "Accepted",
"declined": "Declined",
"contact-request-sent": "sent contact request",
"contact-request-header": "👋 Contact requests",
"contact-request-declined": "Declined ⓧ",
"contact-request-accepted": "Accepted ✓",

View File

@ -3147,10 +3147,10 @@ cliui@^6.0.0:
strip-ansi "^6.0.0"
wrap-ansi "^6.2.0"
clj-kondo@^2020.1.13:
version "2020.1.13"
resolved "https://registry.yarnpkg.com/clj-kondo/-/clj-kondo-2020.1.13.tgz#bffe0dde83169f3b7a605f459f835cbf90ef9347"
integrity sha512-hLKi4toY3UFe1WnuX/HGr2e3PWEt4++7286Jiv/nyBoy1zcEopz7k+e0XnjX5GkxFUgXv1KrIyHQ3eM4+iW2dw==
clj-kondo@^2022.9.8:
version "2022.9.8"
resolved "https://registry.yarnpkg.com/clj-kondo/-/clj-kondo-2022.9.8.tgz#50bd2ca712d92876226d2fbe8083adbfd57e1af8"
integrity sha512-YAJivlvKxdGrnE/RhYh+ggHJKzF7wCnKQn9UVrQNrxJAE2wCirWbxjypt4S5FzlTGlzDaIDxLZSuECxI4iuXdA==
dependencies:
binwrap "^0.2.2"
request "^2.88.0"