From 27c8c5547c7b3e830bc305bb59e79ead7d1048b9 Mon Sep 17 00:00:00 2001 From: Alexander Date: Wed, 18 Jan 2023 22:43:26 +0100 Subject: [PATCH] [#14689] Link previews in chat (#14771) * Initial * Link fetching * Post-merge fix --- ios/Podfile.lock | 4 +- nix/deps/clojure/deps.json | 137 ++------------- nix/deps/clojure/deps.list | 13 +- shadow-cljs.edn | 2 +- src/react_native/reanimated.cljs | 23 +-- src/status_im/chat/models/link_preview.cljs | 119 ------------- src/status_im/multiaccounts/login/core.cljs | 2 +- src/status_im/signals/core.cljs | 8 +- .../ui/screens/chat/message/message.cljs | 0 .../ui/screens/chat/message/styles.cljs | 64 ------- .../ui/screens/communities/community.cljs | 3 +- .../screens/link_previews_settings/views.cljs | 8 +- .../chat/messages/content/text/view.cljs | 9 +- .../contexts/chat/messages/content/view.cljs | 2 +- .../chat/messages/link_preview/events.cljs | 107 ++++++++++++ .../chat/messages/link_preview/style.cljs | 84 ++++++++++ .../chat/messages/link_preview/view.cljs | 158 ++++++++++++++++++ src/status_im2/subs/multiaccount.cljs | 4 +- src/utils/collection.cljs | 5 + 19 files changed, 396 insertions(+), 356 deletions(-) delete mode 100644 src/status_im/chat/models/link_preview.cljs delete mode 100644 src/status_im/ui/screens/chat/message/message.cljs delete mode 100644 src/status_im/ui/screens/chat/message/styles.cljs create mode 100644 src/status_im2/contexts/chat/messages/link_preview/events.cljs create mode 100644 src/status_im2/contexts/chat/messages/link_preview/style.cljs create mode 100644 src/status_im2/contexts/chat/messages/link_preview/view.cljs diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 88ec918a7f..da6235de2a 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -640,7 +640,7 @@ SPEC CHECKSUMS: FBLazyVector: 352a8ca9bbc8e2f097d680747a8c97ecef12d469 FBReactNativeSpec: 7dfb84f624136a45727c813ed21d130cd3e61beb Folly: b73c3869541e86821df3c387eb0af5f65addfab4 - glog: 997518ea2aa2d8cd5df9797b641b758d52ecf2bc + glog: 6934faae5afbec23475648c8aeb6047ce973af65 HMSegmentedControl: 34c1f54d822d8308e7b24f5d901ec674dfa31352 Keycard: ac6df4d91525c3c82635ac24d4ddd9a80aca5fc8 libwebp: 60305b2e989864154bd9be3d772730f08fc6a59c @@ -710,6 +710,6 @@ SPEC CHECKSUMS: TouchID: ba4c656d849cceabc2e4eef722dea5e55959ecf4 Yoga: 0276e9f20976c8568e107cfc1163a8629051adc0 -PODFILE CHECKSUM: a1de9468266e7f0b5273acea713782ab759690f1 +PODFILE CHECKSUM: ca5d07911eadc1267649ecc15379e5127a0cc839 COCOAPODS: 1.11.3 diff --git a/nix/deps/clojure/deps.json b/nix/deps/clojure/deps.json index 6836bc6c5b..8863bb51af 100644 --- a/nix/deps/clojure/deps.json +++ b/nix/deps/clojure/deps.json @@ -52,41 +52,15 @@ }, { - "path": "borkdude/edamame/0.0.11-alpha.28/edamame-0.0.11-alpha.28", + "path": "camel-snake-kebab/camel-snake-kebab/0.4.3/camel-snake-kebab-0.4.3", "host": "https://repo.clojars.org", "pom": { - "sha1": "488f403591739aab5a68ce6e7f82c0e855c23fd6", - "sha256": "1cw712kzza733g55s02ql35q630zd8j8nmnpivcczjkg240jans4" + "sha1": "d8a86256bfd06736b84b6ee4c154a1b2518ab461", + "sha256": "1fxz1fdhppby21l4qkrci9kp38s508yn01z3cjspda56mj1plnid" }, "jar": { - "sha1": "371f3e232e7fdb04b7b2044825eb9822280e8f93", - "sha256": "1iyxq6jypjd875cgdyllknh27mx5fnkyjz80mr927d5i94r0ywj4" - } - }, - - { - "path": "borkdude/sci.impl.reflector/0.0.1/sci.impl.reflector-0.0.1", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "eb3aff6c7db85d91f7e05b98e06d1354a4fce36c", - "sha256": "1bvr7cvpbvqi7swypzpbfrig16zipwvmg4m47y2x5chs5czwxv15" - }, - "jar": { - "sha1": "33dfc86102e0ea400498cbca47572459c1c43b00", - "sha256": "0a5gxmj8kzc01y9bn7l4x7c1v5q9wcbvw5hdr525d3ylsyl6xfkw" - } - }, - - { - "path": "borkdude/sci/0.2.1-alpha.1/sci-0.2.1-alpha.1", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "516cb8e3a8e430d59d9c3e7a51f9f859e3b249e0", - "sha256": "1nrww29m90q1avaahw7i5418c18crc3cidhbg3ml7h7vjjplsl9y" - }, - "jar": { - "sha1": "96e39dcbc3fb3a41c6bf2e628303cf0f534b28a3", - "sha256": "1cjvz85ls982rmx90igv4mcfaa53fj5z29df9mf4jplyig3l9dxb" + "sha1": "5ae08f83ceb8959971e6334596bff0214bf6fdf2", + "sha256": "1j627a99ccc4v0v83c8670vdnsp69cjk7ba0ga2xf433fwsz74c1" } }, @@ -116,19 +90,6 @@ } }, - { - "path": "clj-stacktrace/clj-stacktrace/0.2.8/clj-stacktrace-0.2.8", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "c5a47e6858344c9fd42eecafc7920a7a18d6126d", - "sha256": "1az0fs9k5xvzl8bz564apd77kqc83nl8sxsrvmswmbmkamvy4hd3" - }, - "jar": { - "sha1": "b0654b98763199ee57182526465d823492d1cc3f", - "sha256": "1029wd82qyxv6ji9pd38m32nqia56h6845lqv4ym4dsz5i201g9a" - } - }, - { "path": "cljs-bean/cljs-bean/1.3.0/cljs-bean-1.3.0", "host": "https://repo.clojars.org", @@ -675,19 +636,6 @@ } }, - { - "path": "hashp/hashp/0.2.1/hashp-0.2.1", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "32530b9106a64fd5442d2aa27c6da4175d1f1440", - "sha256": "1z94k8c52r87rq3nhc0mqazcxff6d4phi8gy1bgndhiz4nq1mia7" - }, - "jar": { - "sha1": "fb501db2eb4a028c1875382d58c17c381aec47b8", - "sha256": "1lwirzb60zvksvkb3arpwi4fk1xnp0x5ljmrca5vf7dq25lmwmsd" - } - }, - { "path": "hiccup/hiccup/1.0.5/hiccup-1.0.5", "host": "https://repo.clojars.org", @@ -818,32 +766,6 @@ } }, - { - "path": "mvxcvi/arrangement/1.2.0/arrangement-1.2.0", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "4f19bd291595870162eadd35456a0b2e76444a64", - "sha256": "1qqnl05nwcjnzhlgplcsbckrp8pqvf8n6bf8kr8rz7lz2q4px3vb" - }, - "jar": { - "sha1": "036e640bb9e14c2aa95589a45a018eb8358ce3f6", - "sha256": "1m3v1rkpdmv7aym5vq2swh0mf9mg7xppdr3vrl4g6qm3s42c24b7" - } - }, - - { - "path": "mvxcvi/puget/1.3.1/puget-1.3.1", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "07104dc20a13c7e255fffee4f16de9a6873c187f", - "sha256": "1129rb3qksg7n85j03k6d3sr6868g724097vxids1r4wqj2phxkf" - }, - "jar": { - "sha1": "a4f7dcf71ccd8d69d4ecd2b19f3d671dfc169308", - "sha256": "029znh7p8f1h91gri4jhzwqsa0rnn3i34yr3kav55npym2sw5sfp" - } - }, - { "path": "net/cgrand/macrovich/0.2.1/macrovich-0.2.1", "host": "https://repo.clojars.org", @@ -949,15 +871,15 @@ }, { - "path": "org/clojure/core.rrb-vector/0.1.2/core.rrb-vector-0.1.2", + "path": "org/clojure/core.rrb-vector/0.1.1/core.rrb-vector-0.1.1", "host": "https://repo1.maven.org/maven2", "pom": { - "sha1": "e9336ac820c5a7e07fe0aa431df981cbab6db3e3", - "sha256": "07q9qmxc7ggaxh27imgs34svn4j269rhslbnrs63ahrqzk5bmqlf" + "sha1": "3231642aa1dcf628c864a5f208cd293fbd6a385a", + "sha256": "18kk5sds5lg8r2kidhz9qpgyrvggkj8j4sgfdsmyyl93w3f16lnp" }, "jar": { - "sha1": "0404feea925608b921b56acd11d3b187a0d33fe4", - "sha256": "13hkx1285f2imqlj6wbgyxki2yg8rmfr49iq1zijxm1cgfx8xyai" + "sha1": "aafb7677ec1e9f344fc834bbbdb91e8ba02af474", + "sha256": "0cqyy1vqrhilgwrdxsibd7360ch3hhwjnbbnzsak38v6i6mg66xl" } }, @@ -1494,32 +1416,6 @@ } }, - { - "path": "rewrite-clj/rewrite-clj/0.6.1/rewrite-clj-0.6.1", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "05778b423e14e8f1b054cc78df4e5d048cc0fc68", - "sha256": "0wp9xvmfp37x88msm2yhhzi8sjwd4hxz0vv4bslhal3zi97c1bj6" - }, - "jar": { - "sha1": "55b399417a088ca163ef835e56db76ea962ce0a9", - "sha256": "0qq3an9nrhhw5mba753zs1m9rnk86w9l1f04n8v0hz0i2kbf1cki" - } - }, - - { - "path": "rewrite-cljs/rewrite-cljs/0.4.5/rewrite-cljs-0.4.5", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "378cd53218027b9a93640dcf64c040665f6c9c6b", - "sha256": "1qrhrgzhqdyqizh0ngnr4zgj9xkfzq0qhsws4pxxra3qc9ikl9cq" - }, - "jar": { - "sha1": "c35be115c39dadc71a4de3f584aa8ca295e11257", - "sha256": "1dnw0jhr1hhqz80w3z5d8qdbaqdvgzg4fyhv6lffa51c9ca1dsr1" - } - }, - { "path": "ring-cors/ring-cors/0.1.8/ring-cors-0.1.8", "host": "https://repo.clojars.org", @@ -1648,18 +1544,5 @@ "sha1": "09af0b348e6253dcf9fd567d0d22ffebdea46176", "sha256": "1qg2iyblykfkzmplc2c46916b9m0h5ad6lxmvrk5qn3pdxqr8vw0" } - }, - - { - "path": "zprint/zprint/1.1.1/zprint-1.1.1", - "host": "https://repo.clojars.org", - "pom": { - "sha1": "d7225bdc4978d3a8eac8613187cddd2efb9e39d2", - "sha256": "0yyy31h88zww238c2v4zi9va3pjp7ii76zjggglakd3wm1fdbnck" - }, - "jar": { - "sha1": "3bd9bbedb188a66ccf72c1e22819e8e423a6757a", - "sha256": "1bvrarxw0dqvxlhj6gdwrv6mklzh4p79537g293fqlm07f2knfph" - } } ] diff --git a/nix/deps/clojure/deps.list b/nix/deps/clojure/deps.list index 86c30c386a..51a9e51325 100644 --- a/nix/deps/clojure/deps.list +++ b/nix/deps/clojure/deps.list @@ -2,12 +2,9 @@ args4j/args4j/2.0.26/args4j-2.0.26.jar bidi/bidi/2.1.6/bidi-2.1.6.jar binaryage/env-config/0.2.2/env-config-0.2.2.jar binaryage/oops/0.7.0/oops-0.7.0.jar -borkdude/edamame/0.0.11-alpha.28/edamame-0.0.11-alpha.28.jar -borkdude/sci.impl.reflector/0.0.1/sci.impl.reflector-0.0.1.jar -borkdude/sci/0.2.1-alpha.1/sci-0.2.1-alpha.1.jar +camel-snake-kebab/camel-snake-kebab/0.4.3/camel-snake-kebab-0.4.3.jar cider/cider-nrepl/0.25.3/cider-nrepl-0.25.3.jar cider/piggieback/0.4.1/piggieback-0.4.1.jar -clj-stacktrace/clj-stacktrace/0.2.8/clj-stacktrace-0.2.8.jar cljs-bean/cljs-bean/1.3.0/cljs-bean-1.3.0.jar cljsjs/react-dom-server/17.0.1-0/react-dom-server-17.0.1-0.jar cljsjs/react-dom/17.0.1-0/react-dom-17.0.1-0.jar @@ -50,7 +47,6 @@ day8/re-frame/test/0.1.5/test-0.1.5.jar edn-query-language/eql/0.0.9/eql-0.0.9.jar expound/expound/0.8.5/expound-0.8.5.jar fipp/fipp/0.6.23/fipp-0.6.23.jar -hashp/hashp/0.2.1/hashp-0.2.1.jar hiccup/hiccup/1.0.5/hiccup-1.0.5.jar hickory/hickory/0.7.1/hickory-0.7.1.jar http-kit/http-kit/2.2.0/http-kit-2.2.0.jar @@ -61,8 +57,6 @@ javax/servlet/servlet-api/2.5/servlet-api-2.5.jar javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar medley/medley/0.8.2/medley-0.8.2.jar mvxcvi/alphabase/1.0.0/alphabase-1.0.0.jar -mvxcvi/arrangement/1.2.0/arrangement-1.2.0.jar -mvxcvi/puget/1.3.1/puget-1.3.1.jar net/cgrand/macrovich/0.2.1/macrovich-0.2.1.jar nrepl/nrepl/0.7.0/nrepl-0.7.0.jar org/checkerframework/checker-qual/2.0.0/checker-qual-2.0.0.jar @@ -71,7 +65,7 @@ org/clojure/clojurescript/1.10.773/clojurescript-1.10.773.jar org/clojure/core.async/1.3.610/core.async-1.3.610.jar org/clojure/core.cache/1.0.207/core.cache-1.0.207.jar org/clojure/core.memoize/1.0.236/core.memoize-1.0.236.jar -org/clojure/core.rrb-vector/0.1.2/core.rrb-vector-0.1.2.jar +org/clojure/core.rrb-vector/0.1.1/core.rrb-vector-0.1.1.jar org/clojure/core.specs.alpha/0.2.44/core.specs.alpha-0.2.44.jar org/clojure/data.json/1.0.0/data.json-1.0.0.jar org/clojure/data.priority-map/1.0.0/data.priority-map-1.0.0.jar @@ -113,8 +107,6 @@ re-frisk-remote/re-frisk-remote/1.6.0/re-frisk-remote-1.6.0.jar re-frisk/sente/1.15.0/sente-1.15.0.jar reagent/reagent/1.0.0/reagent-1.0.0.jar refactor-nrepl/refactor-nrepl/2.5.0/refactor-nrepl-2.5.0.jar -rewrite-clj/rewrite-clj/0.6.1/rewrite-clj-0.6.1.jar -rewrite-cljs/rewrite-cljs/0.4.5/rewrite-cljs-0.4.5.jar ring-cors/ring-cors/0.1.8/ring-cors-0.1.8.jar ring/ring-codec/1.1.2/ring-codec-1.1.2.jar ring/ring-core/1.8.1/ring-core-1.8.1.jar @@ -125,4 +117,3 @@ thheller/shadow-cljs/2.11.16/shadow-cljs-2.11.16-aot.jar thheller/shadow-cljsjs/0.0.21/shadow-cljsjs-0.0.21.jar thheller/shadow-util/0.7.0/shadow-util-0.7.0.jar viebel/codox-klipse-theme/0.0.1/codox-klipse-theme-0.0.1.jar -zprint/zprint/1.1.1/zprint-1.1.1.jar diff --git a/shadow-cljs.edn b/shadow-cljs.edn index b7d6d6bfd5..1e5c09963b 100644 --- a/shadow-cljs.edn +++ b/shadow-cljs.edn @@ -11,6 +11,7 @@ [cljs-bean "1.3.0"] [com.cognitect/transit-cljs "0.8.248"] [mvxcvi/alphabase "1.0.0"] + [camel-snake-kebab "0.4.3"] ;; dev dependencies [refactor-nrepl "2.5.0"] [cider/cider-nrepl "0.25.3"] @@ -19,7 +20,6 @@ ;; routing [bidi "2.1.6"] ;; test dependencies - [camel-snake-kebab "0.4.3"] [day8.re-frame/test "0.1.5"] [com.taoensso/tufte "2.1.0"]] diff --git a/src/react_native/reanimated.cljs b/src/react_native/reanimated.cljs index 0a8c9afdb1..24d3d811b5 100644 --- a/src/react_native/reanimated.cljs +++ b/src/react_native/reanimated.cljs @@ -13,8 +13,9 @@ SlideInUp SlideOutUp LinearTransition)] - [clojure.string :as string] - [reagent.core :as reagent])) + [camel-snake-kebab.core :as csk] + [reagent.core :as reagent] + [utils.collection])) ;; Animations (def slide-in-up-animation SlideInUp) @@ -62,18 +63,6 @@ (when anim (set! (.-value anim) val))) -(defn kebab-case->camelCase - [k] - (let [words (string/split (name k) #"-")] - (->> (map string/capitalize (rest words)) - (apply str (first words)) - keyword))) - -(defn map-keys - [f m] - (->> (map (fn [[k v]] [(f k) v]) m) - (into {}))) - ;; Worklets (def worklet-factory (js/require "../src/js/worklet_factory.js")) @@ -90,8 +79,10 @@ ;; so first convert kebab case styles into camel case styles (defn apply-animations-to-style [animations style] - (let [animations (map-keys kebab-case->camelCase animations) - style (apply dissoc (map-keys kebab-case->camelCase style) (keys animations))] + (let [animations (utils.collection/map-keys csk/->camelCaseString animations) + style (apply dissoc + (utils.collection/map-keys csk/->camelCaseString style) + (keys animations))] (use-animated-style (.applyAnimationsToStyle ^js worklet-factory (clj->js animations) (clj->js style))))) diff --git a/src/status_im/chat/models/link_preview.cljs b/src/status_im/chat/models/link_preview.cljs deleted file mode 100644 index 2278fc6039..0000000000 --- a/src/status_im/chat/models/link_preview.cljs +++ /dev/null @@ -1,119 +0,0 @@ -(ns status-im.chat.models.link-preview - (:require [re-frame.core :as re-frame] - [status-im.communities.core :as models.communities] - [status-im.multiaccounts.update.core :as multiaccounts.update] - [utils.re-frame :as rf] - [taoensso.timbre :as log])) - -(rf/defn enable - {:events [::enable]} - [{{:keys [multiaccount]} :db :as cofx} site enabled?] - (rf/merge cofx - (multiaccounts.update/multiaccount-update - :link-previews-enabled-sites - (if enabled? - (conj (get multiaccount :link-previews-enabled-sites #{}) site) - (disj (get multiaccount :link-previews-enabled-sites #{}) site)) - {}))) - -(rf/defn enable-all - {:events [::enable-all]} - [{{:keys [multiaccount]} :db :as cofx} link-previews-whitelist enabled?] - (rf/merge cofx - (multiaccounts.update/multiaccount-update - :link-previews-enabled-sites - (if enabled? - (into #{} (map :title link-previews-whitelist)) - #{}) - {}))) - -(defn community-resolved - [db community-id] - (update db :communities/resolve-community-info dissoc community-id)) - -(defn community-failed-to-resolve - [db community-id] - (update db :communities/resolve-community-info dissoc community-id)) - -(defn community-resolving - [db community-id] - (assoc-in db [:communities/resolve-community-info community-id] true)) - -(rf/defn handle-community-failed-to-resolve - {:events [::community-failed-to-resolve]} - [{:keys [db]} community-id] - {:db (community-failed-to-resolve db community-id)}) - -(defn community-link - [id] - (str "https://join.status.im/c/" id)) - -(rf/defn handle-community-resolved - {:events [::community-resolved]} - [{:keys [db] :as cofx} community-id community] - (rf/merge cofx - (cond-> {:db (community-resolved db community-id)} - (some? community) - (assoc :dispatch - [::cache-link-preview-data - (community-link community-id) community])) - (models.communities/handle-community community))) - -(rf/defn resolve-community-info - {:events [::resolve-community-info]} - [{:keys [db]} community-id] - {:db (community-resolving db community-id) - :json-rpc/call [{:method "wakuext_requestCommunityInfoFromMailserver" - :params [community-id] - :on-success #(re-frame/dispatch [::community-resolved community-id %]) - :on-error #(do - (re-frame/dispatch [::community-failed-to-resolve community-id]) - (log/error "Failed to request community info from mailserver"))}]}) - -(rf/defn load-link-preview-data - {:events [::load-link-preview-data]} - [cofx link] - {:json-rpc/call [{:method "wakuext_getLinkPreviewData" - :params [link] - :on-success #(re-frame/dispatch [::cache-link-preview-data link %]) - :on-error #(re-frame/dispatch - [::cache-link-preview-data - link - {:error (str "Can't get preview data for " link)}])}]}) - -(rf/defn cache-link-preview-data - {:events [::cache-link-preview-data]} - [{{:keys [multiaccount]} :db :as cofx} site data] - (multiaccounts.update/optimistic - cofx - :link-previews-cache - (assoc (get multiaccount :link-previews-cache {}) site data))) - -(defn cache-community-preview-data - [{:keys [id] :as community}] - (re-frame/dispatch [::cache-link-preview-data - (community-link id) - community])) - -(rf/defn should-suggest-link-preview - {:events [::should-suggest-link-preview]} - [{:keys [db] :as cofx} enabled?] - (multiaccounts.update/multiaccount-update - cofx - :link-preview-request-enabled - (boolean enabled?) - {})) - -(rf/defn request-link-preview-whitelist - [_] - {:json-rpc/call [{:method "wakuext_getLinkPreviewWhitelist" - :params [] - :on-success #(re-frame/dispatch [::link-preview-whitelist-received %]) - :on-error #(log/error "Failed to get link preview whitelist")}]}) - -(rf/defn save-link-preview-whitelist - {:events [::link-preview-whitelist-received]} - [{:keys [db]} whitelist] - {:db (assoc db - :link-previews-whitelist - whitelist)}) diff --git a/src/status_im/multiaccounts/login/core.cljs b/src/status_im/multiaccounts/login/core.cljs index e43c65bfa8..893e7fc67e 100644 --- a/src/status_im/multiaccounts/login/core.cljs +++ b/src/status_im/multiaccounts/login/core.cljs @@ -3,7 +3,6 @@ [clojure.string :as string] [re-frame.core :as re-frame] [status-im.async-storage.core :as async-storage] - [status-im.chat.models.link-preview :as link-preview] [status-im.communities.core :as communities] [status-im.contact.core :as contact] [status-im.data-store.chats :as data-store.chats] @@ -41,6 +40,7 @@ [status-im.wallet.prices :as prices] [status-im2.common.json-rpc.events :as json-rpc] [status-im2.contexts.activity-center.events :as activity-center] + [status-im2.contexts.chat.messages.link-preview.events :as link-preview] [status-im2.navigation.events :as navigation] [status-im2.setup.log :as logging] [taoensso.timbre :as log] diff --git a/src/status_im/signals/core.cljs b/src/status_im/signals/core.cljs index 45e94b06da..477f4e4566 100644 --- a/src/status_im/signals/core.cljs +++ b/src/status_im/signals/core.cljs @@ -1,14 +1,14 @@ (ns status-im.signals.core - (:require [status-im.chat.models.link-preview :as link.preview] - [status-im.chat.models.message :as models.message] + (:require [status-im.chat.models.message :as models.message] [status-im.ethereum.subscriptions :as ethereum.subscriptions] [utils.i18n :as i18n] [status-im.mailserver.core :as mailserver] [status-im.multiaccounts.login.core :as login] [status-im.notifications.local :as local-notifications] [status-im.transport.message.core :as transport.message] - [utils.re-frame :as rf] [status-im.visibility-status-updates.core :as visibility-status-updates] + [utils.re-frame :as rf] + [status-im2.contexts.chat.messages.link-preview.events :as link-preview] [taoensso.timbre :as log])) (rf/defn status-node-started @@ -104,7 +104,7 @@ true)) "local-notifications" (local-notifications/process cofx (js->clj event-js :keywordize-keys true)) - "community.found" (link.preview/cache-community-preview-data (js->clj event-js + "community.found" (link-preview/cache-community-preview-data (js->clj event-js :keywordize-keys true)) "status.updates.timedout" (visibility-status-updates/handle-visibility-status-updates diff --git a/src/status_im/ui/screens/chat/message/message.cljs b/src/status_im/ui/screens/chat/message/message.cljs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/status_im/ui/screens/chat/message/styles.cljs b/src/status_im/ui/screens/chat/message/styles.cljs deleted file mode 100644 index 9bf5eaaa8f..0000000000 --- a/src/status_im/ui/screens/chat/message/styles.cljs +++ /dev/null @@ -1,64 +0,0 @@ -(ns status-im.ui.screens.chat.message.styles - (:require [quo.design-system.colors :as colors] - [status-im.ui.components.react :as react] - [status-im.ui.screens.chat.styles.photos :as photos])) - -(defn container-style - [{:keys [outgoing timeline]}] - (merge {:border-top-left-radius 16 - :border-top-right-radius 16 - :border-bottom-right-radius 16 - :border-bottom-left-radius 16 - :background-color (:ui-background @colors/theme)} - (if timeline - {:border-top-left-radius 16 - :border-top-right-radius 4} - (if outgoing - {:border-top-right-radius 4} - {:border-top-left-radius 4})))) - -(def screen-width - (-> "window" - react/get-dimensions - :width)) - -(defn reactions-row - [timeline margin-top] - {:flex-direction :row - :padding-right 8 - :padding-bottom 8 - :justify-content :flex-start - :margin-top margin-top - :flex-wrap :wrap - :max-width (- screen-width (+ 30 photos/default-size (when timeline 8))) - :margin-left (+ 30 photos/default-size (when timeline 8))}) - -(def community-preview-header - {:margin 8 :margin-left 12}) - -(defn scale-dimensions - "Scale a given height and width to be maximum percentage allowed of the screen width" - [{:keys [height width] :as dimensions}] - (let [max-cover 0.5 - aspect-ratio (/ height width) - max-width (* max-cover screen-width)] - (if (< width max-width) - dimensions - {:width max-width - :height (* aspect-ratio max-width)}))) - -(defn link-preview-image - [outgoing {:keys [height width] :as dimensions}] - (merge (if (and (pos? height) - (pos? width)) - (scale-dimensions dimensions) - {:height 170}) - {:overflow :hidden - :border-top-left-radius 16 - :border-top-right-radius 16 - :border-bottom-left-radius (if outgoing 16 4) - :border-bottom-right-radius (if outgoing 4 16)})) - -(def link-preview-title - {:margin-horizontal 12 - :margin-top 10}) diff --git a/src/status_im/ui/screens/communities/community.cljs b/src/status_im/ui/screens/communities/community.cljs index c9d457d08a..bb5690b2e1 100644 --- a/src/status_im/ui/screens/communities/community.cljs +++ b/src/status_im/ui/screens/communities/community.cljs @@ -7,7 +7,6 @@ ;; designs [quo2.components.navigation.floating-shell-button :as floating-shell-button] [react-native.core :as rn] - [status-im.chat.models.link-preview :as link-preview] [status-im.communities.core :as communities] [status-im2.constants :as constants] [status-im.react-native.resources :as resources] @@ -262,7 +261,7 @@ {:style {:padding 16 :flex 1 :flex-direction :row :align-items :center :justify-content :center}} [quo/button - {:on-press (when-not fetching #(rf/dispatch [::link-preview/resolve-community-info community-id])) + {:on-press (when-not fetching #(rf/dispatch [:chat.ui/resolve-community-info community-id])) :disabled fetching :color :secondary} (if fetching diff --git a/src/status_im/ui/screens/link_previews_settings/views.cljs b/src/status_im/ui/screens/link_previews_settings/views.cljs index 68cb582c04..1838c95790 100644 --- a/src/status_im/ui/screens/link_previews_settings/views.cljs +++ b/src/status_im/ui/screens/link_previews_settings/views.cljs @@ -2,12 +2,12 @@ (:require-macros [status-im.utils.views :as views]) (:require [quo.core :as quo] [re-frame.core :as re-frame] - [status-im.chat.models.link-preview :as link-preview] [utils.i18n :as i18n] [status-im.react-native.resources :as resources] [status-im.ui.components.list.views :as list] [status-im.ui.components.react :as react] - [status-im.ui.screens.link-previews-settings.styles :as styles])) + [status-im.ui.screens.link-previews-settings.styles :as styles] + [status-im2.contexts.chat.messages.link-preview.events])) (defn prepare-urls-items-data [link-previews-enabled-sites] @@ -19,7 +19,7 @@ :accessory :switch :active (contains? link-previews-enabled-sites title) :on-press #(re-frame/dispatch - [::link-preview/enable title ((complement boolean) enabled?)])}))) + [:chat.ui/enable-link-previews title ((complement boolean) enabled?)])}))) (views/defview link-previews-settings [] @@ -39,7 +39,7 @@ (when (> (count link-previews-whitelist) 1) [quo/button - {:on-press #(re-frame/dispatch [::link-preview/enable-all + {:on-press #(re-frame/dispatch [:chat.ui/enable-all-link-previews link-previews-whitelist (not all-enabled)]) :type :secondary diff --git a/src/status_im2/contexts/chat/messages/content/text/view.cljs b/src/status_im2/contexts/chat/messages/content/text/view.cljs index 1fd60810f2..2d8cbfdd09 100644 --- a/src/status_im2/contexts/chat/messages/content/text/view.cljs +++ b/src/status_im2/contexts/chat/messages/content/text/view.cljs @@ -1,6 +1,11 @@ (ns status-im2.contexts.chat.messages.content.text.view - (:require [status-im.ui2.screens.chat.messages.message :as old-message])) + (:require + [react-native.core :as rn] + [status-im.ui2.screens.chat.messages.message :as old-message] + [status-im2.contexts.chat.messages.link-preview.view :as link-preview])) (defn text-content [message-data] - [old-message/render-parsed-text message-data]) + [rn/view + [old-message/render-parsed-text message-data] + [link-preview/link-preview message-data]]) diff --git a/src/status_im2/contexts/chat/messages/content/view.cljs b/src/status_im2/contexts/chat/messages/content/view.cljs index 5f5e15b2d3..a28604d84a 100644 --- a/src/status_im2/contexts/chat/messages/content/view.cljs +++ b/src/status_im2/contexts/chat/messages/content/view.cljs @@ -76,7 +76,7 @@ [old-message/quoted-message {:message-id response-to :chat-id chat-id} quoted-message]) [rn/view {:padding-horizontal 12 :flex-direction :row} [avatar message-data] - [rn/view {:margin-left 8} + [rn/view {:margin-left 8 :flex 1} [author message-data] (case content-type diff --git a/src/status_im2/contexts/chat/messages/link_preview/events.cljs b/src/status_im2/contexts/chat/messages/link_preview/events.cljs new file mode 100644 index 0000000000..63eb2487b1 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/link_preview/events.cljs @@ -0,0 +1,107 @@ +(ns status-im2.contexts.chat.messages.link-preview.events + (:require + [camel-snake-kebab.core :as csk] + [status-im.communities.core :as models.communities] + [status-im.multiaccounts.update.core :as multiaccounts.update] + [taoensso.timbre :as log] + [utils.collection] + [utils.re-frame :as rf])) + +(defn community-link + [id] + (str "https://join.status.im/c/" id)) + +(rf/defn cache-link-preview-data + {:events [:chat.ui/cache-link-preview-data]} + [{{:keys [multiaccount]} :db :as cofx} site data] + (let [link-previews-cache (get multiaccount :link-previews-cache {})] + (multiaccounts.update/optimistic + cofx + :link-previews-cache + (assoc link-previews-cache site (utils.collection/map-keys csk/->kebab-case-keyword data))))) + +(rf/defn load-link-preview-data + {:events [:chat.ui/load-link-preview-data]} + [{{:keys [multiaccount] :as db} :db} link] + (let [{:keys [error] :as cache-data} (get-in multiaccount [:link-previews-cache link])] + (if (or (not cache-data) error) + {:json-rpc/call [{:method "wakuext_getLinkPreviewData" + :params [link] + :on-success #(rf/dispatch [:chat.ui/cache-link-preview-data link %]) + :on-error #(rf/dispatch [:chat.ui/cache-link-preview-data link + {:error (str "Can't get preview data for " link)}])}]} + {:db db}))) + +(rf/defn should-suggest-link-preview + {:events [:chat.ui/should-suggest-link-preview]} + [{:keys [db] :as cofx} enabled?] + (multiaccounts.update/multiaccount-update + cofx + :link-preview-request-enabled + (boolean enabled?) + {})) + +(rf/defn handle-community-resolved + {:events [:chat.ui/community-resolved]} + [{:keys [db] :as cofx} community-id community] + (rf/merge cofx + (cond-> {:db (update db :communities/resolve-community-info dissoc community-id)} + + (some? community) + (assoc :dispatch + [:chat.ui/cache-link-preview-data (community-link community-id) community])) + (models.communities/handle-community community))) + +(rf/defn handle-community-failed-to-resolve + {:events [:chat.ui/community-failed-to-resolve]} + [{:keys [db]} community-id] + {:db (update db :communities/resolve-community-info dissoc community-id)}) + +(rf/defn resolve-community-info + {:events [:chat.ui/resolve-community-info]} + [{:keys [db]} community-id] + {:db (assoc-in db [:communities/resolve-community-info community-id] true) + :json-rpc/call [{:method "wakuext_requestCommunityInfoFromMailserver" + :params [community-id] + :on-success #(rf/dispatch [:chat.ui/community-resolved community-id %]) + :on-error #(do + (rf/dispatch [:chat.ui/community-failed-to-resolve community-id]) + (log/error "Failed to request community info from mailserver"))}]}) + +(rf/defn save-link-preview-whitelist + {:events [:chat.ui/link-preview-whitelist-received]} + [{:keys [db]} whitelist] + {:db (assoc db :link-previews-whitelist whitelist)}) + +(rf/defn request-link-preview-whitelist + [_] + {:json-rpc/call [{:method "wakuext_getLinkPreviewWhitelist" + :params [] + :on-success #(rf/dispatch [:chat.ui/link-preview-whitelist-received %]) + :on-error #(log/error "Failed to get link preview whitelist")}]}) + +(defn cache-community-preview-data + [{:keys [id] :as community}] + (rf/dispatch [:chat.ui/cache-link-preview-data (community-link id) community])) + +(rf/defn enable + {:events [:chat.ui/enable-link-previews]} + [{{:keys [multiaccount]} :db :as cofx} site enabled?] + (multiaccounts.update/multiaccount-update + cofx + :link-previews-enabled-sites + (if enabled? + (conj (get multiaccount :link-previews-enabled-sites #{}) site) + (disj (get multiaccount :link-previews-enabled-sites #{}) site)) + {})) + +(rf/defn enable-all + {:events [:chat.ui/enable-all-link-previews]} + [{{:keys [multiaccount]} :db :as cofx} link-previews-whitelist enabled?] + (multiaccounts.update/multiaccount-update + cofx + :link-previews-enabled-sites + (if enabled? + (into #{} (map :title link-previews-whitelist)) + #{}) + {})) \ No newline at end of file diff --git a/src/status_im2/contexts/chat/messages/link_preview/style.cljs b/src/status_im2/contexts/chat/messages/link_preview/style.cljs new file mode 100644 index 0000000000..190a4cc030 --- /dev/null +++ b/src/status_im2/contexts/chat/messages/link_preview/style.cljs @@ -0,0 +1,84 @@ +(ns status-im2.contexts.chat.messages.link-preview.style + (:require [quo2.foundations.colors :as colors] + [quo2.foundations.typography :as typography] + [status-im.ui.components.react :as react])) + +(def screen-width + (-> "window" + react/get-dimensions + :width)) + +(defn scale-dimensions + "Scale a given height and width to be maximum percentage allowed of the screen width" + [{:keys [height width] :as dimensions}] + (let [max-cover 0.5 + aspect-ratio (/ height width) + max-width (* max-cover screen-width)] + (if (< width max-width) + dimensions + {:width max-width + :height (* aspect-ratio max-width)}))) + +(defn link-preview-enable-request-wrapper + [] + {:border-left-width 2 + :border-left-color (colors/theme-colors colors/neutral-10 colors/neutral-80) + :padding-horizontal 16 + :margin-top 8}) + +(defn wrapper + [] + {:overflow :hidden + :border-left-width 2 + :border-left-color (colors/theme-colors colors/neutral-10 colors/neutral-60) + :padding-horizontal 16 + :margin-top 8}) + +(defn separator + [] + {:height 4}) + +(defn title-wrapper + [] + {:flex-direction :row + :align-items :center}) + +(defn title-site-image + [] + {:width 16 + :height 16 + :background-color (colors/theme-colors colors/neutral-20 colors/neutral-60) + :border-radius 8 + :margin-right 6}) + +(defn title-text + [] + (merge + {:color (colors/theme-colors colors/black colors/white)} + typography/font-semi-bold + typography/paragraph-1)) + +(defn main-text + [] + (merge + {:color (colors/theme-colors colors/black colors/white) + :margin-top 4 + :margin-bottom 8} + typography/paragraph-2)) + +(defn extra-text + [] + (merge + {:color (colors/theme-colors colors/neutral-50 colors/neutral-40)} + typography/font-medium + typography/paragraph-2)) + +(defn image + [{:keys [height width] :as dimensions}] + (merge (if (and (pos? height) + (pos? width)) + (scale-dimensions dimensions) + {:height 170}) + {:overflow :hidden + :border-radius 12})) + diff --git a/src/status_im2/contexts/chat/messages/link_preview/view.cljs b/src/status_im2/contexts/chat/messages/link_preview/view.cljs new file mode 100644 index 0000000000..609176c33d --- /dev/null +++ b/src/status_im2/contexts/chat/messages/link_preview/view.cljs @@ -0,0 +1,158 @@ +(ns status-im2.contexts.chat.messages.link-preview.view + (:require + [clojure.string :as string] + [utils.i18n :as i18n] + [reagent.core :as reagent] + [react-native.core :as rn] + [status-im2.constants :as constants] + [status-im2.contexts.chat.messages.link-preview.events] + [status-im2.contexts.chat.messages.link-preview.style :as style] + [quo2.core :as quo] + [utils.re-frame :as rf] + [utils.security.core :as security])) + +(defn link-belongs-to-domain + [link domain] + (string/starts-with? link (str "https://" domain))) + +(defn community-id-from-link + [link] + (nth (re-find constants/regx-community-universal-link link) 4)) + +(defn domain-info-if-whitelisted + [link whitelist] + (->> whitelist + (filter #(link-belongs-to-domain link (:address %))) + first)) + +(defn link-extended-info + [link whitelist enabled-list] + (if-not (community-id-from-link link) + (let [domain-info (domain-info-if-whitelisted link whitelist)] + {:whitelisted? (boolean domain-info) + :enabled? (contains? enabled-list (:title domain-info)) + :link link}) + {:whitelisted? true + :enabled? true + :link link + :community? true})) + +(defn previewable-link + [links whitelist enabled-list] + (->> links + (map #(link-extended-info % whitelist enabled-list)) + (filter #(:whitelisted? %)) + (first))) + +(defn is-gif? + [url] + (and url (string/ends-with? url ".gif"))) + +(defn community-preview + [{:keys [name members description verified] :as community}] + (let [members-count (count members)] + [rn/view (style/wrapper) + [rn/view (style/title-wrapper) + [rn/image {:style (style/title-site-image)}] + [rn/text {:style (style/title-text)} + name]] + [rn/text {:style (style/extra-text)} + (if verified + (i18n/label :t/verified-community) + (i18n/label :t/community))] + [rn/text {:style (style/main-text)} + description] + [rn/text {:style (style/extra-text)} + (i18n/label-pluralize members-count :t/community-members {:count members-count})] + [rn/view (style/separator)] + [quo/button + {:type :grey + :on-press #(rf/dispatch [:navigate-to :community + {:from-chat true + :community-id (:id community)}])} + (i18n/label :view)]])) + +(defn community-preview-loader + [community-link] + (let [cached-preview-data (rf/sub [:link-preview/cache community-link])] + (reagent/create-class + {:component-did-mount + (fn [] + (when-not cached-preview-data + (let [community-id (community-id-from-link community-link)] + (rf/dispatch [:chat.ui/resolve-community-info community-id])))) + :reagent-render + (fn [] + (when cached-preview-data + [community-preview cached-preview-data]))}))) + +(defn link-preview-loader + [link] + (reagent/create-class + {:component-did-mount + (fn [] + (rf/dispatch [:chat.ui/load-link-preview-data link])) + :reagent-render + (fn [] + (let [cached-preview-data (rf/sub [:link-preview/cache link])] + (when-let [{:keys [site title thumbnail-url error] :as preview-data} cached-preview-data] + (when (and (not error) site title) + [rn/touchable-opacity + {:style (when-not (is-gif? thumbnail-url) + {:align-self :stretch}) + :on-press #(when (security/safe-link? link) + (rf/dispatch [:browser.ui/message-link-pressed link]))} + [rn/view (style/wrapper) + (when-not (is-gif? thumbnail-url) + [:<> + [rn/view (style/title-wrapper) + [rn/image {:style (style/title-site-image)}] + [rn/text {:style (style/title-text)} + site]] + [rn/text {:style (style/main-text)} + title] + [rn/text {:style (style/extra-text)} + link]]) + (when-not (string/blank? thumbnail-url) + [:<> + [rn/view (style/separator)] + [rn/image + {:source {:uri thumbnail-url} + :style (style/image (select-keys preview-data [:height :width])) + :accessibility-label :member-photo}]])]]))))})) + +(defn link-preview-enable-request + [] + [rn/view (style/link-preview-enable-request-wrapper) + [rn/text {:style (style/title-text)} + (i18n/label :t/enable-link-previews)] + [rn/text {:style (style/main-text)} + (i18n/label :t/once-enabled-share-metadata)] + [rn/view (style/separator)] + [quo/button + {:type :grey + :on-press #(rf/dispatch [:open-modal :link-preview-settings])} + (i18n/label :enable)] + [rn/view (style/separator)] + [quo/button + {:type :grey + :on-press #(rf/dispatch [:chat.ui/should-suggest-link-preview false])} + (i18n/label :t/dont-ask)]]) + +(defn link-preview + [{:keys [content]}] + (let [links (:links content) + ask-user? (rf/sub [:link-preview/link-preview-request-enabled]) + enabled-sites (rf/sub [:link-preview/enabled-sites]) + whitelist (rf/sub [:link-previews-whitelist])] + (when links + (let [{:keys [link + whitelisted? + enabled? + community?]} + (previewable-link links whitelist enabled-sites) + link-whitelisted? (and link whitelisted?)] + (cond + community? [community-preview-loader link] + (and link-whitelisted? enabled?) [link-preview-loader link] + (and link-whitelisted? ask-user?) [link-preview-enable-request]))))) \ No newline at end of file diff --git a/src/status_im2/subs/multiaccount.cljs b/src/status_im2/subs/multiaccount.cljs index ad69f8c50d..85fb40ff37 100644 --- a/src/status_im2/subs/multiaccount.cljs +++ b/src/status_im2/subs/multiaccount.cljs @@ -242,8 +242,8 @@ (re-frame/reg-sub :link-preview/cache :<- [:multiaccount] - (fn [multiaccount] - (get multiaccount :link-previews-cache))) + (fn [multiaccount [_ link]] + (get-in multiaccount [:link-previews-cache link]))) (re-frame/reg-sub :link-preview/enabled-sites diff --git a/src/utils/collection.cljs b/src/utils/collection.cljs index 0edbb10bd6..e1ccbd8b74 100644 --- a/src/utils/collection.cljs +++ b/src/utils/collection.cljs @@ -20,3 +20,8 @@ [key coll] (let [groups (group-by key coll)] (map #(first (groups %)) (distinct (map key coll))))) + +(defn map-keys + [f m] + (->> (map (fn [[k v]] [(f k) v]) m) + (into {})))