[#14689] Link previews in chat (#14771)

* Initial

* Link fetching

* Post-merge fix
This commit is contained in:
Alexander 2023-01-18 22:43:26 +01:00 committed by GitHub
parent b370514ef3
commit 27c8c5547c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 396 additions and 356 deletions

View File

@ -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

View File

@ -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"
}
}
]

View File

@ -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

View File

@ -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"]]

View File

@ -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)))))

View File

@ -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)})

View File

@ -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]

View File

@ -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

View File

@ -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})

View File

@ -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

View File

@ -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

View File

@ -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]])

View File

@ -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

View File

@ -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))
#{})
{}))

View File

@ -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}))

View File

@ -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])))))

View File

@ -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

View File

@ -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 {})))